155714Skris/*
2160814Ssimon * Copyright (c) 1980, 1993
3160814Ssimon *	The Regents of the University of California.  All rights reserved.
4160814Ssimon *
5160814Ssimon * Redistribution and use in source and binary forms, with or without
6160814Ssimon * modification, are permitted provided that the following conditions
7160814Ssimon * are met:
8160814Ssimon * 1. Redistributions of source code must retain the above copyright
9160814Ssimon *    notice, this list of conditions and the following disclaimer.
10160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright
11160814Ssimon *    notice, this list of conditions and the following disclaimer in the
12160814Ssimon *    documentation and/or other materials provided with the distribution.
13160814Ssimon * 4. Neither the name of the University nor the names of its contributors
14160814Ssimon *    may be used to endorse or promote products derived from this software
15160814Ssimon *    without specific prior written permission.
16160814Ssimon *
17160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18160814Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19160814Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20160814Ssimon * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21160814Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22160814Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23160814Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24160814Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25160814Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26160814Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27160814Ssimon * SUCH DAMAGE.
28160814Ssimon */
29160814Ssimon
30160814Ssimon#ifndef lint
31160814Ssimonstatic const char copyright[] =
32160814Ssimon"@(#) Copyright (c) 1980, 1993\n\
33160814Ssimon	The Regents of the University of California.  All rights reserved.\n";
34160814Ssimon#endif /* not lint */
35160814Ssimon
36160814Ssimon#ifndef lint
37160814Ssimon#if 0
38160814Ssimonstatic char sccsid[] = "@(#)mkstr.c	8.1 (Berkeley) 6/6/93";
39160814Ssimon#endif
40160814Ssimon#endif /* not lint */
41160814Ssimon
42160814Ssimon#include <sys/cdefs.h>
43160814Ssimon__FBSDID("$FreeBSD: releng/10.3/usr.bin/mkstr/mkstr.c 216370 2010-12-11 08:32:16Z joel $");
44160814Ssimon
45160814Ssimon#include <err.h>
46160814Ssimon#include <errno.h>
47160814Ssimon#include <stdio.h>
48160814Ssimon#include <stdlib.h>
49160814Ssimon#include <string.h>
50160814Ssimon
51160814Ssimon#define	ungetchar(c)	ungetc(c, stdin)
52160814Ssimon
53160814Ssimon/*
54160814Ssimon * mkstr - create a string error message file by massaging C source
5555714Skris *
5655714Skris * Bill Joy UCB August 1977
5755714Skris *
5855714Skris * Modified March 1978 to hash old messages to be able to recompile
5955714Skris * without addding messages to the message file (usually)
6055714Skris *
6155714Skris * Based on an earlier program conceived by Bill Joy and Chuck Haley
6255714Skris *
6355714Skris * Program to create a string error message file
6455714Skris * from a group of C programs.  Arguments are the name
6555714Skris * of the file where the strings are to be placed, the
6655714Skris * prefix of the new files where the processed source text
6755714Skris * is to be placed, and the files to be processed.
6855714Skris *
6955714Skris * The program looks for 'error("' in the source stream.
7055714Skris * Whenever it finds this, the following characters from the '"'
7155714Skris * to a '"' are replaced by 'seekpt' where seekpt is a
7255714Skris * pointer into the error message file.
7355714Skris * If the '(' is not immediately followed by a '"' no change occurs.
7455714Skris *
7555714Skris * The optional '-' causes strings to be added at the end of the
7655714Skris * existing error message file for recompilation of single routines.
7755714Skris */
7855714Skris
7955714SkrisFILE	*mesgread, *mesgwrite;
8055714Skrischar	name[100], *np;
8155714Skris
8255714Skrisvoid copystr(void);
8355714Skrisint fgetNUL(char *, int, FILE *);
8455714Skrisunsigned hashit(char *, int, unsigned);
8555714Skrisvoid inithash(void);
8655714Skrisint match(const char *);
8755714Skrisint octdigit(char);
8855714Skrisvoid process(void);
8955714Skrisvoid usage(void);
9055714Skris
9155714Skrisint
9255714Skrismain(int argc, char *argv[])
9355714Skris{
9455714Skris	char addon = 0;
9555714Skris	size_t namelen;
9655714Skris
9755714Skris	argc--, argv++;
9855714Skris	if (argc > 1 && argv[0][0] == '-')
9955714Skris		addon++, argc--, argv++;
10055714Skris	if (argc < 3)
10155714Skris		usage();
10255714Skris	mesgwrite = fopen(argv[0], addon ? "a" : "w");
10355714Skris	if (mesgwrite == NULL)
10455714Skris		err(1, "%s", argv[0]);
10555714Skris	mesgread = fopen(argv[0], "r");
10655714Skris	if (mesgread == NULL)
10755714Skris		err(1, "%s", argv[0]);
10855714Skris	inithash();
10955714Skris	argc--, argv++;
11055714Skris	namelen = strlcpy(name, argv[0], sizeof(name));
111160814Ssimon	if (namelen >= sizeof(name)) {
112160814Ssimon		errno = ENAMETOOLONG;
113160814Ssimon		err(1, "%s", argv[0]);
114160814Ssimon	}
115160814Ssimon	np = name + namelen;
11655714Skris	argc--, argv++;
11755714Skris	do {
11868651Skris		if (strlcpy(np, argv[0], sizeof(name) - namelen) >=
11955714Skris		    sizeof(name) - namelen) {
120109998Smarkm			errno = ENAMETOOLONG;
12155714Skris			err(1, "%s%s", name, argv[0]);
12255714Skris		}
12355714Skris		if (freopen(name, "w", stdout) == NULL)
12455714Skris			err(1, "%s", name);
12555714Skris		if (freopen(argv[0], "r", stdin) == NULL)
12655714Skris			err(1, "%s", argv[0]);
12755714Skris		process();
12855714Skris		argc--, argv++;
12968651Skris	} while (argc > 0);
13055714Skris	exit(0);
13155714Skris}
13255714Skris
13355714Skrisvoid
13455714Skrisusage(void)
13555714Skris{
13655714Skris	fprintf(stderr, "usage: mkstr [-] mesgfile prefix file ...\n");
13755714Skris	exit(1);
13855714Skris}
13955714Skris
14055714Skrisvoid
14155714Skrisprocess(void)
14255714Skris{
14355714Skris	int c;
14455714Skris
14555714Skris	for (;;) {
14655714Skris		c = getchar();
14755714Skris		if (c == EOF)
14855714Skris			return;
14955714Skris		if (c != 'e') {
15055714Skris			putchar(c);
15155714Skris			continue;
15255714Skris		}
15355714Skris		if (match("error(")) {
15455714Skris			printf("error(");
15555714Skris			c = getchar();
15655714Skris			if (c != '"')
15755714Skris				putchar(c);
15855714Skris			else
15955714Skris				copystr();
16055714Skris		}
16155714Skris	}
16255714Skris}
16355714Skris
16455714Skrisint
16555714Skrismatch(const char *ocp)
16655714Skris{
16755714Skris	const char *cp;
16855714Skris	int c;
16955714Skris
17055714Skris	for (cp = ocp + 1; *cp; cp++) {
17155714Skris		c = getchar();
17255714Skris		if (c != *cp) {
17355714Skris			while (ocp < cp)
17455714Skris				putchar(*ocp++);
175109998Smarkm			ungetchar(c);
17655714Skris			return (0);
177109998Smarkm		}
17855714Skris	}
17959191Skris	return (1);
18055714Skris}
18155714Skris
18255714Skrisvoid
18355714Skriscopystr(void)
18455714Skris{
18555714Skris	int c, ch;
18655714Skris	char buf[512];
18755714Skris	char *cp = buf;
18855714Skris
18955714Skris	for (;;) {
190194206Ssimon		if (cp == buf + sizeof(buf) - 2)
191194206Ssimon			errx(1, "message too long");
192194206Ssimon		c = getchar();
193194206Ssimon		if (c == EOF)
194194206Ssimon			break;
195194206Ssimon		switch (c) {
196194206Ssimon
197194206Ssimon		case '"':
19855714Skris			*cp++ = 0;
19955714Skris			goto out;
20055714Skris		case '\\':
20155714Skris			c = getchar();
20255714Skris			switch (c) {
20355714Skris
20455714Skris			case 'b':
20555714Skris				c = '\b';
20655714Skris				break;
20755714Skris			case 't':
20855714Skris				c = '\t';
20955714Skris				break;
21055714Skris			case 'r':
21155714Skris				c = '\r';
21255714Skris				break;
21355714Skris			case 'n':
21455714Skris				c = '\n';
21555714Skris				break;
21655714Skris			case '\n':
21755714Skris				continue;
21855714Skris			case 'f':
21955714Skris				c = '\f';
22055714Skris				break;
22155714Skris			case '0':
22255714Skris				c = 0;
22368651Skris				break;
22468651Skris			case '\\':
225194206Ssimon				break;
226194206Ssimon			default:
22768651Skris				if (!octdigit(c))
22868651Skris					break;
22968651Skris				c -= '0';
23068651Skris				ch = getchar();
23155714Skris				if (!octdigit(ch))
23255714Skris					break;
23355714Skris				c <<= 7, c += ch - '0';
23455714Skris				ch = getchar();
23555714Skris				if (!octdigit(ch))
23668651Skris					break;
23755714Skris				c <<= 3, c+= ch - '0', ch = -1;
23855714Skris				break;
23955714Skris			}
24055714Skris		}
24155714Skris		*cp++ = c;
24255714Skris	}
24355714Skrisout:
24455714Skris	*cp = 0;
24555714Skris	printf("%d", hashit(buf, 1, 0));
24655714Skris}
24755714Skris
24855714Skrisint
24955714Skrisoctdigit(char c)
25055714Skris{
25155714Skris
25255714Skris	return (c >= '0' && c <= '7');
25355714Skris}
25455714Skris
25555714Skrisvoid
25655714Skrisinithash(void)
25755714Skris{
25855714Skris	char buf[512];
25955714Skris	int mesgpt = 0;
26055714Skris
26155714Skris	rewind(mesgread);
26255714Skris	while (fgetNUL(buf, sizeof buf, mesgread) != 0) {
26355714Skris		hashit(buf, 0, mesgpt);
26455714Skris		mesgpt += strlen(buf) + 2;
26555714Skris	}
26655714Skris}
26755714Skris
26855714Skris#define	NBUCKETS	511
26955714Skris
27055714Skrisstruct	hash {
271160814Ssimon	long	hval;
272160814Ssimon	unsigned hpt;
273194206Ssimon	struct	hash *hnext;
27455714Skris} *bucket[NBUCKETS];
275160814Ssimon
276160814Ssimonunsigned
277160814Ssimonhashit(char *str, int really, unsigned fakept)
278160814Ssimon{
279160814Ssimon	int i;
280160814Ssimon	struct hash *hp;
281160814Ssimon	char buf[512];
282160814Ssimon	long hashval = 0;
283160814Ssimon	char *cp;
284160814Ssimon
285160814Ssimon	if (really)
286160814Ssimon		fflush(mesgwrite);
287160814Ssimon	for (cp = str; *cp;)
288160814Ssimon		hashval = (hashval << 1) + *cp++;
289160814Ssimon	i = hashval % NBUCKETS;
290160814Ssimon	if (i < 0)
291160814Ssimon		i += NBUCKETS;
292160814Ssimon	if (really != 0)
293160814Ssimon		for (hp = bucket[i]; hp != 0; hp = hp->hnext)
294160814Ssimon		if (hp->hval == hashval) {
295160814Ssimon			fseek(mesgread, (long) hp->hpt, 0);
296160814Ssimon			fgetNUL(buf, sizeof buf, mesgread);
297160814Ssimon/*
298160814Ssimon			fprintf(stderr, "Got (from %d) %s\n", hp->hpt, buf);
299160814Ssimon*/
300160814Ssimon			if (strcmp(buf, str) == 0)
301160814Ssimon				break;
302160814Ssimon		}
303160814Ssimon	if (!really || hp == 0) {
304160814Ssimon		hp = (struct hash *) calloc(1, sizeof *hp);
305160814Ssimon		if (hp == NULL)
306160814Ssimon			err(1, NULL);
307160814Ssimon		hp->hnext = bucket[i];
308160814Ssimon		hp->hval = hashval;
309194206Ssimon		hp->hpt = really ? ftell(mesgwrite) : fakept;
310194206Ssimon		if (really) {
311194206Ssimon			fwrite(str, sizeof (char), strlen(str) + 1, mesgwrite);
312194206Ssimon			fwrite("\n", sizeof (char), 1, mesgwrite);
313194206Ssimon		}
314194206Ssimon		bucket[i] = hp;
315194206Ssimon	}
316194206Ssimon/*
317194206Ssimon	fprintf(stderr, "%s hashed to %ld at %d\n", str, hp->hval, hp->hpt);
318194206Ssimon*/
319194206Ssimon	return (hp->hpt);
320194206Ssimon}
321194206Ssimon
322194206Ssimonint
323194206SsimonfgetNUL(char *obuf, int rmdr, FILE *file)
324194206Ssimon{
325194206Ssimon	int c;
326194206Ssimon	char *buf = obuf;
327194206Ssimon
328194206Ssimon	while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF)
329194206Ssimon		*buf++ = c;
330194206Ssimon	*buf++ = 0;
331194206Ssimon	getc(file);
332194206Ssimon	return ((feof(file) || ferror(file)) ? 0 : 1);
333194206Ssimon}
334194206Ssimon