Elegance of Reason

Saturday, January 14, 2006

The Good, the Bad and the Ugly, Part 1

This is a detour from shell programming to the halcyon days of C programming, initially born of the necessity to investigate a surprising incident involving popen(), which however I am turning into a kind of mini-series. Consider the following code:


/*
popen-test: experiment with popen().

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

fflush(NULL); /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
fputs(argv[i], sink);
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
fprintf(stderr,
"%s: pclose() failed.\n",
argv[0]);
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
fprintf(stderr,
"%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
fprintf(stderr,
"%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
fprintf(stderr,
"%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
fprintf(stderr,
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]);
}
}
else {
fprintf(stderr,
"%s: popen(\"%s\") failed.\n",
argv[0], argv[1]);
}
}
else {
fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


If you excuse the poor formatting of fprintf(), the above code seems to get the job done: it allows you to experiment with popen() by launching the command specified as first argument and piping the remaining command line arguments, one by one, into its standard input. The result of pclose() is then cracked using a few macros from <sys/wait.h>.

This is, however, bad code; it appears to work, fulfills the intended purpose and, because of this, tempts the casual onlooker into believing that it can be reused as is, or as an example of using popen(). If it didn't work, or didn't fit its initial purpose, it would be discarded with little harm done.

In subsequent entries, it is my intent to pick at its defects and address them one by one; what I mean to show over the course of the series, however, is that getting code that works and fulfills its intended purpose is only the beginning of the journey. Code should also be correct, by which I do not mean "proven correct"; for those of us who are not into correcteness proofs, "at least not sloppy" would be enough. After getting past the "not sloppy" milestone, the result will be ugly code (yes, uglier than that) which will need a dose of ... elegance to obtain good code.

Whether this author will be up to the task, of course, remains to be seen.

0 Comments:

Post a Comment

<< Home