1 /* $Id: lintlibs.c,v 4.21 2011/01/02 19:24:03 tom Exp $
2  *
3  * C prototype/lint-library generator
4  * These routines implement the semantic actions for lint libraries executed by
5  * the yacc parser.
6  */
7 #include <semantic.h>
8 #include <symbol.h>
9 
10 #if OPT_LINTLIBRARY
11 
12 unsigned in_include;
13 
14 static SymbolTable *include_list;
15 static SymbolTable *declared_list;
16 
17 static int in_typedef;
18 static int blank_lines;		/* used to filter blank lines from typedefs */
19 
20 static int implied_cnt;		/* state-count associated with implied_buf */
21 static char *implied_buf;
22 
23 static char quote_l = '"';
24 static char quote_r = '"';
25 
26 /*
27  * Output a string to standard output, keeping track of the trailing newlines
28  * to make it simple to format with blank lines.
29  */
30 void
put_string(FILE * outf,const char * s)31 put_string(FILE *outf, const char *s)
32 {
33     if (*s != '\0') {
34 	fputs(s, outf);
35 	if (outf == stdout) {	/* ensure we aren't doing temp-file! */
36 	    while (*s != '\0') {
37 		if (*s++ == '\n')
38 		    blank_lines++;
39 		else
40 		    blank_lines = 0;
41 	    }
42 	}
43     }
44 }
45 
46 /*
47  * Output a single character
48  */
49 void
put_char(FILE * outf,int c)50 put_char(FILE *outf, int c)
51 {
52     static char s[] = "?";
53     s[0] = (char) c;
54     put_string(outf, s);
55 }
56 
57 /*
58  * Write a newline, taking care not to make a blank line
59  */
60 void
put_newline(FILE * outf)61 put_newline(FILE *outf)
62 {
63     while (!blank_lines)
64 	put_string(outf, "\n");
65 }
66 
67 /*
68  * Make a blank line (limited to 2 successive newlines)
69  */
70 void
put_blankline(FILE * outf)71 put_blankline(FILE *outf)
72 {
73     while (blank_lines < 2)
74 	put_string(outf, "\n");
75 }
76 
77 /*
78  * Put a token, padded by a tab if it is short enough
79  */
80 void
put_padded(FILE * outf,const char * s)81 put_padded(FILE *outf, const char *s)
82 {
83     put_string(outf, s);
84     put_char(outf, (lintLibrary() && strlen(s) < 8) ? '\t' : ' ');
85 }
86 
87 /*
88  * Format lint-library so that we put a blank line before each item that may
89  * take up multiple lines:
90  *	0) functions
91  *	1) typedefs (explicit and implied)
92  * as well as when transitioning to
93  *	2) variable declarations
94  *
95  * If the "-T" option is set, we skip a blank line around typedefs.
96  */
97 void
fmt_library(int code)98 fmt_library(int code)
99 {
100     if (lintLibrary() || types_out) {
101 	static int save;
102 
103 	if (!lintLibrary() && code == 0)
104 	    code = 3;
105 	if (code <= 1 || code != save)
106 	    put_blankline(stdout);
107 	save = code;
108     }
109 }
110 
111 /*
112  * conversion for names so test-diffs are less
113  * (patch: should use relpath)
114  */
115 static char *
strip_name(char * s)116 strip_name(char *s)
117 {
118 #ifdef	vms
119     static char stripped[BUFSIZ];
120     auto int len = strlen(getwd(stripped));
121     if (strlen(s) > len
122 	&& !strncmp(s, stripped, len))
123 	s += len;
124     s = (vms2name(stripped, s));
125 #else
126     static char GccLeaf[] = "/gcc-lib/";
127     static char IncLeaf[] = "/include/";
128     char *t;
129     unsigned n;
130     size_t len;
131     int standard = FALSE;
132 
133     for (n = 1; n < num_inc_dir; n++) {
134 	len = strlen(inc_dir[n]);
135 	if (!strncmp(inc_dir[n], s, len)
136 	    && is_path_sep(s[len])) {
137 	    standard = TRUE;
138 	    s += len + 1;
139 	    quote_l = '<';
140 	    quote_r = '>';
141 	    break;
142 	}
143     }
144     if (!standard) {
145 	quote_l =
146 	    quote_r = '"';
147 	if (*s == '.' && is_path_sep(s[1]))
148 	    s += 2;
149 	else if ((t = strstr(s, GccLeaf)) != 0
150 		 && (t = strstr(t, IncLeaf)) != 0) {
151 	    s = t + sizeof(IncLeaf) - 1;
152 	    quote_l = '<';
153 	    quote_r = '>';
154 	}
155     }
156 #endif
157     return s;
158 }
159 #define	CUR_FILE	strip_name(cur_file_name())
160 
161 static unsigned base_level;
162 static unsigned inc_depth = 0;
163 static char **inc_stack = 0;
164 
165 static char *
get_inc_stack(unsigned n)166 get_inc_stack(unsigned n)
167 {
168     return (((int) n) < 0 || n >= inc_depth) ? 0 : inc_stack[n];
169 }
170 
171 #ifdef	DEBUG
172 static void
dump_stack(char * tag)173 dump_stack(char *tag)
174 {
175     unsigned j;
176     printf("/* stack%s:%s", tag, cur_file_name());
177     for (j = 0; j <= in_include; j++)
178 	printf("\n\t%d%s:%s", j,
179 	       j == base_level ? "*" : "",
180 	       get_inc_stack(j) ? get_inc_stack(j) : "?");
181     printf(" */\n");
182 }
183 #endif /* DEBUG */
184 
185 static void
free_inc_stack(unsigned n)186 free_inc_stack(unsigned n)
187 {
188     if (get_inc_stack(n) != 0) {
189 	free(inc_stack[n]);
190 	inc_stack[n] = 0;
191     }
192 }
193 
194 static void
make_inc_stack(unsigned n,char * s)195 make_inc_stack(unsigned n, char *s)
196 {
197     if (n != 0) {
198 	unsigned need = (n | 31) + 1;
199 
200 	free_inc_stack(n);
201 	if (n > inc_depth) {
202 	    inc_stack = type_realloc(char *, inc_stack, need);
203 	    while (inc_depth < need)
204 		inc_stack[inc_depth++] = 0;
205 	    inc_depth = need;
206 	}
207 	inc_stack[n] = xstrdup(s);
208     }
209 }
210 
211 /*
212  * Keep track of include-files so that we only include each once.
213  */
214 static int
already_included(char * path)215 already_included(char *path)
216 {
217     if (!include_list)
218 	include_list = new_symbol_table();
219     if (find_symbol(include_list, path) != NULL)
220 	return TRUE;
221     new_symbol(include_list, path, NULL, DS_NONE);
222     return FALSE;
223 }
224 
225 /*
226  * Keep track of variables that may have been implicitly declared via
227  * include-files so that we declare them only once in the lint library
228  * output.
229  */
230 int
already_declared(char * name)231 already_declared(char *name)
232 {
233     if (declared_list == 0)
234 	declared_list = new_symbol_table();
235     if (find_symbol(declared_list, name) == 0) {
236 	(void) new_symbol(declared_list, name, 0, 0);
237 	return FALSE;
238     }
239     return TRUE;
240 }
241 
242 /*
243  * Initialize state for 'track_in()'
244  */
245 static int InitTracking;
246 
247 void
begin_tracking(void)248 begin_tracking(void)
249 {
250     InitTracking = FALSE;
251 }
252 
253 static int
c_suffix(char * path)254 c_suffix(char *path)
255 {
256     char *last = path + strlen(path);
257 #ifdef	vms
258     char *vers = strrchr(path, ';');
259     if (vers != 0)
260 	last = vers;
261 #endif
262     return ((last - path) > 2 && !strcmp(last - 2, ".c"));
263 }
264 
265 /*
266  * Keep track of "include files" that we always want to filter out (ignore).
267  */
268 static int
ignored(char * path)269 ignored(char *path)
270 {
271     if (strcmp(path, "<built-in>") == 0
272 	|| strcmp(path, "<command line>") == 0)
273 	return TRUE;
274     return FALSE;
275 }
276 
277 static const char *
skip_dot(const char * a)278 skip_dot(const char *a)
279 {
280     if (!strncmp(a, "./", (size_t) 2))
281 	a += 2;
282     return a;
283 }
284 
285 static int
same_file(const char * a,const char * b)286 same_file(const char *a, const char *b)
287 {
288     return !strcmp(skip_dot(a), skip_dot(b));
289 }
290 
291 /*
292  * For lint-library, we want to keep track of what file we are in so that we
293  * can generate appropriate comments and include-statements.
294  *
295  * The main program 'cproto' is invoked with 'cpp' once for each C-file,
296  * relying on it to spit out "#" comments which identify the name and line
297  * number of each processed file.  After the first '#' comment, all others
298  * refer to included files.
299  */
300 void
track_in(void)301 track_in(void)
302 {
303     static char old_file[MAX_TEXT_SIZE];	/* from last call */
304     auto int show = lintLibrary();
305 
306     if (!show && !do_tracking && !debug_trace)
307 	return;
308 
309 #ifdef	DEBUG
310     printf("/* track_in: in_include=%d line_num=%d base_file=%s */\n",
311 	   in_include, cur_line_num(), base_file);
312     dump_stack("-before");
313 #endif /* DEBUG */
314 
315     if (cur_line_num() == 0) {	/* begin new (nested?) file */
316 	if (!InitTracking) {
317 	    InitTracking = TRUE;
318 	    /* yacc may omit first cpp-line! */
319 	    in_include =
320 		base_level = (unsigned) !same_file(cur_file_name(), base_file);
321 	    make_inc_stack(0, base_file);
322 	} else if (same_file(cur_file_name(), base_file)) {
323 	    flush_varargs();
324 	    in_include = 0;	/* reset level */
325 	} else {
326 	    make_inc_stack(in_include, old_file);
327 	    if (in_include++ == 0) {
328 		char *s = CUR_FILE;
329 		if (show && !already_included(s) && !ignored(s)) {
330 		    fmt_library(4);
331 		    put_string(stdout, "#include ");
332 		    put_char(stdout, quote_l);
333 		    put_string(stdout, s);
334 		    put_char(stdout, quote_r);
335 		    put_newline(stdout);
336 		}
337 		if (debug_trace)
338 		    fprintf(stderr, "++ %s\n", cur_file_name());
339 	    }
340 	    make_inc_stack(in_include, cur_file_name());
341 	}
342 	(void) strcpy(old_file, cur_file_name());
343     } else if (same_file(cur_file_name(), base_file)) {
344 	in_include = 0;		/* kludgy bison! */
345 	(void) strcpy(old_file, cur_file_name());
346     } else if (!same_file(old_file, cur_file_name())) {		/* continue/unnest ? */
347 	unsigned n;
348 	int found;
349 	char *s = cur_file_name();
350 #ifdef DEBUG
351 	char temp[80];
352 #endif
353 
354 	flush_varargs();
355 	for (n = in_include, found = FALSE; (int) n >= 0; n--) {
356 	    if (same_file(get_inc_stack(n), s)) {
357 		found = TRUE;
358 		in_include--;
359 		break;
360 	    }
361 	}
362 	if (!found) {
363 	    /*
364 	     * There's two kinds of broken programs that can cause
365 	     * us to lose sync at this point:  (1) programs such as
366 	     * yacc that don't reference the grammar file, instead
367 	     * referencing the generated file, and (2) broken
368 	     * preprocessors (such as on OSF/1) that neglect to
369 	     * report line #1 on headers that are rejected due to
370 	     * prior inclusion.
371 	     *
372 	     * If the source file's extension is ".h", we'll assume
373 	     * the latter case (i.e., just report it).  The former
374 	     * case requires that we reset the stack.
375 	     */
376 #ifdef DEBUG
377 	    sprintf(temp, "/* lost sync @%d: ", cur_line_num() + 1);
378 	    put_blankline(stdout);
379 	    put_string(stdout, temp);
380 	    put_string(stdout, s);
381 	    put_string(stdout, " */\n");
382 #endif
383 	    if (in_include == 1 && c_suffix(s)) {
384 		/* yacc did it again! */
385 		in_include = 0;
386 		make_inc_stack(in_include, strcpy(base_file, s));
387 #ifdef DEBUG
388 		put_string(stdout, "/* processed ");
389 		put_string(stdout, s);
390 		put_string(stdout, " */\n");
391 #endif
392 	    }
393 	}
394 	(void) strcpy(old_file, get_inc_stack(in_include));
395     }
396 #ifdef	DEBUG
397     dump_stack("-after");
398 #endif /* DEBUG */
399 }
400 
401 /*
402  * Copy/append to 'implied_buf[]'
403  */
404 static void
add2implied_buf(const char * s,int append)405 add2implied_buf(const char *s, int append)
406 {
407     static size_t implied_len;	/* current strlen(implied_buf) */
408     static size_t implied_max;	/* maximum size of implied_buf */
409 
410     if (!append)
411 	implied_len = 0;
412     implied_len += strlen(s);
413 
414     if (implied_buf == 0)
415 	*(implied_buf = (char *) malloc(implied_max = BUFSIZ)) = '\0';
416     if (implied_max < implied_len + 2)
417 	implied_buf = (char *) realloc(implied_buf, implied_max +=
418 				       implied_len + 2);
419     if (!append)
420 	*implied_buf = '\0';
421     (void) strcat(implied_buf, s);
422 }
423 
424 /*
425  * If the "-t" option is set (or if we are generating a lint-library), we
426  * intercept tokens which are part of a typedef, copying them to the output.
427  *
428  * The 'imply_typedef()' entrypoint is called from the grammar for the special
429  * cases of struct/union/enum when we expect to be getting curly-braces which
430  * define the structure.  If no curly-braces are found by the end of the
431  * rule, we can discard the buffer.
432  */
433 int
want_typedef(void)434 want_typedef(void)
435 {
436     if (lintLibrary()) {
437 	if (in_include == 0)
438 	    return (TRUE);
439     } else if (types_out) {
440 	return (TRUE);
441     }
442     return (FALSE);
443 }
444 
445 void
begin_typedef(void)446 begin_typedef(void)
447 {
448     if (want_typedef()) {
449 	in_typedef = TRUE;
450 	fmt_library(1);
451 	copy_typedef("typedef");
452     }
453 }
454 
455 void
copy_typedef(const char * s)456 copy_typedef(const char *s)
457 {
458     if (!strcmp(s, "/*")
459 	|| *s == '#') ;		/* ignore */
460     else if (in_typedef) {
461 	if (*s == '\n')
462 	    put_newline(stdout);
463 	else
464 	    put_string(stdout, s);
465     } else if (implied_cnt > 0) {	/* "KEY ID {" ? */
466 	add2implied_buf(s, TRUE);
467 	if (!isspace(UCH(*s)))
468 	    implied_cnt--;
469 	if ((implied_cnt == 2 || implied_cnt == 1)
470 	    && !strcmp(s, "{")) {
471 	    implied_cnt = 9999;
472 	}
473     }
474 }
475 
476 void
end_typedef(void)477 end_typedef(void)
478 {
479     copy_typedef("\n");
480     in_typedef = FALSE;
481     (void) implied_typedef();
482 }
483 
484 void
imply_typedef(const char * s)485 imply_typedef(const char *s)
486 {
487     if (!in_typedef && want_typedef()) {
488 	add2implied_buf(s, FALSE);
489 	implied_cnt = 3;
490     }
491 }
492 
493 char *
implied_typedef(void)494 implied_typedef(void)
495 {
496     if (implied_cnt > 0) {
497 	implied_cnt = 0;
498 	return (implied_buf);
499     }
500     return (0);
501 }
502 
503 /*
504  * Indent lint-library stuff to make it readable
505  */
506 void
indent(FILE * outf)507 indent(FILE *outf)
508 {
509     put_string(outf, "\n\t\t");
510 }
511 
512 /* Test for the special case of an ellipsis-parameter when trying to make a
513  * lint-library
514  */
515 int
lint_ellipsis(Parameter * p)516 lint_ellipsis(Parameter * p)
517 {
518     return (knrLintLibrary()
519 	    && (!strcmp(p->declarator->name, ELLIPSIS)));
520 }
521 
522 /*
523  * Reset the data used for "VARARGS" comment.  Actually, reset almost any
524  * special attribute that's attached to a function, so we don't accidentally
525  * propagate it to the next function (or data) to be output.
526  */
527 void
flush_varargs(void)528 flush_varargs(void)
529 {
530     exitlike_func = FALSE;
531 
532     varargs_num = 0;
533     if (varargs_str != 0) {
534 	free(varargs_str);
535 	varargs_str = 0;
536     }
537 }
538 
539 /* If either we received a "VARARGS" comment in the lexical scanner, or if the
540  * parameter list contains an ellipsis, generate a corresponding "VARARGS"
541  * comment for lint-library output.
542  */
543 void
ellipsis_varargs(Declarator * d)544 ellipsis_varargs(Declarator * d)
545 {
546     int count;
547     Parameter *p;
548 
549     fmt_library(0);
550     for (p = d->params.first, count = 0; p != 0; p = p->next, count++)
551 	if (lint_ellipsis(p)) {
552 	    varargs_num = count;
553 	    break;
554 	}
555     if (varargs_num != 0) {
556 	put_string(stdout, "\t/* VARARGS");
557 	if (varargs_num > 0) {
558 	    printf("%d", varargs_num);
559 	    if (varargs_str != 0) {
560 		put_char(stdout, ' ');
561 		put_string(stdout, varargs_str);
562 	    }
563 	}
564 	flush_varargs();
565 	put_string(stdout, " */\n");
566     }
567 }
568 
569 /* (Attempt to) create a parameter name for lint-library applications in which
570  * we are starting from a function prototype which has no explicit parameter
571  * name.
572  */
573 char *
supply_parm(int count)574 supply_parm(int count)
575 {
576     static char temp[80];
577     (void) sprintf(temp, "p%d", count);
578     while (is_typedef_name(temp) && (strlen(temp) < sizeof(temp) - 1))
579 	(void) strcat(temp, "_");
580     return (temp);
581 }
582 
583 /*
584  * (Attempt to) distinguish between declarators for functions and for
585  * function pointers.
586  */
587 int
is_actual_func(Declarator * d)588 is_actual_func(Declarator * d)
589 {
590     if (lintLibrary() && (d->func_def != FUNC_NONE)) {
591 	if (d->func_stack->text[0] == PAREN_L
592 	    && d->func_stack->text[1] == '*') {
593 	    if (strstr(d->func_stack->text, "()") != 0)
594 		return TRUE;
595 	} else {
596 	    return TRUE;
597 	}
598     }
599     return FALSE;
600 }
601 
602 /*
603  * Output the body (or terminating semicolon) of a procedure
604  */
605 void
put_body(FILE * outf,DeclSpec * decl_spec,Declarator * declarator)606 put_body(
607 	    FILE *outf,
608 	    DeclSpec * decl_spec,	/* declaration specifier */
609 	    Declarator * declarator)
610 {
611     const char *spec_text;
612 
613     if (is_actual_func(declarator)) {
614 	strcut(decl_spec->text, "static");
615 	strcut(decl_spec->text, "extern");
616 	indent(outf);
617 	put_char(outf, CURL_L);
618 	if (!*(spec_text = decl_spec->text))
619 	    spec_text = "void";
620 	if (exitlike_func) {
621 	    put_string(outf, " for(;;); /* no return */ ");
622 	} else if (!strcmp(spec_text, "void")
623 		   && declarator->text[0] != '*'
624 		   && declarator->func_stack->func_def == FUNC_NONE) {
625 	    put_string(outf, " /* void */ ");
626 	} else {
627 	    put_string(outf, " return(*(");
628 	    if (declarator->func_stack->func_def == FUNC_NONE) {
629 		put_string(outf, spec_text);
630 		put_char(outf, ' ');
631 		if (declarator->pointer) {
632 		    char *s = declarator->text;
633 		    while (*s++ == '*')
634 			put_char(outf, '*');
635 		}
636 		put_char(outf, '*');
637 	    } else {
638 		put_string(outf, spec_text);
639 		put_string(outf, "(*)()");
640 	    }
641 	    put_string(outf, ")0); ");
642 	}
643 	put_char(outf, CURL_R);
644     } else {
645 	if (proto_style == PROTO_LINTLIBRARY
646 	    || proto_style == PROTO_ANSI_LLIB) {
647 	    /* SVR4 lint complains if we declare const data without an
648 	     * initializer.
649 	     */
650 	    if (strkey(decl_spec->text, "const") != NULL
651 		|| strkey(declarator->text, "const") != NULL) {
652 		put_string(outf, " = {0}");
653 	    }
654 	}
655 	put_string(outf, ";");
656     }
657     put_newline(outf);
658     exitlike_func = FALSE;
659 }
660 
661 #ifdef NO_LEAKS
662 void
free_lintlibs(void)663 free_lintlibs(void)
664 {
665     unsigned n;
666 
667     if (implied_buf != 0)
668 	free(implied_buf);
669     if (inc_stack != 0) {
670 	for (n = 0; n < inc_depth; n++)
671 	    free_inc_stack(n);
672 	free(inc_stack);
673     }
674     if (include_list != 0)
675 	free_symbol_table(include_list);
676     if (declared_list != 0)
677 	free_symbol_table(declared_list);
678 }
679 #endif
680 
681 #endif /* OPT_LINTLIBRARY */
682