home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume4 / printfck / printfck.c next >
C/C++ Source or Header  |  1986-11-30  |  10KB  |  575 lines

  1. /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/
  2. /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/
  3. /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */
  4. #include    <stdio.h>
  5.  
  6. /* Feed with a list of routine names and descriptions:
  7.  *    printf("",...)
  8.  *    sprintf(s,"",...)
  9.  *    fprintf(f,"",...)
  10.  * and with a source file; produce output in which occurrences of e.g.
  11.  *    sprintf(buf, "%s%ld", s, l)
  12.  * are replaced by
  13.  *    sprintf(buf, "%s%ld", procent_s(s), procent_L(l))
  14.  * Now let lint do the checking.
  15.  * Bugs:
  16.  *    Cases where the format string is not explicitly given (e.g., is the
  17.  *    result of some other routine, or looks like  bool ? "s1" : "s2")
  18.  *    are not handled.
  19.  *    Cases where the preprocessor produces quotes or comment delimiters
  20.  *    or concatenates partial identifiers are not handled.
  21.  *    We do not distinguish two sets of identifiers.
  22.  *    Only the parts lint sees get checked - not parts between (false)
  23.  *    #ifdef's. If the call to printf is outside #ifdef's, but some
  24.  *    args are inside, printfck may get confused. However, this is easy
  25.  *    to avoid:
  26.  *
  27.  *    THIS FAILS            THIS WORKS
  28.  *    ----------            ----------
  29.  *        printf("%s%d",            printf("%s%d", (
  30.  *    #ifdef debug            #ifdef debug
  31.  *            "foo"                "foo"
  32.  *    #else                #else
  33.  *            "bar"                "bar"
  34.  *    #endif debug            #endif debug
  35.  *            , num);                ), num);
  36.  *
  37.  */
  38.  
  39. char *index();
  40. char *rindex();
  41. char *malloc();
  42.  
  43. #define MAXIRS 100
  44.  
  45. struct ir {
  46.     char *rname;
  47.     int pn;        /* number of args preceding format string */
  48. } irs[MAXIRS+1] = {    /* should be read in - for now explicit */
  49.     "printf",    0,
  50.     "fprintf",    1,
  51.     "sprintf",    1,
  52. };
  53.  
  54. int nirs;
  55.  
  56. char *progname = "printfck";
  57. char *filename = NULL;
  58. FILE *inp;
  59.  
  60. int eof;
  61. int peekc;
  62. int lastc;
  63. int linenr;
  64.  
  65. initgetcx()
  66. {
  67.     eof = 0;
  68.     peekc = '\n';    /* recognize # on very first line */
  69.     lastc = 0;    /* result of last getchar() */
  70.     linenr = 1;
  71. }
  72.  
  73. getcx()
  74. {
  75.     register int c;
  76.  
  77.     if(peekc) {
  78.         c = peekc;
  79.         peekc = 0;
  80.     } else if(eof) {
  81.         c = EOF;
  82.     } else {
  83.         if(lastc) {
  84.             putchar(lastc);
  85.             lastc = 0;
  86.         }
  87.         if((c = getc(inp)) == EOF)
  88.             eof++;
  89.         else {
  90.             lastc = c;
  91.             if(c == '\n')
  92.                 linenr++;
  93.         }
  94.     }
  95.  
  96.     return(c);
  97. }
  98.  
  99. /* Note: we do not want to eliminate comments; perhaps they contain
  100.    lint directives. */
  101. getcy()        /* as getcx(), but skip comments */
  102. {
  103.     register int c = getcx();
  104.  
  105.     if(c == '/') {
  106.         c = getcx();
  107.         if(c == '*') {
  108.             while(1) {
  109.                 c = getcx();
  110.                 if(c == EOF)
  111.                     error("unfinished comment");
  112.                 while(c == '*') {
  113.                     c = getcx();
  114.                     if(c == '/')
  115.                         return(getcy());
  116.                 }
  117.             }
  118.         } else {
  119.             peekc = c;
  120.             c = '/';
  121.         }
  122.     }
  123.     return(c);
  124. }
  125.  
  126. getcz()        /* as getcy(), but skip preprocessor directives */
  127. {
  128.     register int c = getcy();
  129.  
  130.     while(c == '\n') {
  131.         c = getcx();
  132.         if(c == '#') {
  133.             while(c != '\n') {
  134.                 c = getcx();
  135.                 if(c == EOF)
  136.                     error("incomplete line");
  137.                 while(c == '\\') {
  138.                     (void) getcx(); c = getcx();
  139.                 }
  140.             }
  141.         } else {
  142.             peekc = c;
  143.             return('\n');
  144.         }
  145.     }
  146.     return(c);
  147. }
  148.  
  149. getcq()        /* as getcz() but skip strings */
  150. {
  151.     register int c = getcz();
  152.     register int delim;
  153.  
  154.     if(c == '\'' || c == '"') {
  155.         delim = c;
  156.         while(1) {
  157.             c = getcx();
  158.             if(c == '\n' || c == EOF)
  159.                 error("Unfinished string (delim %c)", delim);
  160.             if(c == '\\') {
  161.                 (void) getcx();
  162.                 continue;
  163.             }
  164.             if(c == delim)
  165.                 return(getcq());
  166.         }
  167.     }
  168.     return(c);
  169. }
  170.  
  171. usage()
  172. {
  173.     fprintf(stderr,
  174.       "Usage: %s [-n] [-e function] ... [-f datafile] ... [file.c] ...\n",
  175.       progname);
  176.     exit(2);
  177. }
  178.  
  179. extern char *optarg;
  180. extern int optind;
  181.  
  182. main(argc, argv)
  183. int argc;
  184. char **argv;
  185. {
  186.     register int c;
  187.     FILE *fp;
  188.  
  189.     if (argc > 0) {
  190.         progname = rindex(argv[0], '/');
  191.         if (progname != NULL)
  192.             ++progname;
  193.         else
  194.             progname = argv[0];
  195.     }
  196.  
  197.     for (; irs[nirs].rname != NULL; ) ++nirs; /* Count defaults */
  198.  
  199.     while ((c = getopt(argc, argv, "e:nf:")) != EOF) {
  200.         switch (c) {
  201.         case '?':
  202.             usage();
  203.             /* NOTREACHED */
  204.         case 'e':
  205.             addir(optarg, 0);
  206.             break;
  207.         case 'n':
  208.             nirs = 0;
  209.             irs[nirs].rname = NULL;
  210.             break;
  211.         case 'f':
  212.             getirfile(optarg);
  213.             break;
  214.         }
  215.     }
  216.  
  217.     printf("#include \"procent.h\"\n");
  218.  
  219.     if (optind == argc)
  220.         treat(stdin, NULL);
  221.     else {
  222.         for (; optind < argc; ++optind) {
  223.             if (strcmp(argv[optind], "-") == 0)
  224.                 treat(stdin, NULL);
  225.             else {
  226.                 filename = argv[optind];
  227.                 fp = fopen(filename, "r");
  228.                 if (fp == NULL)
  229.                     filerror(filename);
  230.                 treat(fp, filename);
  231.                 fclose(fp);
  232.             }
  233.         }
  234.     }
  235.     exit(0);
  236. }
  237.  
  238. treat(fp, file)
  239. FILE *fp;
  240. char *file;
  241. {
  242.     register int c;
  243.  
  244.     filename = file;
  245.     linenr = 0;
  246.     inp = fp;
  247.     initgetcx();
  248.     while((c = getcq()) != EOF) {
  249.  
  250.         /* check for (interesting) identifiers */
  251.         if(letter(c))
  252.             rd_id(c);
  253.     }
  254.     filename = NULL;
  255.     linenr = 0;
  256.     irs[nirs].rname = NULL;
  257. }
  258.  
  259.  
  260. rd_id(first)
  261. register int first;
  262. {
  263.     char idf[256];
  264.     register char *ip = idf;
  265.     register int c;
  266.  
  267.     *ip++ = first;
  268.     while(letdig(c = getcx()))
  269.         if(ip-idf < sizeof(idf)-1)
  270.             *ip++ = c;
  271.     peekc = c;
  272.     *ip = 0;
  273.     handle(idf);
  274. }
  275.  
  276. /*VARARGS1*/
  277. error(s, x)
  278. char *s;
  279. {
  280.     printf("\n"); /* Finish incomplete output line */
  281.     fprintf(stderr, "%s: Error (", progname);
  282.     if (filename != NULL) fprintf(stderr, "%s, ", filename);
  283.     fprintf(stderr, "line %d): ", linenr);
  284.     fprintf(stderr, s, x);
  285.     fprintf(stderr, "\n");
  286.     exit(1);
  287. }
  288.  
  289. /*VARARGS1*/
  290. warning(s, x1, x2)
  291. char *s;
  292. {
  293.     fprintf(stderr, "%s: Warning (", progname);
  294.     if (filename != NULL) fprintf(stderr, "%s, ", filename);
  295.     fprintf(stderr, "line %d): ", linenr);
  296.     fprintf(stderr, s, x1, x2);
  297.     fprintf(stderr, "\n");
  298. }
  299.  
  300. filerror(file)
  301. char *file;
  302. {
  303.     fprintf(stderr, "%s: can't open ", progname);
  304.     perror(file);
  305.     exit(2);
  306. }
  307.  
  308. letter(c)
  309. register int c;
  310. {
  311.     return(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_');
  312. }
  313.  
  314. digit(c)
  315. register int c;
  316. {
  317.     return('0' <= c && c <= '9');
  318. }
  319.  
  320. letdig(c)
  321. register int c;
  322. {
  323.     return(letter(c) || digit(c));
  324. }
  325.  
  326. handle(idf)
  327. register char *idf;
  328. {
  329.     register struct ir *irp = irs;
  330.  
  331.     while(irp->rname) {
  332.         if(!strcmp(idf, irp->rname)) {
  333.             doit(irp);
  334.             return;
  335.         }
  336.         irp++;
  337.     }
  338. }
  339.  
  340. skipspaces()
  341. {
  342.     register int c;
  343.  
  344.     while(1) {
  345.         c = getcz();
  346.         if(c == ' ' || c == '\t' || c == '\n')
  347.             continue;
  348.         peekc = c;
  349.         return;
  350.     }
  351. }
  352.  
  353. doit(irp)
  354. register struct ir *irp;
  355. {
  356.     register int c, cnt = irp->pn;
  357.  
  358.     skipspaces();
  359.     if((c = getcz()) != '(') {
  360.         peekc = c;
  361.         warning("%s not followed by '('", irp->rname);
  362.         return;
  363.     }
  364.  
  365.     while(cnt--) {
  366.         c = skiparg();
  367.         if(c != ',') {
  368.             peekc = c;
  369.             warning("arg of %s not followed by comma", irp->rname);
  370.             return;
  371.         }
  372.     }
  373.     skipspaces();
  374.  
  375.     /* now parse format string (if present) */
  376.     /* (here we also avoid defining occurrences) */
  377.     if((c = getcx()) != '"') {
  378.         peekc = c;
  379.         return;
  380.     }
  381.     domore(irp);
  382. }
  383.  
  384. domore(irp)
  385. register struct ir *irp;
  386. {
  387.     char fmt[256];
  388.     register char *fp = fmt;
  389.     register int c;
  390.  
  391.     while(1) {
  392.         c = getcx();
  393.         if(c == '\n' || c == EOF)
  394.             error("Unfinished format string");
  395.         if(c == '"')
  396.             break;
  397.         if(c != '%') {
  398.             if (c == '\\')
  399.                 (void) getcx();
  400.             continue;
  401.         }
  402.         c = getcx();
  403.         if(c == '%')
  404.             continue;
  405.         if(c == '-')
  406.             c = getcx();
  407.         if(c == '*') {
  408.             c = getcx();
  409.             if(fp-fmt < sizeof(fmt)-1)
  410.                 *fp++ = 'd';
  411.         } else while(digit(c))
  412.             c = getcx();
  413.         if(c == '.')
  414.             c = getcx();
  415.         if(c == '*') {
  416.             c = getcx();
  417.             if(fp-fmt < sizeof(fmt)-1)
  418.                 *fp++ = 'd';
  419.         } else while(digit(c))
  420.             c = getcx();
  421.         if(c == '#')
  422.             c = getcx();
  423.         if(c == 'l') {
  424.             c = getcx();
  425.             if('a' <= c && c <= 'z')
  426.                 c -= 'a'-'A';
  427.             else
  428.                 error("%%l not followed by lowercase");
  429.         }
  430.         if(fp-fmt < sizeof(fmt)-1)
  431.             *fp++ = c;
  432.         else
  433.             warning("ridiculously long format");
  434.     }
  435.     *fp = 0;
  436.     fp = fmt;
  437.     skipspaces();
  438.     while((c = getcz()) == ',') {
  439.         if(!*fp)
  440.             error("%s has too many arguments", irp->rname);
  441.         skipspaces();
  442.         printf("procent_%c(", *fp++);
  443.         c = skiparg();
  444.         printf(")");
  445.         peekc = c;
  446.     }
  447.     if(c != ')')
  448.         error("%s has ill-formed argument list", irp->rname);
  449.     if(*fp)
  450.         error("%s has too few arguments", irp->rname);
  451. }
  452.  
  453. skiparg()
  454. {
  455.     register int parenct = 0;
  456.     register int c;
  457.  
  458.     parenct = 0;
  459.     while(1) {
  460.         c = getcq();
  461.         if(c == EOF)
  462.             error("eof in arg list");
  463.         if(!parenct && (c == ',' || c == ')'))
  464.             return(c);
  465.         if(c == '(' || c == '[') {
  466.             parenct++;
  467.             continue;
  468.         }
  469.         if(c == ')' || c == ']') {
  470.             parenct--;
  471.             continue;
  472.         }
  473.     }
  474. }
  475.  
  476. char *strsave(s)
  477. char *s;
  478. {
  479.     char *t = malloc(strlen(s) + 1);
  480.     if (t == NULL) error("out of memory");
  481.     strcpy(t, s);
  482.     return t;
  483. }
  484.  
  485. getirfile(irfile)
  486. char *irfile;
  487. {
  488.     FILE *fp = fopen(irfile, "r");
  489.     char line[256];
  490.     char name[256];
  491.     char *end;
  492.     int n;
  493.     int cnt;
  494.  
  495.     if (fp == NULL) filerror(irfile);
  496.     filename = irfile;
  497.     linenr = 0;
  498.     while (fgets(line, sizeof line, fp)) {
  499.         ++linenr;
  500.         end = index(line, '#');
  501.         if (end) *end = '\0';
  502.         n= sscanf(line, " %s %d %s", name, &cnt, name+1);
  503.         if (n == 0 || name[0] == '\0')
  504.             continue; /* Skip empty line or comment */
  505.         if (n != 2 || cnt < 0)
  506.             error("bad format (must be %%s %%u)");
  507.         /* Should also check for valid name... */
  508.         addir(strsave(name), cnt);
  509.     }
  510.     fclose(fp);
  511.     filename = NULL;
  512.     linenr = 0;
  513. }
  514.  
  515. addir(name, cnt)
  516. char *name;
  517. int cnt;
  518. {
  519.     if (nirs >= MAXIRS) error("table overflow");
  520.     irs[nirs].rname = name;
  521.     irs[nirs].pn = cnt;
  522.     ++nirs;
  523. }
  524.  
  525. /*
  526.  * get option letter from argument vector
  527.  */
  528. int    opterr = 1,        /* useless, never set or used */
  529.     optind = 1,        /* index into parent argv vector */
  530.     optopt;            /* character checked for validity */
  531. char    *optarg;        /* argument associated with option */
  532.  
  533. #define BADCH    (int)'?'
  534. #define EMSG    ""
  535. #define tell(s)    fputs(*nargv,stderr);fputs(s,stderr); \
  536.         fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
  537.  
  538. getopt(nargc,nargv,ostr)
  539. int    nargc;
  540. char    **nargv,
  541.     *ostr;
  542. {
  543.     static char    *place = EMSG;    /* option letter processing */
  544.     register char    *oli;        /* option letter list index */
  545.     char    *index();
  546.  
  547.     if(!*place) {            /* update scanning pointer */
  548.         if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
  549.         if (*place == '-') {    /* found "--" */
  550.             ++optind;
  551.             return(EOF);
  552.         }
  553.     }                /* option letter okay? */
  554.     if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
  555.         if(!*place) ++optind;
  556.         tell(": illegal option -- ");
  557.     }
  558.     if (*++oli != ':') {        /* don't need argument */
  559.         optarg = NULL;
  560.         if (!*place) ++optind;
  561.     }
  562.     else {                /* need an argument */
  563.         if (*place) optarg = place;    /* no white space */
  564.         else if (nargc <= ++optind) {    /* no arg */
  565.             place = EMSG;
  566.             tell(": option requires an argument -- ");
  567.         }
  568.          else optarg = nargv[optind];    /* white space */
  569.         place = EMSG;
  570.         ++optind;
  571.     }
  572.     return(optopt);            /* dump back option letter */
  573. }
  574.  
  575.