Is this bad programming?

Richard A. O'Keefe ok at goanna.cs.rmit.oz.au
Fri Aug 10 17:57:48 AEST 1990


In article <1910 at tkou02.enet.dec.com>, diamond at tkou02.enet.dec.com (diamond at tkovoa) writes:
> In article <1990Aug8.100614.1223 at resam.dk> andrew at resam.dk (Leif Andrew Rump) writes:
> >char	errortext[][40] = {
> >	"234567890123456789012345678901234567890",
> >#define	FILE_FOOTER_ERROR		1
> >	"File footer error",

Instead of all this, suppose you have k separate arrays, k >= 1,
that you want to initialise "in parallel".  You want to give names
to the numbers, and a name to the total number.

The obvious method is to construct a miniature language a la Bentley.
Take a data file like this:

#File: mktable.demo
#
EntryCt;	int whence;	int whither;	char *name
Butcher;	1;		2;		"Butcher"
Baker;		1;		3;		"Baker"
Bellman;	2;		3;		"Bellman"
Boojum;		0;		0;		NULL
#End: mktable.demo

Here the first non-comment line has the form
	<total>; <type 1> <name 1>; ... <type k> <name k>
where <total> is the name that will be #defined to the number of
elements in each array, <name i> is the name of the ith array to
be initialised, and <type i> is the type of its elements.
Subsequent non-comment lines have the form

	<record name>; <value 1>; ... <value k>

where <record name> is the name that will be #defined to the 
record number, and <value i> is a C expression which is to be
one of the initial values of array <name i>.

We feed this through the following AWK script:

#File: mktable.awk
#
BEGIN		{		# note that no header nor data seen yet
		    k = 0	# number of fields required in each record
		    r = 0	# number of data records processed so far
		    p = 0	# number of data *values* saved in data[]
		}
$0 ~ /^[ \t]*#/	{		# ignore comment lines
		    next
		}
k == 0		{		# first non-comment line is header
		    if (NF < 2) {
			print "Error in line " NR ": 2 or more names expected"
			exit
		    }
		    k = NF	# number of fields to be in EACH line
		    for (i = 1; i <= NF; i++) name[i] = $i
		    next	# don't treat this as data!
		}
k != 0		{		# this must be a data record
		    if (NF != k) {
			print "Error in line " NR ": wrong number of fields"
			exit
		    }
		    print "#define", $1, r	# define the record number
		    r++				# then save the data away
		    for (i = 2; i <= NF; i++) data[p++] = $i
		}
END		{				# all records seen
		    if (k == 0) {		# never saw a header
			print "Error; no header record present"
			exit
		    }
		    if (r == 0) {		# never saw any data
			print "Error; no data records present"
			exit
		    }
		    # otherwise, data[] is r blocks of (k-1) strings
		    print "#define", name[1], r
		    for (i = 2; i <= k; i++) {
			print name[i] "[] = {"
			for (t = i-2; t < p; t += k-1) print "\t" data[t] ","
			print "};"
		    }
		}
#End: mktable.awk

In UNIX it is easy to bolt all this together

	awk -F\;              -f mktable.awk  mktable.demo | cb >tables.c
	    ^field separator  ^awk program    ^data file         ^result

(Note that using ; as the field separator allows spaces in expressions.
If you want "strings" or 'ints' containing ";", use some other character.)

The result is
#define Butcher 0
#define Baker 1
#define Bellman 2
#define Boojum 3
#define EntryCt 4
int whence[] = {
	1,
	1,
	2,
	0,
};
int whither[] = {
	2,
	3,
	3,
	0,
};
char *name[] = {
	"Butcher",
	"Baker",
	"Bellman",
	NULL,
};

In MS-DOS, it's a little harder (you might have to get the MKS awk,
for example).  On the other hand, it would be pretty easy to translate
this awk program to C.  In any case, just #include the result into your
C program, and Bob's your uncle!

-- 
Taphonomy begins at death.



More information about the Comp.lang.c mailing list