What? ctypes and Bash, these two together? Yes, you read it right.

Someone — namely Tavis Ormandy — is crazy enough to make a foreign function interface for Bash, behold, the ctypes.sh! It’s a Bash’s C extension with a helper Bash script that set up the environment for you to go nuts.

Don’t like Bash’s printf, want the real deal, the standard C printf(3)? You can have it:


% dlopen libc.so.6
0x7feec65e09a0
% dlcall $RTLD_DEFAULT printf $'Hello, World\n'
Hello, World

Although, I don’t see the point since Bash’s version is quite the same. Or do some mathematics?


% dlopen libm.so
0x8584b0
% dlcall -n RAND -r int32 ${DLHANDLES[libm.so]} rand
int32:719885386
% dlcall -r double ${DLHANDLES[libm.so]} sqrt ${RAND/int32/double}
double:26830.679939

When I saw this example, I was wondering how I could do basic operations with floating numbers since Bash can only do integer, it would be great if I can figure this one out.

I also thought about an actual example, and I decided to port xcinfo, a C project gathers X cursor information for use in shell scripting, to Bash, xcinfo.sh. Well just a simple script for this post, not gonna put this into that repository.


#!/bin/bash
# Written by Yu-Jie Lin @ 2015-07-28T23:48:04Z
# Public Domain

source ctypes.sh

>/dev/null dlopen libX11.so
>/dev/null dlopen libXfixes.so

2>/dev/null dlcall -n dpy -r pointer ${DLHANDLES[libX11.so]} XOpenDisplay 'pointer:(nil)'
2>/dev/null dlcall -n scr -r int ${DLHANDLES[libX11.so]} XDefaultScreen $dpy
2>/dev/null dlcall -n rtw -r int ${DLHANDLES[libX11.so]} XRootWindow $dpy $scr
2>/dev/null dlcall -n sw -r int ${DLHANDLES[libX11.so]} XDisplayWidth $dpy $scr
2>/dev/null dlcall -n sh -r int ${DLHANDLES[libX11.so]} XDisplayHeight $dpy $scr

declare -a ci
{
unset n
ci[ci_x = n++]="int16"
ci[ci_y = n++]="int16"
ci[ci_w = n++]="uint16"
ci[ci_h = n++]="uint16"
ci[ci_xh = n++]="uint16"
ci[ci_yh = n++]="uint16"
}

2>/dev/null dlcall -n cibuf -r pointer $RTLD_DEFAULT malloc 1024
2>/dev/null dlcall -n cibuf -r pointer ${DLHANDLES[libXfixes.so]} XFixesGetCursorImage $dpy
unpack $cibuf ci

printf "\
Display : %s
Screen : %8d
RootWindow: %8d
x : %8d
y : %8d
Width : %8d
Height : %8d
w : %8d
h : %8d
xh : %8d
yh : %8d

${ci[ci_x]##*:} ${ci[ci_y]##*:} ${sw##*:} ${sh##*:} \
${ci[ci_w]##*:} ${ci[ci_h]##*:} ${ci[ci_xh]##*:} ${ci[ci_yh]##*:}\n"
\
${dpy##*:} ${scr##*:} ${rtw##*:} ${ci[ci_x]##*:} ${ci[ci_y]##*:} ${sw##*:} ${sh##*:} ${ci[ci_w]##*:} ${ci[ci_h]##*:} ${ci[ci_xh]##*:} ${ci[ci_yh]##*:}

dlcall $RTLD_DEFAULT free $cibuf

An output example is:


% ./xcinfo.sh
Display : 0x6da450
Screen : 0
RootWindow: 645
x : 1206
y : 860
Width : 1680
Height : 1050
w : 9
h : 16
xh : 4
yh : 8

1206 860 1680 1050 9 16 4 8

ctypes.sh is written in C and Bash, under the MIT License. Be sure to read what people said about ctypes.sh in README. This is probably my favorite: “you’ve gone too far with this.” Too far? Nah, it’s because he can.