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