ensure terminal

I have stayed up far too late tonight working on a C port of a shell-script I'd written to ensure a given program is run in a terminal. In the process I've learned more than I feel like I have in the smattering of C tutorials I've read, though of course I'm assisted by knowing how C basically looks and how to find information on it (and of course, having a friend in #programming on irc).

the problem to solve

As you may be able to guess, ensure-terminal ensures a given program is run in a terminal without having to worry about where it's called from. This means that I can write wrapper scripts to call ensure-terminal <program>, and i can call that wrapper from dmenu_run or a .desktop file or the command line and it'll just do what i want it to do. Mildly useful!

porting to c

I had been using a bash script, which works fine:

#!/bin/sh

if tty &gt;/dev/null 2&gt;&amp;1
then "$@"
else xfce4-terminal -x "$@"
fi

but i've been toying with the idea of learning C so tonight, I was like, let's port a trivial shell script program. This was the one I picked.

As you can see, this is a fairly straightforward port. The hardest part was manually dealing with the argv -- in sh you just pass "$@" and it's covered for you. I finally realized I just needed more array iterator variables and it worked out from there.

The other interesting thing here is that the terminal command is "customizable," if you're willing to compile from source (which I mean, technically everything about any C program is customizable if you're willing to compile from source). Still, it's enough for my needs I think.

OH! I also learned about ending arrays with NULL, which means that lots of C i've seen in the wild suddenly makes a lot more sense. AND I learned about strace :)

ensure-terminal.c

#include &lt;unistd.h&gt; /* isatty(3) */

/* Change this to be whatever you want it to be */
static char *termcmd[] = { "xfce4-terminal", "-x", NULL };

int arraylen(char *arr[]) {
	int i;
	for (i = 0; arr[i]; i++);
	return i;
}

int main(int argc, char **argv) {
	int i = 1, n = 0; /* argv[i], new_argv[n] */
	int new_argc = argc + arraylen(termcmd);
	char *new_argv[new_argc+1];

	if (!isatty(0)) {
		for (int t = 0; t &lt; arraylen(termcmd); t++, n++) {
			new_argv[n] = termcmd[t];
		}
	}

	for (i; i &lt; argc; i++, n++) {
		new_argv[n] = argv[i];
	}

	new_argv[n] = NULL;
	execvp(new_argv[0], new_argv);
}

In case you're wondering, the code is all licensed under a BSD 3-Clause.

conclusion

I had a great time playing with this tiny project. Next time, I think I'll try porting my upload script I just wrote today (the latest in a long iterative line of upload scripts, but that's another story for another post).