xref: /illumos-gate/usr/src/ucbcmd/mkstr/mkstr.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * mkstr - create a string error message file by massaging C source
32  *
33  * Bill Joy UCB August 1977
34  *
35  * Modified March 1978 to hash old messages to be able to recompile
36  * without addding messages to the message file (usually)
37  *
38  * Based on an earlier program conceived by Bill Joy and Chuck Haley
39  *
40  * Program to create a string error message file
41  * from a group of C programs.  Arguments are the name
42  * of the file where the strings are to be placed, the
43  * prefix of the new files where the processed source text
44  * is to be placed, and the files to be processed.
45  *
46  * The program looks for 'error("' in the source stream.
47  * Whenever it finds this, the following characters from the '"'
48  * to a '"' are replaced by 'seekpt' where seekpt is a
49  * pointer into the error message file.
50  * If the '(' is not immediately followed by a '"' no change occurs.
51  *
52  * The optional '-' causes strings to be added at the end of the
53  * existing error message file for recompilation of single routines.
54  */
55 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <locale.h>
60 #include <sys/param.h>
61 
62 #define	ungetchar(c)	ungetc(c, stdin)
63 
64 #define	NBUCKETS	511
65 
66 static char	usagestr[] =	"usage: %s [ - ] mesgfile prefix file ...\n";
67 
68 static FILE	*mesgread, *mesgwrite;
69 
70 static void process(void);
71 static int match(char *ocp);
72 static void copystr(void);
73 static int octdigit(char c);
74 static void inithash(void);
75 static int hashit(char *str, char really, unsigned int fakept);
76 static int fgetNUL(char *obuf, int rmdr, FILE *file);
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	char addon = 0;
82 	char *progname, *np, name[MAXPATHLEN];
83 	size_t size = 0;
84 	size_t len;
85 
86 	(void) setlocale(LC_ALL, "");
87 
88 #if !defined(TEXT_DOMAIN)
89 #define	TEXT_DOMAIN	"SYS_TEST"
90 #endif
91 	(void) textdomain(TEXT_DOMAIN);
92 
93 	argc--, progname = *argv++;
94 	if (argc > 1 && argv[0][0] == '-')
95 		addon++, argc--, argv++;
96 	if (argc < 3)
97 		(void) fprintf(stderr, gettext(usagestr), progname), exit(1);
98 	mesgwrite = fopen(argv[0], addon ? "a" : "w");
99 	if (mesgwrite == NULL)
100 		perror(argv[0]), exit(1);
101 	mesgread = fopen(argv[0], "r");
102 	if (mesgread == NULL)
103 		perror(argv[0]), exit(1);
104 	inithash();
105 	argc--, argv++;
106 
107 	if (strlcpy(name, argv[0], sizeof (name)) >= sizeof (name)) {
108 		(void) fprintf(stderr, gettext("%s: %s: string too long"),
109 			progname, argv[0]);
110 		exit(1);
111 	}
112 
113 	np = name + strlen(name);
114 
115 	len = strlen(name);
116 	np = name + len;
117 	size = sizeof (name) - len;
118 	argc--, argv++;
119 	do {
120 		if (strlcpy(np, argv[0], size) >= size) {
121 			(void) fprintf(stderr,
122 				gettext("%s: %s: string too long"),
123 				progname, argv[0]);
124 			exit(1);
125 		}
126 		if (freopen(name, "w", stdout) == NULL)
127 			perror(name), exit(1);
128 		if (freopen(argv[0], "r", stdin) == NULL)
129 			perror(argv[0]), exit(1);
130 		process();
131 		argc--, argv++;
132 	} while (argc > 0);
133 
134 	return (0);
135 }
136 
137 static void
138 process(void)
139 {
140 	int c;
141 
142 	for (;;) {
143 		c = getchar();
144 		if (c == EOF)
145 			return;
146 		if (c != 'e') {
147 			(void) putchar(c);
148 			continue;
149 		}
150 		if (match("error(")) {
151 			(void) printf(gettext("error("));
152 			c = getchar();
153 			if (c != '"')
154 				(void) putchar(c);
155 			else
156 				copystr();
157 		}
158 	}
159 }
160 
161 static int
162 match(char *ocp)
163 {
164 	char *cp;
165 	int c;
166 
167 	for (cp = ocp + 1; *cp; cp++) {
168 		c = getchar();
169 		if (c != *cp) {
170 			while (ocp < cp)
171 				(void) putchar(*ocp++);
172 			(void) ungetchar(c);
173 			return (0);
174 		}
175 	}
176 	return (1);
177 }
178 
179 static void
180 copystr(void)
181 {
182 	int c, ch;
183 	char buf[512];
184 	char *cp = buf;
185 
186 	for (;;) {
187 		c = getchar();
188 		if (c == EOF)
189 			break;
190 		switch (c) {
191 
192 		case '"':
193 			*cp++ = 0;
194 			goto out;
195 		case '\\':
196 			c = getchar();
197 			switch (c) {
198 
199 			case 'b':
200 				c = '\b';
201 				break;
202 			case 't':
203 				c = '\t';
204 				break;
205 			case 'r':
206 				c = '\r';
207 				break;
208 			case 'n':
209 				c = '\n';
210 				break;
211 			case '\n':
212 				continue;
213 			case 'f':
214 				c = '\f';
215 				break;
216 			case '0':
217 				c = 0;
218 				break;
219 			case '\\':
220 				break;
221 			default:
222 				if (!octdigit(c))
223 					break;
224 				c -= '0';
225 				ch = getchar();
226 				if (!octdigit(ch))
227 					break;
228 				c <<= 7, c += ch - '0';
229 				ch = getchar();
230 				if (!octdigit(ch))
231 					break;
232 				c <<= 3, c += ch - '0', ch = -1;
233 				break;
234 			}
235 		}
236 		*cp++ = c;
237 	}
238 out:
239 	*cp = 0;
240 	(void) printf("%d", hashit(buf, 1, NULL));
241 }
242 
243 static int
244 octdigit(char c)
245 {
246 
247 	return (c >= '0' && c <= '7');
248 }
249 
250 static void
251 inithash(void)
252 {
253 	char buf[512];
254 	int mesgpt = 0;
255 
256 	rewind(mesgread);
257 	while (fgetNUL(buf, sizeof (buf), mesgread) != NULL) {
258 		(void) hashit(buf, 0, mesgpt);
259 		mesgpt += strlen(buf) + 2;
260 	}
261 }
262 
263 static struct	hash {
264 	long	hval;
265 	unsigned int hpt;
266 	struct	hash *hnext;
267 } *bucket[NBUCKETS];
268 
269 static int
270 hashit(char *str, char really, unsigned int fakept)
271 {
272 	int i;
273 	struct hash *hp;
274 	char buf[512];
275 	long hashval = 0;
276 	char *cp;
277 
278 	if (really)
279 		(void) fflush(mesgwrite);
280 	for (cp = str; *cp; )
281 		hashval = (hashval << 1) + *cp++;
282 	i = hashval % NBUCKETS;
283 	if (i < 0)
284 		i += NBUCKETS;
285 	if (really != 0)
286 		for (hp = bucket[i]; hp != 0; hp = hp->hnext)
287 		if (hp->hval == hashval) {
288 			(void) fseek(mesgread, (long)hp->hpt, 0);
289 			(void) fgetNUL(buf, sizeof (buf), mesgread);
290 			if (strcmp(buf, str) == 0)
291 				break;
292 		}
293 	if (!really || hp == 0) {
294 		hp = (struct hash *)calloc(1, sizeof (*hp));
295 		hp->hnext = bucket[i];
296 		hp->hval = hashval;
297 		hp->hpt = really ? ftell(mesgwrite) : fakept;
298 		if (really) {
299 			(void) fwrite(str, sizeof (char), strlen(str) + 1,
300 				mesgwrite);
301 			(void) fwrite("\n", sizeof (char), 1, mesgwrite);
302 		}
303 		bucket[i] = hp;
304 	}
305 	return (hp->hpt);
306 }
307 
308 static int
309 fgetNUL(char *obuf, int rmdr, FILE *file)
310 {
311 	int c;
312 	char *buf = obuf;
313 
314 	while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF)
315 		*buf++ = c;
316 	*buf++ = 0;
317 	(void) getc(file);
318 	return ((feof(file) || ferror(file)) ? NULL : 1);
319 }
320