changeset 9:1fb47f5b597a

helpers: import from Magnetite
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 16 Oct 2020 07:01:13 +0000
parents 99ae5bf8cab5
children 775dba605f33
files .hgignore helpers/Makefile helpers/build-date.c helpers/cfg-hdr-gen.c helpers/makeline.c helpers/mk-flash-script.c helpers/mokosrec2bin.c helpers/srec4ram.c helpers/str2ind-ver.c
diffstat 9 files changed, 829 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,11 @@
+syntax: regexp
+
+^build-
+
+^helpers/build-date$
+^helpers/cfg-hdr-gen$
+^helpers/makeline$
+^helpers/mk-flash-script$
+^helpers/mokosrec2bin$
+^helpers/srec4ram$
+^helpers/str2ind-ver$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/Makefile	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,30 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	build-date cfg-hdr-gen makeline mk-flash-script mokosrec2bin srec4ram \
+	str2ind-ver
+
+all:	${PROGS}
+
+build-date:	build-date.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+cfg-hdr-gen:	cfg-hdr-gen.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+makeline:	makeline.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+mk-flash-script:	mk-flash-script.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+mokosrec2bin:	mokosrec2bin.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+srec4ram:	srec4ram.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+str2ind-ver:	str2ind-ver.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+clean:
+	rm -f ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/build-date.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,37 @@
+/*
+ * This program runs at firmware build time to produce a C file for the
+ * fw build that includes the build date and time stamp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+
+main(argc, argv)
+	char **argv;
+{
+	time_t now;
+	struct tm *tm;
+
+	if (argc < 2 || argc > 4) {
+		fprintf(stderr,
+			"usage: %s config_name target_name src_version\n",
+			argv[0]);
+		exit(1);
+	}
+	time(&now);
+	tm = gmtime(&now);
+	printf("const char firmware_version_str[] =\n");
+	if (argc >= 3)
+		printf("\"FreeCalypso Magnetite %s (%s), ", argv[1], argv[2]);
+	else
+		printf("\"FreeCalypso Magnetite %s, ", argv[1]);
+	if (argc >= 4 && strcmp(argv[3], "unknown"))
+		printf("source version %s, ", argv[3]);
+	printf("build date %d-%02d-%02dT%02d:%02d:%02dZ\";\n",
+		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+		tm->tm_hour, tm->tm_min, tm->tm_sec);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/cfg-hdr-gen.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,141 @@
+/*
+ * This helper program generates the set of *.cfg header files, based on a
+ * template file and environment variables for the non-constant settings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+char *infname;
+FILE *inf, *outf;
+char include_guard_symbol[32];
+char linebuf[256];
+int lineno;
+
+make_include_guard_symbol(fname)
+	char *fname;
+{
+	char *sp, *dp;
+	int c;
+
+	sp = fname;
+	dp = include_guard_symbol;
+	*dp++ = '_';
+	*dp++ = '_';
+	while (c = *sp++) {
+		if (islower(c))
+			c = toupper(c);
+		else if (c == '.')
+			c = '_';
+		*dp++ = c;
+	}
+	*dp++ = '_';
+	*dp++ = '_';
+	*dp = '\0';
+}
+
+close_output()
+{
+	if (outf) {
+		fprintf(outf, "#endif /* %s */\n", include_guard_symbol);
+		fclose(outf);
+		outf = 0;
+	}
+}
+
+bracket_line()
+{
+	char *cp;
+
+	close_output();
+	cp = index(linebuf+1, ']');
+	if (!cp) {
+		fprintf(stderr, "%s line %d: unterminated bracket line\n",
+			infname, lineno);
+		exit(1);
+	}
+	*cp = '\0';
+	outf = fopen(linebuf+1, "w");
+	if (!outf) {
+		perror(linebuf+1);
+		exit(1);
+	}
+	make_include_guard_symbol(linebuf+1);
+	fprintf(outf, "#ifndef %s\n", include_guard_symbol);
+	fprintf(outf, "#define %s\n", include_guard_symbol);
+}
+
+process_line()
+{
+	char *cp, *symbol, *value;
+
+	if (linebuf[0] == '[')
+		return bracket_line();
+	for (cp = linebuf; isspace(*cp); cp++)
+		;
+	if (*cp == '\0' || *cp == '#')
+		return;
+	for (symbol = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (!*cp) {
+inv:		fprintf(stderr, "%s line %d: expected two fields\n",
+			infname, lineno);
+		exit(1);
+	}
+	*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		goto inv;
+	for (value = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv;
+	if (!strcmp(value, "var")) {
+		value = getenv(symbol);
+		if (!value) {
+			fprintf(stderr,
+			"%s line %d: no environment variable named %s\n",
+				infname, lineno, symbol);
+			exit(1);
+		}
+	}
+	if (!outf) {
+		fprintf(stderr, "%s line %d: no open output file\n",
+			infname, lineno);
+		exit(1);
+	}
+	fprintf(outf, "#define %s %s\n", symbol, value);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s template-file output-dir\n",
+			argv[0]);
+		exit(1);
+	}
+	infname = argv[1];
+	inf = fopen(infname, "r");
+	if (!inf) {
+		perror(infname);
+		exit(1);
+	}
+	if (chdir(argv[2]) < 0) {
+		perror(argv[2]);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++)
+		process_line();
+	close_output();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/makeline.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,76 @@
+/*
+ * This helper utility for the FreeCalypso Magnetite build system
+ * emits potentially long generated Makefile lines, breaking them into
+ * multiple lines with backslashes.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+int is_cmd, equ_or_colon, extra_indent;
+int linelen;
+
+main(argc, argv)
+	char **argv;
+{
+	char **ap;
+
+	if (argc < 4) {
+		fprintf(stderr, "error: too few arguments\n", argv[0]);
+		exit(1);
+	}
+	if (!strcmp(argv[1], "def")) {
+		is_cmd = 0;
+		equ_or_colon = '=';
+		extra_indent = 0;
+	} else if (!strcmp(argv[1], "dep")) {
+		is_cmd = 0;
+		equ_or_colon = ':';
+		extra_indent = 1;
+	} else if (!strcmp(argv[1], "cmd")) {
+		is_cmd = 1;
+		extra_indent = 1;
+	} else {
+		fprintf(stderr, "error: line type \"%s\" not known\n", argv[1]);
+		exit(1);
+	}
+	if (is_cmd) {
+		putchar('\t');
+		linelen = 8;
+	} else
+		linelen = 0;
+	fputs(argv[2], stdout);
+	linelen += strlen(argv[2]);
+	if (is_cmd) {
+		putchar(' ');
+		linelen++;
+	} else {
+		putchar(equ_or_colon);
+		linelen++;
+		putchar('\t');
+		do
+			linelen++;
+		while (linelen & 7);
+	}
+	fputs(argv[3], stdout);
+	linelen += strlen(argv[3]);
+	for (ap = argv + 4; *ap; ap++) {
+		if (linelen + 1 + strlen(*ap) <= 78) {
+			putchar(' ');
+			linelen++;
+		} else {
+			fputs(" \\\n\t", stdout);
+			linelen = 8;
+			if (extra_indent) {
+				fputs("    ", stdout);
+				linelen += 4;
+			}
+		}
+		fputs(*ap, stdout);
+		linelen += strlen(*ap);
+	}
+	putchar('\n');
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/mk-flash-script.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,39 @@
+/*
+ * This helper program generates the fc-loadtool command script
+ * for flashing the just-built firmware image.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+main(argc, argv)
+	char **argv;
+{
+	struct stat st;
+	u_long image_size, sector_size;
+
+	if (argc != 4) {
+		fprintf(stderr,
+			"usage: %s fwimage.bin flash-base flash-sector-size\n",
+			argv[0]);
+		exit(1);
+	}
+	if (stat(argv[1], &st) < 0) {
+		perror(argv[1]);
+		exit(1);
+	}
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "error: %s is not a regular file\n", argv[1]);
+		exit(1);
+	}
+	image_size = st.st_size;
+	sector_size = strtoul(argv[3], 0, 16);
+	image_size += sector_size - 1;
+	image_size &= ~(sector_size - 1);
+	printf("flash erase %s 0x%lx\n", argv[2], image_size);
+	printf("flash program-bin %s %s\n", argv[2], argv[1]);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/mokosrec2bin.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,231 @@
+/*
+ * GSM device firmwares that are built with TI's TMS470 toolchain in TI's
+ * canonical way come out in TI's *.m0 format produced by TI's hex470 tool.
+ * TI's *.m0 is a variant of the classic S-record format from Motorola,
+ * but the specific variant depends on the -memwidth and -romwidth options
+ * with which the hex470 tool is run.
+ *
+ * In TI's canonical architecture (as opposed to Mot/Compal's heavily modified
+ * version) this hex470 tool is run with -memwidth 16 -romwidth 16 options,
+ * and the *.m0 file comes out in the format variant which we have nicknamed
+ * "moko-style" after its most famous user.  This variant is a byte-reversed
+ * S-record format in that each 16-bit word is byte-reversed relative to the
+ * native byte order of the ARM7 processor.  (This strange byte order actually
+ * makes some sense if one views the image as a long array of 16-bit hex
+ * values; 16 bits is the width of the flash memory on Calypso GSM devices and
+ * thus the natural unit size for flash programming.)
+ *
+ * The present mokosrec2bin utility converts these "moko-style" S-record files
+ * to straight binary, a conversion that includes flipping the order of bytes.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+char *infname;
+FILE *inf, *outf;
+u_char fillbyte;
+char srecbuf[80];
+u_char srecbin[40];
+int lineno, state;
+u_long lastaddr;
+
+u_char header[6] = {0x06, 0x00, 0x00, 'H', 'D', 'R'};
+
+decode_hex_byte(s)
+	char *s;
+{
+	register int u, l;
+
+	if (!isxdigit(s[0]) || !isxdigit(s[1]))
+		return(-1);
+	if (isdigit(s[0]))
+		u = s[0] - '0';
+	else if (isupper(s[0]))
+		u = s[0] - 'A' + 10;
+	else
+		u = s[0] - 'a' + 10;
+	if (isdigit(s[1]))
+		l = s[1] - '0';
+	else if (isupper(s[1]))
+		l = s[1] - 'A' + 10;
+	else
+		l = s[1] - 'a' + 10;
+	return((u << 4) | l);
+}
+
+srec2bin()
+{
+	register int i, l, b;
+
+	l = decode_hex_byte(srecbuf + 2);
+	if (l < 1) {
+		fprintf(stderr, "%s line %d: S-record length octet is bad\n",
+			infname, lineno);
+		exit(1);
+	}
+	srecbin[0] = l;
+	if (l > 35) {
+		fprintf(stderr,
+			"%s line %d: S-record is longer than expected\n",
+			infname, lineno);
+		exit(1);
+	}
+	for (i = 1; i <= l; i++) {
+		b = decode_hex_byte(srecbuf + i*2 + 2);
+		if (b < 0) {
+			fprintf(stderr, "%s line %d: hex decode error\n",
+				infname, lineno);
+			exit(1);
+		}
+		srecbin[i] = b;
+	}
+	return(0);
+}
+
+srec_cksum()
+{
+	u_char accum;
+	register int i, len;
+
+	len = srecbin[0] + 1;
+	accum = 0;
+	for (i = 0; i < len; i++)
+		accum += srecbin[i];
+	if (accum != 0xFF) {
+		fprintf(stderr, "%s line %d: bad checksum\n", infname, lineno);
+		exit(1);
+	}
+	return(0);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	register int i;
+	u_long curaddr;
+	int datalen;
+
+	if (argc < 3 || argc > 4) {
+usage:		fprintf(stderr, "usage: %s input.m0 output.bin [fill-byte]\n",
+			argv[0]);
+		exit(1);
+	}
+	infname = argv[1];
+	inf = fopen(infname, "r");
+	if (!inf) {
+		perror(infname);
+		exit(1);
+	}
+	if (argc > 3) {
+		i = decode_hex_byte(argv[3]);
+		if (i >= 0)
+			fillbyte = i;
+		else
+			goto usage;
+	} else
+		fillbyte = 0xFF;
+
+	state = 0;
+	for (lineno = 1; ; lineno++) {
+		if (!fgets(srecbuf, sizeof srecbuf, inf)) {
+			fprintf(stderr, "%s: premature EOF\n", infname);
+			exit(1);
+		}
+		if (srecbuf[0] != 'S') {
+			fprintf(stderr, "%s line %d: not an S-record\n",
+				infname, lineno);
+			exit(1);
+		}
+		switch (srecbuf[1]) {
+		case '0':
+			if (state == 0)
+				break;
+			else
+				goto badtype;
+		case '3':
+			if (state == 0)
+				goto badtype;
+			else
+				break;
+		case '7':
+			if (state == 2)
+				break;
+			else
+				goto badtype;
+		default:
+		badtype:
+			fprintf(stderr,
+				"%s line %d: S-record type unexpected\n",
+				infname, lineno);
+			exit(1);
+		}
+		srec2bin();
+		srec_cksum();
+		if (state == 0) {
+			if (bcmp(srecbin, header, 6)) {
+				fprintf(stderr, "%s: expected header missing\n",
+					infname);
+				exit(1);
+			}
+			state = 1;
+			continue;
+		}
+		switch (srecbuf[1]) {
+		case '3':
+			if (srecbin[0] < 6) {
+				fprintf(stderr,
+					"%s line %d: S3 record is too short\n",
+					infname, lineno);
+				exit(1);
+			}
+			curaddr = (srecbin[1] << 24) | (srecbin[2] << 16) |
+				  (srecbin[3] << 8) | srecbin[4];
+			if (curaddr & 1) {
+				fprintf(stderr, "%s line %d: odd address\n",
+					infname, lineno);
+				exit(1);
+			}
+			datalen = srecbin[0] - 5;
+			if (datalen & 1) {
+				fprintf(stderr, "%s line %d: odd data length\n",
+					infname, lineno);
+				exit(1);
+			}
+			if (state < 2) {
+				outf = fopen(argv[2], "w");
+				if (!outf) {
+					perror(argv[2]);
+					exit(1);
+				}
+				state = 2;
+				lastaddr = 0;
+			}
+			if (curaddr < lastaddr) {
+				fprintf(stderr,
+					"%s line %d: address going backwards\n",
+					infname, lineno);
+				exit(1);
+			}
+			while (lastaddr < curaddr) {
+				putc(fillbyte, outf);
+				lastaddr++;
+			}
+			for (i = 0; i < datalen; i += 2) {
+				putc(srecbin[i + 6], outf);
+				putc(srecbin[i + 5], outf);
+			}
+			lastaddr = curaddr + datalen;
+			continue;
+		case '7':
+			fclose(outf);
+			exit(0);
+		default:
+			abort();
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/srec4ram.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,222 @@
+/*
+ * This ad hoc utility is used as part of building RAM-loadable
+ * firmware images for targets with large RAM.  It reads the ramimage.m0
+ * S-record file produced by TI's hex470 post-linker and re-emits it
+ * in another SREC form that is suitable for feeding to fc-xram.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+char *infname;
+FILE *inf, *outf;
+char srecbuf[80];
+u_char srecbin[40], srecout[40];
+int lineno;
+
+decode_hex_byte(s)
+	char *s;
+{
+	register int u, l;
+
+	if (!isxdigit(s[0]) || !isxdigit(s[1]))
+		return(-1);
+	if (isdigit(s[0]))
+		u = s[0] - '0';
+	else if (isupper(s[0]))
+		u = s[0] - 'A' + 10;
+	else
+		u = s[0] - 'a' + 10;
+	if (isdigit(s[1]))
+		l = s[1] - '0';
+	else if (isupper(s[1]))
+		l = s[1] - 'A' + 10;
+	else
+		l = s[1] - 'a' + 10;
+	return((u << 4) | l);
+}
+
+srec2bin()
+{
+	register int i, l, b;
+
+	l = decode_hex_byte(srecbuf + 2);
+	if (l < 1) {
+		fprintf(stderr, "%s line %d: S-record length octet is bad\n",
+			infname, lineno);
+		exit(1);
+	}
+	srecbin[0] = l;
+	if (l > 35) {
+		fprintf(stderr,
+			"%s line %d: S-record is longer than expected\n",
+			infname, lineno);
+		exit(1);
+	}
+	for (i = 1; i <= l; i++) {
+		b = decode_hex_byte(srecbuf + i*2 + 2);
+		if (b < 0) {
+			fprintf(stderr, "%s line %d: hex decode error\n",
+				infname, lineno);
+			exit(1);
+		}
+		srecbin[i] = b;
+	}
+	return(0);
+}
+
+srec_cksum()
+{
+	u_char accum;
+	register int i, len;
+
+	len = srecbin[0] + 1;
+	accum = 0;
+	for (i = 0; i < len; i++)
+		accum += srecbin[i];
+	if (accum != 0xFF) {
+		fprintf(stderr, "%s line %d: bad checksum\n", infname, lineno);
+		exit(1);
+	}
+	return(0);
+}
+
+emit_output_srec(type, buf)
+	u_char *buf;
+{
+	int i;
+	u_char accum = 0;
+
+	putc('S', outf);
+	putc(type, outf);
+	for (i = 0; i < buf[0]; i++) {
+		fprintf(outf, "%02X", buf[i]);
+		accum += buf[i];
+	}
+	fprintf(outf, "%02X\n", ~accum & 0xFF);
+	return 0;
+}
+
+transform_s3()
+{
+	int datalen;
+	u_char *sp, *dp;
+	int i;
+
+	if (srecbin[0] < 6) {
+		fprintf(stderr,
+			"%s line %d: S3 record is too short\n",
+			infname, lineno);
+		exit(1);
+	}
+	datalen = srecbin[0] - 5;
+	if (datalen & 1) {
+		fprintf(stderr, "%s line %d: odd data length\n",
+			infname, lineno);
+		exit(1);
+	}
+	sp = srecbin;
+	dp = srecout;
+	for (i = 0; i < 5; i++)
+		*dp++ = *sp++;
+	for (i = 0; i < datalen; i += 2) {
+		dp[0] = sp[1];
+		dp[1] = sp[0];
+		sp += 2;
+		dp += 2;
+	}
+	return 0;
+}
+
+generate_vectors_record()
+{
+	u_char *dp;
+	u_long addr;
+	int i;
+
+	srecout[0] = 28 + 5;
+	srecout[1] = 0x00;
+	srecout[2] = 0x80;
+	srecout[3] = 0x00;
+	srecout[4] = 0x00;
+	dp = srecout + 5;
+	for (i = 0; i < 7; i++) {
+		addr = 0x01004000 + i * 4;
+		*dp++ = addr;
+		*dp++ = addr >> 8;
+		*dp++ = addr >> 16;
+		*dp++ = addr >> 24;
+	}
+	emit_output_srec('3', srecout);
+	return 0;
+}
+
+generate_jump_record()
+{
+	srecout[0] = 5;
+	srecout[1] = 0x01;
+	srecout[2] = 0x00;
+	srecout[3] = 0x40;
+	srecout[4] = 0x58;
+	emit_output_srec('7', srecout);
+	return 0;
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: %s input.m0 [output.srec]\n", argv[0]);
+		exit(1);
+	}
+	infname = argv[1];
+	inf = fopen(infname, "r");
+	if (!inf) {
+		perror(infname);
+		exit(1);
+	}
+	if (argc > 2) {
+		outf = fopen(argv[2], "w");
+		if (!outf) {
+			perror(argv[2]);
+			exit(1);
+		}
+	} else
+		outf = stdout;
+	for (lineno = 1; ; lineno++) {
+		if (!fgets(srecbuf, sizeof srecbuf, inf)) {
+			fprintf(stderr, "%s: premature EOF\n", infname);
+			exit(1);
+		}
+		if (srecbuf[0] != 'S') {
+			fprintf(stderr, "%s line %d: not an S-record\n",
+				infname, lineno);
+			exit(1);
+		}
+		srec2bin();
+		srec_cksum();
+		switch (srecbuf[1]) {
+		case '0':
+			emit_output_srec('0', srecbin);
+			continue;
+		case '3':
+			transform_s3();
+			emit_output_srec('3', srecout);
+			continue;
+		case '7':
+			break;
+		default:
+			fprintf(stderr, "%s line %d: unexpected S%c record\n",
+				infname, lineno, srecbuf[1]);
+			exit(1);
+		}
+		break;
+	}
+	generate_vectors_record();
+	generate_jump_record();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/helpers/str2ind-ver.c	Fri Oct 16 07:01:13 2020 +0000
@@ -0,0 +1,42 @@
+/*
+ * This utility extracts the timestamp from a str2ind.tab file
+ * and emits the corresponding char *str2ind_version C line.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+main(argc, argv)
+	char **argv;
+{
+	FILE *inf;
+	char buf[32], *cp;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s str2ind.tab\n", argv[0]);
+		exit(1);
+	}
+	inf = fopen(argv[1], "r");
+	if (!inf) {
+		perror(argv[1]);
+		exit(1);
+	}
+	if (!fgets(buf, sizeof buf, inf)) {
+inv:		fprintf(stderr,
+			"Error: %s does not have the expected first line\n",
+			argv[1]);
+		exit(1);
+	}
+	cp = index(buf, '\n');
+	if (!cp || cp == buf)
+		goto inv;
+	*cp = '\0';
+	if (cp[-1] == '\r')
+		*--cp = '\0';
+	if (cp != buf + 10)
+		goto inv;
+	printf("char *str2ind_version = \"&%s\";\n", buf);
+	exit(0);
+}