xref: /illumos-gate/usr/src/cmd/genmsg/genmsg.l (revision 4ba5c7f83ffc37214b22853dd1ef6752d0178a4e)
1 %{
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance 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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <limits.h>
30 #include <string.h>
31 #include <libintl.h>
32 #include <locale.h>
33 #include "genmsg.h"
34 #include "y.tab.h"
35 
36 extern int is_cat_found;	/* from main.c */
37 extern void add_comment(Mode, char *);	/* from util.c */
38 
39 int lineno = 1;
40 
41 /*
42  * msg_line stores the line number where a msgid is to be replaced.
43  */
44 int msg_line = 0;
45 
46 int end_of_cat = TRUE;
47 
48 /*
49  * In preprocessor mode, genmsg has to parse both the original
50  * soruce code and the code which a preprocessor generates.
51  * While genmsg is parsing the original source code,  'pound_is_mine'
52  * is set to TRUE.
53  */
54 int pound_is_mine = FALSE;
55 
56 void warning(char *);
57 
58 #define	NOLINEMSG	-2
59 
60 void set_linemsgid(int, int);
61 int get_linemsgid(int);
62 
63 /*
64  * cat_field indicates which token is currently parsed by lex.
65  */
66 #define	CatdField	0
67 #define	SetidField	1
68 #define	MsgidField	2
69 #define	StrField	3
70 
71 static int cat_field;
72 
73 /*
74  * This will be turned on when '-' is found in the catgets message
75  * number field.
76  */
77 static int save_minus = FALSE;
78 
79 static char *skip_quoted(int skip_ch);
80 static char *skip_comment(void);
81 static void parse_cppline(char *);
82 %}
83 %s CAT
84 %%
85 
86 [0-9a-zA-Z\_\.]catgets	{
87 			if (IsActiveMode(ReplaceMode)) {
88 				(void) fprintf(newfp, "%s", yytext);
89 			}
90 		}
91 
92 catgets[0-9a-zA-Z\_\.]	{
93 			if (IsActiveMode(ReplaceMode)) {
94 				(void) fprintf(newfp, "%s", yytext);
95 			}
96 		}
97 
98 catgets		{
99 			if (end_of_cat) {
100 				/*
101 				 * If the previous catgets
102 				 * state is on, turn it off
103 				 * first.
104 				 */
105 				BEGIN 0;
106 			}
107 			if (IsActiveMode(ReplaceMode)) {
108 				(void) fprintf(newfp, "%s", yytext);
109 			}
110 			if (!IsActiveMode(ReplaceMode) ||
111 			    !IsActiveMode(PreProcessMode)) {
112 				BEGIN CAT;
113 				end_of_cat = FALSE;
114 				cat_field = CatdField;
115 				return (CATGETS);
116 			}
117 		}
118 
119 <CAT>\,		{	/* punctuation */
120 			cat_field++;
121 			if (IsActiveMode(ReplaceMode)) {
122 				(void) fprintf(newfp, "%c", yytext[0]);
123 			}
124 			if (end_of_cat) {
125 				BEGIN 0;
126 			} else {
127 				return (yytext[0]);
128 			}
129 		}
130 
131 <CAT>[+*/();>]	{	/* punctuation */
132 			if (IsActiveMode(ReplaceMode)) {
133 				(void) fprintf(newfp, "%c", yytext[0]);
134 			}
135 			if (end_of_cat) {
136 				BEGIN 0;
137 			} else {
138 				return (yytext[0]);
139 			}
140 		}
141 
142 <CAT>const	{
143 			if (IsActiveMode(ReplaceMode)) {
144 				(void) fprintf(newfp, "%s", yytext);
145 			}
146 			if (end_of_cat) {
147 				BEGIN 0;
148 			} else {
149 				return (CONST);
150 			}
151 		}
152 
153 <CAT>nl_catd	{
154 			if (IsActiveMode(ReplaceMode)) {
155 				(void) fprintf(newfp, "%s", yytext);
156 			}
157 			if (end_of_cat) {
158 				BEGIN 0;
159 			} else {
160 				return (CATD);
161 			}
162 		}
163 
164 <CAT>char	{
165 			if (IsActiveMode(ReplaceMode)) {
166 				(void) fprintf(newfp, "%s", yytext);
167 			}
168 			if (end_of_cat) {
169 				BEGIN 0;
170 			} else {
171 				return (CHAR);
172 			}
173 		}
174 
175 <CAT>int	{
176 			if (IsActiveMode(ReplaceMode)) {
177 				(void) fprintf(newfp, "%s", yytext);
178 			}
179 			if (end_of_cat) {
180 				BEGIN 0;
181 			} else {
182 				return (INT);
183 			}
184 		}
185 
186 <CAT>\+\+	{
187 			if (IsActiveMode(ReplaceMode)) {
188 				(void) fprintf(newfp, "%s", yytext);
189 			}
190 			if (end_of_cat) {
191 				BEGIN 0;
192 			} else {
193 				return (INC);
194 			}
195 		}
196 
197 <CAT>\-\-	{
198 			if (IsActiveMode(ReplaceMode)) {
199 				(void) fprintf(newfp, "%s", yytext);
200 			}
201 			if (end_of_cat) {
202 				BEGIN 0;
203 			} else {
204 				return (INC);
205 			}
206 		}
207 
208 <CAT>\"		{	/* extract quoted string */
209 			yylval.str = skip_quoted('"');
210 			if (IsActiveMode(ReplaceMode)) {
211 				(void) fprintf(newfp, "\"%s\"", yylval.str);
212 			}
213 			if (end_of_cat) { /* just in case */
214 				BEGIN 0;
215 				free(yylval.str);
216 			} else {
217 				return (QSTR);
218 			}
219 		}
220 
221 <CAT>-		{	/* punctuation */
222 			if (IsActiveMode(ReplaceMode)) {
223 				if (cat_field == MsgidField &&
224 					get_linemsgid(lineno) != NOLINEMSG) {
225 					save_minus = TRUE; /*  be replaced. */
226 				} else {
227 					(void) fprintf(newfp, "%c", yytext[0]);
228 				}
229 			}
230 			if (end_of_cat) { /* just in case */
231 				BEGIN 0;
232 			} else {
233 				return (yytext[0]);
234 			}
235 		}
236 
237 <CAT>[0-9]+	{	/* numbers */
238 			switch (cat_field) {
239 			case SetidField:
240 				yylval.id = atoi(yytext);
241 				if (IsActiveMode(ReplaceMode)) {
242 					(void) fprintf(newfp, "%s", yytext);
243 				}
244 				if (end_of_cat) {
245 					BEGIN 0;
246 				} else {
247 					return (SETID);
248 				}
249 				break;
250 			case MsgidField:
251 				yylval.id = atoi(yytext);
252 				if (IsActiveMode(ReplaceMode)) {
253 					int id = get_linemsgid(lineno);
254 					if (id == NOLINEMSG) {
255 						(void) fprintf(newfp, "%s",
256 						    yytext);
257 					} else if (id == NOMSGID &&
258 						IsActiveMode(ReverseMode)) {
259 						(void) fprintf(newfp, "%d",
260 						    NOMSGID);
261 					} else if (save_minus == TRUE &&
262 						yylval.id == 1) {
263 						(void) fprintf(newfp, "%d", id);
264 					} else { /* just in case */
265 						(void) fprintf(newfp, "%s",
266 						    yytext);
267 					}
268 					save_minus = FALSE;
269 				} else {
270 					msg_line = lineno;
271 				}
272 				if (end_of_cat) {
273 					BEGIN 0;
274 				} else {
275 					return (MSGID);
276 				}
277 				break;
278 			default:
279 				yylval.id = atoi(yytext);
280 				if (IsActiveMode(ReplaceMode)) {
281 					(void) fprintf(newfp, "%s", yytext);
282 				}
283 				if (end_of_cat) {
284 					BEGIN 0;
285 				} else {
286 					return (DIGIT);
287 				}
288 			}
289 		}
290 
291 <CAT>[a-zA-Z0-9_\&][a-zA-Z0-9_\>\&\.]*	{
292 			if (IsActiveMode(ReplaceMode)) {
293 				(void) fprintf(newfp, "%s", yytext);
294 			}
295 			if (end_of_cat) {
296 				BEGIN 0;
297 			} else {
298 				return (STR);
299 			}
300 		}
301 
302 <CAT>\n		{
303 			lineno++;
304 			if (IsActiveMode(ReplaceMode)) {
305 				(void) fprintf(newfp, "\n");
306 			}
307 			if (end_of_cat) {
308 				BEGIN 0;
309 			}
310 		}
311 
312 <CAT>.		{	/* not interested */
313 			if (IsActiveMode(ReplaceMode)) {
314 				(void) fprintf(newfp, "%c", yytext[0]);
315 			}
316 			if (end_of_cat) {
317 				BEGIN 0;
318 			}
319 		}
320 
321 -((([ \t]+)1)|1) {	/* -1 */
322 			if (end_of_cat == FALSE) {
323 				REJECT
324 			} else if (IsActiveMode(ReplaceMode)) {
325 				if (IsActiveMode(PreProcessMode)) {
326 					int id = get_linemsgid(lineno);
327 					if (id == NOLINEMSG) {
328 						(void) fprintf(newfp, "%s",
329 						    yytext);
330 					} else { /* could be -1. */
331 						(void) fprintf(newfp, "%d", id);
332 					}
333 				} else {
334 					(void) fprintf(newfp, "%s", yytext);
335 				}
336 			}
337 		}
338 
339 [0-9]+		{
340 			if (IsActiveMode(ReplaceMode)) {
341 				if (IsActiveMode(PreProcessMode) &&
342 					IsActiveMode(ReverseMode)) {
343 					int id = get_linemsgid(lineno);
344 					if (id == NOLINEMSG) {
345 						(void) fprintf(newfp, "%s",
346 						    yytext);
347 					} else if (id == NOMSGID) {
348 						(void) fprintf(newfp, "%d", id);
349 					}
350 				} else {
351 					(void) fprintf(newfp, "%s", yytext);
352 				}
353 			}
354 		}
355 
356 ^#[ \t]*[0-9]+.*\n	{	/* pound for c-preprocessor */
357 			if (IsActiveMode(PreProcessMode)) {
358 				if (IsActiveMode(ReplaceMode)) {
359 					(void) fprintf(newfp, "%s", yytext);
360 				} else {
361 					parse_cppline(yytext);
362 				}
363 			} else if (IsActiveMode(ReplaceMode)) {
364 				(void) fprintf(newfp, "%s", yytext);
365 			}
366 			lineno++;
367 		}
368 
369 "/*"		{	/* skip a comment block */
370 			char *comment = skip_comment();
371 			if (IsActiveMode(ReplaceMode)) {
372 				(void) fprintf(newfp, "%s", comment);
373 			} else {
374 				if (IsActiveMode(MsgCommentMode)) {
375 					add_comment(MsgCommentMode, comment);
376 				}
377 				if (IsActiveMode(SetCommentMode)) {
378 					add_comment(SetCommentMode, comment);
379 				}
380 			}
381 			free(comment);
382 		}
383 
384 "//".*\n	{	/* skip a c++ comment */
385 			if (IsActiveMode(ReplaceMode)) {
386 				(void) fprintf(newfp, "%s", yytext);
387 			} else {
388 				if (IsActiveMode(MsgCommentMode)) {
389 					add_comment(MsgCommentMode, yytext);
390 				}
391 				if (IsActiveMode(SetCommentMode)) {
392 					add_comment(SetCommentMode, yytext);
393 				}
394 			}
395 			lineno++;
396 		}
397 
398 \"		{	/* skip quoted string */
399 			char *qstr = skip_quoted('"');
400 			if (IsActiveMode(ReplaceMode)) {
401 				(void) fprintf(newfp, "\"%s\"", qstr);
402 			}
403 			free(qstr);
404 		}
405 
406 \'		{	/* skip single-quoted character */
407 			char *qchr = skip_quoted('\'');
408 			if (IsActiveMode(ReplaceMode)) {
409 				(void) fprintf(newfp, "\'%s\'", qchr);
410 			}
411 			free(qchr);
412 		}
413 
414 \n		{
415 			if (IsActiveMode(ReplaceMode)) {
416 				(void) fprintf(newfp, "\n");
417 			}
418 			lineno++;
419 		}
420 
421 .		{
422 			if (IsActiveMode(ReplaceMode)) {
423 				(void) fprintf(newfp, "%c", yytext[0]);
424 			}
425 		}
426 
427 %%
428 
429 static char *
430 skip_quoted(int skip_ch)
431 {
432 	char *buf, *ptr;	/* saved buffer and its pointer */
433 	int bsize = BUFSIZ;	/* growing buffer size */
434 	int i = 0;		/* counter */
435 	int c, old = 0;		/* input character */
436 
437 	if ((buf = ptr = malloc(bsize)) == NULL) {
438 		prg_err(gettext("fatal: out of memory"));
439 		exit(EXIT_FAILURE);
440 	}
441 	for (; ; i++) {
442 		if (i == bsize) {
443 			bsize += BUFSIZ;
444 			if ((buf = realloc(buf, bsize)) == NULL) {
445 				prg_err(gettext("fatal: out of memory"));
446 				exit(EXIT_FAILURE);
447 			}
448 			ptr = buf + i;
449 		}
450 		c = input();
451 		if (c == skip_ch && old != '\\') {
452 			break;
453 		} else if (c == '\n') {
454 			lineno++;
455 		} else if (c == 0) {
456 			if (skip_ch == '"') {
457 				warning(gettext("warning: unmatched \""));
458 			} else if (skip_ch == '\'') {
459 				warning(gettext("warning: unmatched '"));
460 			} else {
461 				/* Should not happen */
462 				warning(gettext(
463 				    "warning: unmatched character"));
464 			}
465 			break;
466 		}
467 		*ptr++ = c;
468 		if (old == '\\') {
469 			old = '\0';
470 		} else {
471 			old = c;
472 		}
473 	}
474 	*ptr = '\0';
475 	return (buf);
476 }
477 
478 static char *
479 skip_comment(void)
480 {
481 	char *buf, *ptr;	/* saved buffer and its pointer */
482 	int bsize = BUFSIZ;	/* growing buffer size */
483 	int i = 0;		/* counter */
484 	int c, old = 0;		/* input character */
485 
486 	if ((buf = ptr = malloc(bsize)) == NULL) {
487 		prg_err(gettext("fatal: out of memory"));
488 		exit(EXIT_FAILURE);
489 	}
490 	*ptr++ = '/';	i++;
491 	*ptr++ = '*';	i++;
492 	for (; ; i++) {
493 		if (i == bsize) {
494 			bsize += BUFSIZ;
495 			if ((buf = realloc(buf, bsize)) == NULL) {
496 				prg_err(gettext("fatal: out of memory"));
497 				exit(EXIT_FAILURE);
498 			}
499 			ptr = buf + i;
500 		}
501 		c = input();
502 		if (c == '/' && old == '*') {
503 			*ptr++ = c;
504 			break;
505 		} else if (c == '\n') {
506 			lineno++;
507 		} else if (c == 0) {
508 			warning(gettext("warning: unmatched /*"));
509 			break;
510 		}
511 		*ptr++ = old = c;
512 	}
513 	*ptr = '\0';
514 	return (buf);
515 }
516 
517 /*
518  * parse_cppline() parses the line control information that a C
519  * preprocessor generates to indicate the location in the original
520  * file.  See the cpp man in the details.
521  */
522 static void
523 parse_cppline(char *str)
524 {
525 	int n, line, len;
526 	char ch;
527 	char file[BUFSIZ];
528 	char *altfile = NULL;
529 	char *pfile;
530 
531 	len = strlen(str);
532 	if (len >= sizeof (file)) {
533 		if ((altfile = malloc(len + 1)) == NULL) {
534 			prg_err(gettext("fatal: out of memory"));
535 			exit(EXIT_FAILURE);
536 		}
537 		pfile = altfile;
538 	} else {
539 		pfile = file;
540 	}
541 	/* LINTED: E_SEC_SCANF_UNBOUNDED_COPY */
542 	n = sscanf(str, "%c%d%s", &ch, &line, pfile);
543 
544 	/* 'file' is a quoted string but 'srcfile' is not. */
545 	len = strlen(pfile) - 2;
546 
547 	pfile++;
548 	if (n == 3 && (strncmp(pfile, srcfile, len) == 0)) {
549 		pound_is_mine = TRUE;
550 		lineno = line - 1;
551 	} else if (n == 2 && (pound_is_mine == TRUE)) {
552 		lineno = line - 1;
553 	} else {
554 		pound_is_mine = FALSE;
555 	}
556 	if (altfile)
557 		free(altfile);
558 }
559 
560 typedef struct {
561 	int line;
562 	int msgid;
563 } LineMsgID;
564 
565 static LineMsgID line_msgid[NL_MSGMAX];
566 static int line_msgcnt;
567 
568 void
569 init_lex(void)
570 {
571 	lineno = 1;
572 	end_of_cat = TRUE;
573 	pound_is_mine = FALSE;
574 }
575 
576 void
577 init_linemsgid(void)
578 {
579 	line_msgcnt = 0;
580 	(void) memset(line_msgid, 0, sizeof (LineMsgID) * NL_MSGMAX);
581 }
582 
583 void
584 set_linemsgid(int line, int msgid)
585 {
586 	if (line_msgcnt >= NL_MSGMAX) {
587 		return; /* oops */
588 	}
589 	line_msgid[line_msgcnt].line = line;
590 	line_msgid[line_msgcnt].msgid = msgid;
591 	line_msgcnt++;
592 }
593 
594 int
595 get_linemsgid(int line)
596 {
597 	int i, left, right;
598 	left = 0;
599 	right = line_msgcnt - 1;
600 	while (left <= right) {
601 		i = (left + right) >> 1;
602 		if (line < line_msgid[i].line) {
603 			right = i - 1;
604 		} else if (line > line_msgid[i].line) {
605 			left = i + 1;
606 		} else {
607 			return (line_msgid[i].msgid);
608 		}
609 	}
610 	return (NOLINEMSG);
611 }
612 
613 void
614 yyerror(char *s)
615 {
616 	if ((IsActiveMode(PreProcessMode) && pound_is_mine == FALSE) ||
617 	    IsActiveMode(ReplaceMode)) {
618 		return;
619 	}
620 	src_err(srcfile, lineno, gettext("%s before or at: %s"), s, yytext);
621 }
622 
623 void
624 warning(char *s)
625 {
626 	if ((IsActiveMode(PreProcessMode) && pound_is_mine == FALSE) ||
627 	    IsActiveMode(ReplaceMode)) {
628 		return;
629 	}
630 	src_err(srcfile, lineno, "%s", s);
631 }
632