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
main(int argc,char * argv[])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
extract(char * name)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
yankstr(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
prstr(char * cp,int lineno,int posno)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
usage(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
getachar(void)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
replace(char * name)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
badformat(char * line)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