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