xref: /illumos-gate/usr/src/cmd/exstr/exstr.c (revision 6446bd46ed1b4e9f69da153665f82181ccaedad5)
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  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <signal.h>
36 #include <setjmp.h>
37 #include <string.h>
38 
39 /* static functions */
40 
41 static	void	extract(char *);
42 static	void	replace(char *);
43 static	void	yankstr(void);
44 static	void	badformat(char *);
45 static	void	prstr(char *, int, int);
46 static	int	getachar(void);
47 static  void	usage(void);
48 
49 /* static variables */
50 
51 static	int	eflg;		/* find strings in source file(s) */
52 static	int	dflg;		/* use replaced string a second argument */
53 static  int	rflg;		/* replace strings by function calls */
54 static  int	errflg;		/* syntax error on command line */
55 static  char	*Fname;		/* name of source file */
56 static  int	Lineno;		/* line number in source file */
57 static	int	Posno;		/* character position within line */
58 static  int	flag;		/* sets when newline is encountered */
59 static  jmp_buf	to_eof;
60 
61 
62 int
63 main(int argc, char *argv[])
64 {
65 	int	ch;
66 
67 	while ((ch = getopt(argc, argv, "erd")) != -1)
68 		switch (ch) {
69 		case 'e':
70 			if (rflg)
71 				errflg++;
72 			else
73 				eflg++;
74 			continue;
75 		case 'r':
76 			if (eflg)
77 				errflg++;
78 			else
79 				rflg++;
80 			continue;
81 		case 'd':
82 			if (eflg)
83 				errflg++;
84 			else
85 				dflg++;
86 			continue;
87 		default:
88 			errflg++;
89 		}
90 	if (optind ==  argc || errflg)
91 		usage();
92 	if (!rflg)
93 		for (; optind < argc; optind++)
94 			extract(argv[optind]);
95 	else  {
96 		if (optind+1 != argc)
97 			usage();
98 		replace(argv[optind]);
99 	}
100 	return (0);
101 }
102 
103 static void
104 extract(char *name)
105 {
106 	if (freopen(name, "r", stdin) == NULL) {
107 		(void) fprintf(
108 		    stderr, "exstr: ERROR: couldn't open file '%s'\n", name);
109 		exit(1);
110 	}
111 	Fname = name;
112 	flag = 1;
113 	Lineno = 0;
114 
115 	if (setjmp(to_eof) != 0)
116 		return;
117 
118 	for (;;) {
119 		char ch;
120 
121 		ch = getachar();
122 
123 		switch (ch) {
124 		case '#':
125 			if (Posno != 0)
126 				continue;
127 			do {
128 				ch = getachar();
129 			} while (isspace(ch));
130 			if (ch == 'd')
131 				continue;
132 			while (getachar() != '\n')
133 				;
134 			break;
135 		case '"':
136 			yankstr();
137 			break;
138 		case '\'':
139 			while ((ch = getachar()) != '\'')
140 				if (ch == '\\')
141 					ch = getachar();
142 			break;
143 
144 		case '/':
145 			ch = getachar();
146 			if (ch == '*') {
147 				int level = 0;
148 				while (level != 2) {
149 					ch = getachar();
150 					if (level == 0 && ch == '*')
151 						level++;
152 					else if (level == 1 && ch == '/')
153 						level++;
154 					else
155 						level = 0;
156 				}
157 			}
158 			break;
159 		}
160 	}
161 }
162 
163 static void
164 yankstr(void)
165 {
166 	char cc;
167 	char dbuf[BUFSIZ];
168 	register char *dp = dbuf;
169 	int saved_posno;
170 	int saved_lineno;
171 
172 	saved_posno = Posno;
173 	saved_lineno = Lineno;
174 	while ((cc = getachar()) != '"') {
175 		if (cc == '\\') {
176 			*dp++ = cc;
177 			cc = getachar();
178 		}
179 		if (cc == '\n') {
180 			dp--;
181 			continue;
182 		}
183 		*dp++ = cc;
184 	}
185 	*dp = 0;
186 	prstr(dbuf, saved_lineno, saved_posno);
187 }
188 
189 static void
190 prstr(char *cp, int lineno, int posno)
191 {
192 	if (eflg)
193 		(void) fprintf(stdout, "%s:%d:%d:::%s\n", Fname, lineno, posno,
194 		    cp);
195 	else
196 		(void) fprintf(stdout, "%s:%s\n", Fname, cp);
197 
198 }
199 
200 static void
201 usage(void)
202 {
203 	(void) fprintf(stderr, "usage: exstr [-e] files\n");
204 	(void) fprintf(stderr, "or   : exstr -r [-d] file\n");
205 	exit(1);
206 }
207 
208 static int
209 getachar(void)
210 {
211 	int cc;
212 
213 	cc = getchar();
214 	if (flag) {
215 		Lineno++;
216 		Posno = 0;
217 		flag = 0;
218 	} else
219 		Posno++;
220 	if (cc == EOF)
221 		longjmp(to_eof, 1);
222 	if (cc == '\n')
223 		flag = 1;
224 	return (cc);
225 }
226 
227 
228 static void
229 replace(char *name)
230 {
231 	char	linebuf[BUFSIZ];
232 	char	*cp;
233 	int	curlineno;		/* line number in strings file */
234 	int	curposno;		/* character position in string file */
235 	int	savelineno = 0;
236 	int	curmsgno;		/* message number in strings file */
237 	int	wrong_msg;		/* invalid message number */
238 	int	cont_str = 0;		/* string continues in the next line */
239 	char	*repstr;
240 	char	repbuf[BUFSIZ], *repbufp;
241 	char	curline[BUFSIZ];
242 	char	outbuf[BUFSIZ];
243 	/* keeps track of character position within input file */
244 	char	*inp;
245 	/* keeps track of character position within output buffer */
246 	char	*outp;
247 	char	*msgfile;
248 	FILE	*fi;			/* input source file pointer */
249 
250 	inp = linebuf;
251 	outp = outbuf;
252 	linebuf[0] = '\0';
253 	/* open input C source file */
254 	if ((fi = fopen(name, "r")) == NULL) {
255 		(void) fprintf(stderr,
256 		    "exstr: ERROR: couldn't open file '%s'\n", name);
257 		exit(1);
258 	}
259 	Fname = name;
260 
261 	(void) fprintf(stdout, "extern char *gettxt();\n");
262 
263 	/* process file containing the list of strings */
264 	while (fgets(repbuf, sizeof (repbuf), stdin) != NULL) {
265 
266 		wrong_msg = 0;
267 
268 		/* save a copy of the current line */
269 		(void) strcpy(curline, repbuf);
270 
271 		/* take apart the input string */
272 		repbufp = strchr(repbuf, ':');
273 		if (repbufp == NULL)
274 			badformat(curline);
275 		*repbufp++ = '\0';
276 		/* verify that string belongs to the input C source file */
277 		if (strcmp(repbuf, name) != 0)
278 			continue;
279 		repstr = strchr(repbufp, ':');
280 		if (repstr == NULL)
281 			badformat(curline);
282 		*repstr++ = '\0';
283 		curlineno = atoi(repbufp);
284 		if (curlineno < savelineno) {
285 			(void) fprintf(stderr,
286 			    "exstr: ERROR: stdin: line out of order\n");
287 			(void) fprintf(stderr, "%s", curline);
288 			exit(1);
289 		}
290 		savelineno = curlineno;
291 		repbufp = repstr;
292 		repstr = strchr(repbufp, ':');
293 		if (repstr == NULL)
294 			badformat(curline);
295 		repstr[strlen(repstr) - 1 ] = '\0';
296 		*repstr++ = '\0';
297 		curposno = atoi(repbufp);
298 		repbufp = repstr;
299 		repstr = strchr(repbufp, ':');
300 		if (repstr == NULL)
301 			badformat(curline);
302 		*repstr++ = '\0';
303 		msgfile = repbufp;
304 		if (strlen(msgfile) > (size_t)14 || *msgfile == '\0') {
305 			(void) fprintf(stderr,
306 			    "exstr: ERROR: stdin: invalid message file name "
307 			    "'%s'\n", msgfile);
308 			(void) fprintf(stderr, "%s", curline);
309 			exit(1);
310 		}
311 		repbufp = repstr;
312 		repstr = strchr(repbufp, ':');
313 		if (repstr == NULL)
314 			badformat(curline);
315 		*repstr++ = '\0';
316 		cp = repbufp;
317 		while (*cp)
318 			if (!isdigit(*cp++)) {
319 				wrong_msg++;
320 				break;
321 			}
322 		if (*repbufp == '\0' || wrong_msg) {
323 			(void) fprintf(stderr, "exstr: ERROR: stdin: invalid "
324 			    "message number '%s'\n", repbufp);
325 			(void) fprintf(stderr, "%s", curline);
326 			exit(1);
327 		}
328 		curmsgno = atoi(repbufp);
329 
330 		/* move up to this line */
331 		while (Lineno != curlineno) {
332 			if (outp != outbuf) {
333 				while (*inp != '\0')
334 					*outp++ = *inp++;
335 				*outp = '\0';
336 				(void) fputs(outbuf, stdout);
337 			} else if (*linebuf != '\0')
338 				(void) fputs(linebuf, stdout);
339 			outp = outbuf;
340 			inp = linebuf;
341 			if (fgets(linebuf,
342 			    sizeof (linebuf), fi) == NULL) {
343 				(void) fprintf(stderr, "read error\n");
344 				exit(1);
345 			}
346 			Lineno++;
347 			Posno = 0;
348 		}
349 		if (Posno > curposno) {
350 			(void) fprintf(stderr,
351 			    "Bad input record line number %d\n", Lineno);
352 			exit(1);
353 		}
354 		while (Posno != curposno) {
355 			*outp++ = *inp++;
356 			Posno++;
357 		}
358 		if (*inp != '"') {
359 			(void) fprintf(stderr, "exstr: ERROR: cannot replace "
360 			    "string '%s' in line (%d) of file (%s)\n", repstr,
361 			    Lineno, Fname);
362 			exit(1);
363 		}
364 		/* check if string continues in next line */
365 		while (inp[strlen(inp)-2] == '\\' &&
366 		    inp[strlen(inp)-1] == '\n') {
367 			if (fgets(linebuf,
368 			    sizeof (linebuf), fi) == NULL) {
369 				(void) fprintf(stderr, "exstr: ERROR: read "
370 				    "error in file (%s)\n", Fname);
371 				exit(1);
372 			}
373 			cont_str++;
374 			Lineno++;
375 		}
376 		if (cont_str) {
377 			cp = linebuf;
378 			while (*cp != '\0' && *cp++ != '"')
379 				;
380 			if (*cp == '\0') {
381 				(void) fprintf(stderr, "exstr: ERROR: cannot "
382 				    "replace string '%s' in line (%d) of file "
383 				    "(%s)\n", repstr, Lineno, Fname);
384 				exit(1);
385 			}
386 			inp = cp;
387 			Posno = cp - linebuf;
388 		}
389 		if (dflg)
390 			outp += snprintf(outp, BUFSIZ - (outp - outbuf),
391 			    "gettxt(\"%s:%d\", \"%s\")", msgfile, curmsgno,
392 			    repstr);
393 		else
394 			outp += snprintf(outp, BUFSIZ - (outp - outbuf),
395 			    "gettxt(\"%s:%d\", \"\")", msgfile, curmsgno);
396 		if (!cont_str) {
397 			inp += strlen(repstr)+2;
398 			Posno += strlen(repstr)+2;
399 		}
400 		else
401 			cont_str = 0;
402 	}
403 	if (outp != outbuf) {
404 		while (*inp != '\0')
405 			*outp++ = *inp++;
406 		*outp = '\0';
407 		(void) fputs(outbuf, stdout);
408 	}
409 	while (fgets(linebuf, sizeof (linebuf), fi) != NULL)
410 		(void) fputs(linebuf, stdout);
411 
412 	(void) fclose(fi);
413 }
414 
415 static	void
416 badformat(char *line)
417 {
418 	(void) fprintf(stderr, "exstr: ERROR: stdin: Badly formatted "
419 	    "replacement string\n%s", line);
420 	exit(1);
421 }
422