home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / cproto.zip / cproto46 / lintlibs.c < prev    next >
C/C++ Source or Header  |  1998-01-18  |  14KB  |  650 lines

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