# Pseudoportable C script pattern

(See the next iteration.)

From time to time, while working with a command line in *nix family of operating systems, we have to write those scripts doing a rather specific task. Usually we use bash + utility programs to do the job. However, I had to ask myself: how to implement such a script in C? After an invocation of such a script, it should perform the following:

1. Create a temporary source file $S$,
2. Dump the C code to $S$,
3. Compile $S$ to the program $P$,
4. Run $P$ passing the arguments to it and caching its exit status,
5. Remove $S$ and $P$,
6. Return the cached exit status of $P$.

Code

#! /bin/bash

# Below, XXXX asks for random characters in order to make a unique file name.
# Mac OSX seems to ignore XXXX, yet Ubuntu requires them.

# Create a temporary file name for the source code:
TMP_SOURCE_FILE="$(mktemp -t sourceXXXX).c" # Create a temporary file name for the executable file: TMP_PROGRAM_FILE="$(mktemp -t programXXXX)"

# Write the source code into the temporary source file:
cat > $TMP_SOURCE_FILE <<- END_OF_SOURCE #include <stdio.h> int main(int argc, char* argv[]) { int i; puts("Hello, world! I am a pseudoportable C program."); for (i = 1; i < argc; ++i) { printf("Argument %d: %s\n", i, argv[i]); } return argc - 1; } END_OF_SOURCE # Compile and run: gcc$TMP_SOURCE_FILE -o $TMP_PROGRAM_FILE$TMP_PROGRAM_FILE $@ EXIT_STATUS=$?

# Clean the source and the binary:
rm $TMP_SOURCE_FILE rm$TMP_PROGRAM_FILE

# Delegate the exit status of the C program to calling bash:
exit $EXIT_STATUS  So, what do you think? How can I improve anything? Also, can you come with an example of the situation where this "C script" pattern is more preferable than bash + command line utilities, Python, etc.? • Have you considered using tcc instead? It can run C as a script. – 200_success Nov 27 '15 at 10:29 • Never heard about tcc before. However, the aim is to be as "portable" as possible and it seems like I can't assume that the user has tcc installed. – coderodde Nov 27 '15 at 10:32 ## 1 Answer It's not guaranteed that $TMP_PROGRAM_FILE is on the $PATH, so you need to either set the $PATH or use an absolute path for $TMP_PROGRAM_FILE. You have problems with mktemp. • -t option: It means different things on GNU/Linux mktemp(1) and Mac OS X mktemp(1). On GNU/Linux: -t interpret TEMPLATE as a single file name component, relative to a directory:$TMPDIR, if set; else the
directory specified via -p; else /tmp [deprecated]


On OS X:

mktemp [-t prefix] template …
mktemp -t prefix

-t prefix
Generate a template (using the supplied prefix and TMPDIR if set) to create a filename tem-plate. template.
plate.


Perhaps the confusion is the reason why the GNU/Linux man page indicates that it is deprecated. I think you just want to run mktemp TEMPLATE without the -t option.

• Cleanup: Use trap "rm $TMP_SOURCE_FILE$TMP_PROGRAM_FILE" EXIT as a more robust way to delete the temporary files, in case your script aborts before reaching the rm commands.
• .c suffix: By writing TMP_SOURCE_FILE="$(mktemp -t sourceXXXX).c", you end up getting mktemp to create the file sourceXXXX. That file does not get cleaned up. Also, when you do cat >$TMP_SOURCE_FILE, you may be creating sourceXXXX.c, or worse, truncating an existing file.

On GNU/Linux, a remedy would be to use the --suffix .c option. Unfortunately, the BSD/OS X version doesn't support it. One workaround is to rename the file after creation using mv -i — which might fail, but at least it's secure.

A better workaround would be to use gcc -x c to tell GCC that it is C source code, without considering the filename extension.

An even better solution would be…

• Avoid $TMP_SOURCE_FILE altogether: gcc can take its source code from its standard input! Use - as the filename. gcc -o "$TMP_PROGRAM_FILE" -x c - <<- END_OF_SOURCE
…
END_OF_SOURCE

• For some reason I can't read the exit status of the embedded C program after the trap command. Tried on Ubuntu and OSX; both return systematically 0 when the number of arguments should be returned. – coderodde Nov 28 '15 at 9:13
• Interesting. Apparently, you need to have the trap handler propagate the exit status explicitly. – 200_success Nov 28 '15 at 9:24