NULL as a string terminator

Richard A. O'Keefe ok at goanna.cs.rmit.oz.au
Mon Aug 20 17:39:52 AEST 1990


In article <1990Aug20.000227.12867 at icc.com>, cbp at icc.com (Chris Preston) writes:
(quoting a quote)
> >Other cases occur where someone makes #defines for error strings
> >that are used only once:

> >#define	FOO_BAR_ERROR	"foo bar error"
> >#define UNDEF_BAZ_ERR	"undef baz err"
> >
> >I cringe when I come across code like this.  Needlessly removing objects
> >a level is distracting and gains nothing.

and then defends this, where there are conditional definitions.
He suggests
>   #if MSDOS
>   #define DATACOM_NOT_INIT "Execute datacomm.exe and restart the program"
>   #elif  SYSV
>   #define DATACOM_NOT_INIT "Contact you system administrator for datacomm startup"
>   #elif BTOS
>   #define DATACOM_NOT_INIT "Master Cluster datacomm not initialized"
>   #else
>   #define DATACOM_NOT_INIT "Datacomm is not initailized"
>   #endif

as an example of a Good Thing.  I cringe when I see code like this too.
For why?  Internationalisation, _that's_ for why.

>   It also impacts on the reusability of code.  Pay me now or pay me later.

Considering the large negative impact on internationalisation of having
fixed strings in the program, may we _bill_ him, I wonder?

A very simple way of making a "resource file" in UNIX is this:

cat >resource.awk <<'end_of_file.'
BEGIN	{ pos = 0
	  max = 0
	}
	{ print "#define", $1, (pos + length($1) + 1) "L"
	  pos += length + 1	# for MS-DOS, use + 2
	  l = length - length($1) - 1
	  if (l > max) max = l
	  filename = FILENAME
	}
END	{ print "#define res__file \"" filename "\""
	  print "#define res__max  " max
	}
end_of_file.
cat >resource.c <<'end_of_file.'
#include <stdio.h>
#include "resource.h"

char *rescpy(dst, dstlen, offset)
    char *dst;
    int dstlen;
    long offset;
    {
	FILE *resfile;
	char buffer[res__max+1];
	int ok;

	resfile = fopen(res__file, "r");
	if (!resfile) return NULL;
	ok = fseek(resfile, offset, 0) == 0
	  && fgets(dst, dstlen, resfile) != NULL;
	fclose(resfile);
	return ok ? dst : NULL;
    }

void reserr(offset)
    long offset;
    {
	char buffer[512];
	if (rescpy(buffer, sizeof buffer, offset))
	    fprintf(stderr, "%s", buffer);
	else
	    fprintf(stderr, "Unknown error (%ldL)\n", offset);
    }

end_of_file.

This takes a resource file made up of lines
	<message name> <TAB> <message text>
If you do
	awk -f resource.awk resourcefile >resource.h
and then
	cc -c resource.c
you get a header file defining the <message name>s to be the
appropriate offsets in the resource file, and then
	rescpy(buffer, sizeof buffer, MessageName)
with arguments rather like fgets() will give you a copy of the
message, and
	reserr(MessageName)
will write the message to stderr.

The bottom line is that you can just make a whole bunch of files
	resource.uk
	resource.us
	resource.fr
	resource.dk
and so on, then
	awk -f resource.awk resource.$LANG >resource.h
and re'make'ing your program will let you adapt to a different language.

(I used awk here to keep this posting short.  It would be better to make
the header file with a C program so that you could use ftell() to get
exactly the right values to give to fseek().)

Of course, if you are using CMS, or VMS, or SVR4, or something with the
X/Open "nls" routines, you could use those.  Unfortunately, none of these
interfaces is portable.  A crude and limited hack like the one above may
have a (near term) place in writing code portable between these systems.

This code is free.  Free code is worth what you pay for it.
-- 
The taxonomy of Pleistocene equids is in a state of confusion.



More information about the Comp.lang.c mailing list