home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume6 / printfck2 < prev    next >
Text File  |  1986-11-30  |  33KB  |  1,086 lines

  1. Subject: v06i021:  new printfck and manpage (printfck2)
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4. Reply-To: liam@cs.qmc.ac.uk (William Roberts)
  5.  
  6. Mod.sources: Volume 6, Issue 21
  7. Submitted by: talcott!seismo!mcvax!cs.qmc.ac.uk!liam
  8. Archive-name: printfck2
  9.  
  10. [ One of the major reasons why I'm sending this out is that is
  11.   that there is now a manual page.  From now on, I will probably
  12.   not send out anything that is missing one.  Probably exceptions
  13.   are non-Unix source, like the recent Apollo pacman.  --r$]
  14.  
  15. The enclosed shar file is an enhanced version of printfck which
  16. generates check code for scanf() as well as printf(), has a
  17. manual page and comes with a makefile.  It is a straightforward
  18. extension of the program from Guido van Rossum (mod.sources:
  19. Volume 4, Issue 114) which was recently distributed.
  20.  
  21. William Roberts                 ARPA: liam@cs.qmc.ac.uk
  22. Queen Mary College              UUCP: liam@qmc-cs.UUCP
  23. LONDON, UK
  24.  
  25. --------------------CUT HERE--------------------
  26. #! /bin/sh
  27. #  shar:  Shell Archiver
  28. #         Run the following with /bin/sh to create:
  29. #             README
  30. #             printfck.1
  31. #             printfck.c
  32. #             printfck.h
  33. #             percent.c
  34. #             Makefile
  35. # This archive created: Fri May 30 18:24:35 BST 1986
  36. echo shar: extracting "README" '('3109 chars')'
  37. if test -f README
  38. then
  39.     echo shar: will not overwrite existing file "README"
  40. else
  41. cat << \SHAR_EOF > README
  42. Mod.sources:  Volume 4, Issue 114
  43. Submitted by: Guido van Rossum <seismo!mcvax!guido>
  44.  
  45. Here's something a colleague of mind wrote one or two years ago, and
  46. which recently prompted some interest on the net.  Unfortunately it is
  47. not a very finished product; I post this so that others can benefit from
  48. it and change it to fit their needs.  I tried to compile and run it (on
  49. a VAX running 4.2BSD) and it gave sensible output when fed with itself
  50. as input -- I can't say more about the quality.  Foreseen use is
  51. something like:
  52.         printfck file.c >temp.c
  53.         lint temp.c procent.c
  54. Lint warnings about improper usage of any of the procent_* functions
  55. mean you're using an incorrect argument to a % escape.  For variable
  56. strings used as formats it doesn't help you; see also the comments at
  57. the begin of the program.
  58. Look in the program to find the command line options (you can feed it
  59. your own list of printf-like functions).
  60.  
  61. I'm sorry I can't spend more time on this (well I can but don't intend
  62. to since I have no need for it right now).  If anybody comes up with a
  63. manual, an improved version or any other changes, I'd like to hear about
  64. it.
  65.  
  66. Greetings,
  67.         Guido van Rossum, CWI, Amsterdam <guido@mcvax.UUCP>
  68.  
  69. /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/
  70. /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/
  71. /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */
  72. #include        <stdio.h>
  73.  
  74. /* Feed with a list of routine names and descriptions:
  75.  *      printf("",...)
  76.  *      sprintf(s,"",...)
  77.  *      fprintf(f,"",...)
  78.  * and with a source file; produce output in which occurrences of e.g.
  79.  *      sprintf(buf, "%s%ld", s, l)
  80.  * are replaced by
  81.  *      sprintf(buf, "%s%ld", procent_s(s), procent_L(l))
  82.  * Now let lint do the checking.
  83.  * Bugs:
  84.  *      Cases where the format string is not explicitly given (e.g., is the
  85.  *      result of some other routine, or looks like  bool ? "s1" : "s2")
  86.  *      are not handled.
  87.  *      Cases where the preprocessor produces quotes or comment delimiters
  88.  *      or concatenates partial identifiers are not handled.
  89.  *      We do not distinguish two sets of identifiers.
  90.  *      Only the parts lint sees get checked - not parts between (false)
  91.  *      #ifdef's. If the call to printf is outside #ifdef's, but some
  92.  *      args are inside, printfck may get confused. However, this is easy
  93.  *      to avoid:
  94.  *
  95.  *      THIS FAILS                      THIS WORKS
  96.  *      ----------                      ----------
  97.  *              printf("%s%d",                  printf("%s%d", (
  98.  *      #ifdef debug                    #ifdef debug
  99.  *                      "foo"                           "foo"
  100.  *      #else                           #else
  101.  *                      "bar"                           "bar"
  102.  *      #endif debug                    #endif debug
  103.  *                      , num);                         ), num);
  104.  *
  105.  */
  106.  
  107.  
  108. 30/5/86         W.T. Roberts <liam@cs.qmc.ac.uk>
  109.  
  110. I have modified printfck to handle scanf() as well as printf(),
  111. produced a manual page and a Makefile.
  112. SHAR_EOF
  113. if test 3109 -ne `wc -c < README`
  114. then
  115.     echo shar: error transmitting "README" '('should be 3109 chars')'
  116. else
  117.     echo README
  118. fi
  119. fi
  120. echo shar: extracting "printfck.1" '('3752 chars')'
  121. if test -f printfck.1
  122. then
  123.     echo shar: will not overwrite existing file "printfck.1"
  124. else
  125. cat << \SHAR_EOF > printfck.1
  126. .TH PRINTFCK 1  "18 January 1983"
  127. .SH NAME
  128. printfck \- modify C program to enable typechecking of printf() calls
  129. .SH SYNOPSIS
  130. .B printfck
  131. [ -n ]
  132. [ -e functionname ] ...
  133. [ -f functionfile ] ...
  134. [ c-program ] ...
  135. .SH DESCRIPTION
  136. .I Printfck
  137. reads the C-program source from the named files (or standard
  138. input if no arguments are given) and determines the types of
  139. the arguments to
  140. .IR printf (3S)
  141. and
  142. .IR scanf (3S)
  143. according to the given format string. It writes to standard
  144. output a modified version of the program, where the
  145. .I printf()
  146. or
  147. .I scanf()
  148. arguments are turned into calls to dummy routines. For example:
  149. .PP
  150.                 printf("Name %8s not known.", x);
  151.  becomes
  152.                 printf("Name %8s not known.", percent_s(x));
  153. .PP
  154. The routine percent_s() is defined within the modified program
  155. to be a function requiring a pointer to a character, so
  156. .IR lint (1)
  157. can now check that variable x does indeed have the appropriate
  158. type.  Any lint warnings about improper usage of a percent_*
  159. function indicates that the corresponding printf argument does
  160. not match the command string!
  161. .PP
  162. The program recognises the routines
  163. .IR printf() ,
  164. .IR sprintf() ,
  165. .IR fprintf() ,
  166. .IR scanf() ,
  167. .I sscanf()
  168. and
  169. .IR fscanf() .
  170. .PP
  171. A typical way of using this would be:
  172.  
  173.                 printfck part1.c part2.c part3.c  > temp.c
  174.                 lint temp.c
  175. .SH "OPTIONS"
  176. The options allow modification of the list of functions checked
  177. by
  178. .IR printfck .
  179. This allows the correct checking of user-defined variants of
  180. printf() or scanf()
  181. and makes lint give more useful line numbers when it
  182. complains.
  183. .TP
  184. -n
  185. Erase the default list of functions.
  186. .TP
  187. -e
  188. The following argument is the name of a function to be checked
  189. in the same way as printf(): its first argument is a printf()
  190. format string from which subsequent argument types may be
  191. deduced. Note that fprintf() could not be specified in this
  192. way, because its first argument is not the format string.
  193. .TP
  194. -f
  195. The following argument is the name of a file containing a list
  196. of (function name,
  197. .IR n ,
  198. .IR t )
  199. triples, where
  200. .I n
  201. is the number of arguments which precede the format string
  202. and
  203. .I t
  204. indicates the type of formatting involved; 0 means
  205. printf()-style and 1 means scanf()-style.
  206. For example, the following file is equivalent to the default
  207. function names (# indicates a comment):
  208. .PP
  209.                 # default specification for printfck
  210.                 printf   0        # printf( "format", arg2, ... );
  211.                 fprintf  1        # fprintf( fp, "format", arg3, ... );
  212.                 sprintf  1        # sprintf( s, "format", arg3, ... );
  213.                 scanf    0        # scanf( "format", arg2, ... );
  214.                 fscanf   1        # fscanf( fp, "format", arg3, ... );
  215.                 sscanf   1        # sscanf( s, "format", arg3, ... );
  216. .SH "LIMITATIONS"
  217. If the format string is not an explicit constant,
  218. .I printfck
  219. cannot help you. It will however do its level best, and can
  220. cope with complicated #ifdefs, given suitable hints:
  221.  
  222. .nr x \n(.lu/2u
  223. .in +0.5i
  224.  THIS FAILS                \h'|\nxu' THIS WORKS
  225.                            \h'|\nxu'
  226.         printf("%s%d",     \h'|\nxu'         printf("%s%d", (
  227.  #ifdef debug              \h'|\nxu' #ifdef debug
  228.                 "foo"      \h'|\nxu'                 "foo"
  229.  #else                     \h'|\nxu' #else
  230.                 "bar"      \h'|\nxu'                 "bar"
  231.  #endif debug              \h'|\nxu' #endif debug
  232.                 , num);    \h'|\nxu'                 ), num);
  233. .in
  234. .SH "BUGS"
  235. Doesn't check arguments to see if they contain invocations of
  236. the routines to be checked.
  237. .SH "SEE ALSO"
  238. printf(3S), scanf(3S), lint(1)
  239. .SH "AUTHOR"
  240. .nf
  241. Mod.sources:  Volume 4, Issue 114
  242. Submitted by: Guido van Rossum <seismo!mcvax!guido>
  243. SHAR_EOF
  244. if test 3752 -ne `wc -c < printfck.1`
  245. then
  246.     echo shar: error transmitting "printfck.1" '('should be 3752 chars')'
  247. else
  248.     echo printfck.1
  249. fi
  250. fi
  251. echo shar: extracting "printfck.c" '('13824 chars')'
  252. if test -f printfck.c
  253. then
  254.     echo shar: will not overwrite existing file "printfck.c"
  255. else
  256. cat << \SHAR_EOF > printfck.c
  257. /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/
  258. /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/
  259. /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */
  260. /* $Header: printfck.c,v 1.3 86/05/30 12:31:42 liam Exp $
  261.  * $Log:        printfck.c,v $
  262.  * Revision 1.3  86/05/30  12:31:42  liam
  263.  * Added facility to recognise scanf formats as well.
  264.  *
  265.  */
  266.  
  267. #include        <stdio.h>
  268. #include        <strings.h>
  269.  
  270. #include "printfck.h"
  271.  
  272. /* Feed with a list of routine names and descriptions:
  273.  *      printf("",...)
  274.  *      sprintf(s,"",...)
  275.  *      fprintf(f,"",...)
  276.  * and with a source file; produce output in which occurrences of e.g.
  277.  *      sprintf(buf, "%s%ld", s, l)
  278.  * are replaced by
  279.  *      sprintf(buf, "%s%ld", percent_s(s), percent_L(l))
  280.  * Now let lint do the checking.
  281.  * Bugs:
  282.  *      Cases where the format string is not explicitly given (e.g., is the
  283.  *      result of some other routine, or looks like  bool ? "s1" : "s2")
  284.  *      are not handled.
  285.  *      Cases where the preprocessor produces quotes or comment delimiters
  286.  *      or concatenates partial identifiers are not handled.
  287.  *      We do not distinguish two sets of identifiers.
  288.  *      Only the parts lint sees get checked - not parts between (false)
  289.  *      #ifdef's. If the call to printf is outside #ifdef's, but some
  290.  *      args are inside, printfck may get confused. However, this is easy
  291.  *      to avoid:
  292.  *
  293.  *      THIS FAILS                      THIS WORKS
  294.  *      ----------                      ----------
  295.  *              printf("%s%d",                  printf("%s%d", (
  296.  *      #ifdef debug                    #ifdef debug
  297.  *                      "foo"                           "foo"
  298.  *      #else                           #else
  299.  *                      "bar"                           "bar"
  300.  *      #endif debug                    #endif debug
  301.  *                      , num);                         ), num);
  302.  *
  303.  */
  304.  
  305. char *index();
  306. char *rindex();
  307. char *malloc();
  308.  
  309. #define MAXIRS 100
  310.  
  311. struct ir {
  312.         char *rname;
  313.         int pn;         /* number of args preceding format string */
  314.         int type;       /* 0 = printf, 1 = scanf */
  315. } irs[MAXIRS+1] = {     /* should be read in - for now explicit */
  316.         "printf",       0,  0,
  317.         "fprintf",      1,  0,
  318.         "sprintf",      1,  0,
  319.         "scanf",        0,  1,
  320.         "fscanf",       1,  1,
  321.         "sscanf",       1,  1,
  322. };
  323.  
  324. int nirs;
  325.  
  326. char *progname;
  327. char *filename = NULL;
  328. FILE *inp;
  329.  
  330. int eof;
  331. int peekc;
  332. int lastc;
  333. int linenr;
  334.  
  335. initgetcx()
  336. {
  337.         eof = 0;
  338.         peekc = '\n';   /* recognize # on very first line */
  339.         lastc = 0;      /* result of last getchar() */
  340.         linenr = 1;
  341. }
  342.  
  343. getcx()
  344. {
  345.         register int c;
  346.  
  347.         if(peekc) {
  348.                 c = peekc;
  349.                 peekc = 0;
  350.         } else if(eof) {
  351.                 c = EOF;
  352.         } else {
  353.                 if(lastc) {
  354.                         putchar(lastc);
  355.                         lastc = 0;
  356.                 }
  357.                 if((c = getc(inp)) == EOF)
  358.                         eof++;
  359.                 else {
  360.                         lastc = c;
  361.                         if(c == '\n')
  362.                                 linenr++;
  363.                 }
  364.         }
  365.  
  366.         return(c);
  367. }
  368.  
  369. /* Note: we do not want to eliminate comments; perhaps they contain
  370.    lint directives. */
  371. getcy()         /* as getcx(), but skip comments */
  372. {
  373.         register int c = getcx();
  374.  
  375.         if(c == '/') {
  376.                 c = getcx();
  377.                 if(c == '*') {
  378.                         while(1) {
  379.                                 c = getcx();
  380.                                 if(c == EOF)
  381.                                         error("unfinished comment");
  382.                                 while(c == '*') {
  383.                                         c = getcx();
  384.                                         if(c == '/')
  385.                                                 return(getcy());
  386.                                 }
  387.                         }
  388.                 } else {
  389.                         peekc = c;
  390.                         c = '/';
  391.                 }
  392.         }
  393.         return(c);
  394. }
  395.  
  396. getcz()         /* as getcy(), but skip preprocessor directives */
  397. {
  398.         register int c = getcy();
  399.  
  400.         while(c == '\n') {
  401.                 c = getcx();
  402.                 if(c == '#') {
  403.                         while(c != '\n') {
  404.                                 c = getcx();
  405.                                 if(c == EOF)
  406.                                         error("incomplete line");
  407.                                 while(c == '\\') {
  408.                                         (void) getcx(); c = getcx();
  409.                                 }
  410.                         }
  411.                 } else {
  412.                         peekc = c;
  413.                         return('\n');
  414.                 }
  415.         }
  416.         return(c);
  417. }
  418.  
  419. getcq()         /* as getcz() but skip strings */
  420. {
  421.         register int c = getcz();
  422.         register int delim;
  423.  
  424.         if(c == '\'' || c == '"') {
  425.                 delim = c;
  426.                 while(1) {
  427.                         c = getcx();
  428.                         if(c == '\n' || c == EOF)
  429.                                 error("Unfinished string (delim %c)", delim);
  430.                         if(c == '\\') {
  431.                                 (void) getcx();
  432.                                 continue;
  433.                         }
  434.                         if(c == delim)
  435.                                 return(getcq());
  436.                 }
  437.         }
  438.         return(c);
  439. }
  440.  
  441. usage()
  442. {
  443.         fprintf(stderr,
  444.           "Usage: %s [-n] [-e function] ... [-f datafile] ... [file.c] ...\n",
  445.           progname);
  446.         exit(2);
  447. }
  448.  
  449. extern char *optarg;
  450. extern int optind;
  451.  
  452. main(argc, argv)
  453. int argc;
  454. char **argv;
  455. {
  456.         register int c;
  457.         FILE *fp;
  458.  
  459.         if (argc > 0) {
  460.                 progname = rindex(argv[0], '/');
  461.                 if (progname != NULL)
  462.                         ++progname;
  463.                 else
  464.                         progname = argv[0];
  465.         }
  466.  
  467.         for (; irs[nirs].rname != NULL; ) ++nirs; /* Count defaults */
  468.  
  469.         while ((c = getopt(argc, argv, "e:nf:")) != EOF) {
  470.                 switch (c) {
  471.                 case '?':
  472.                         usage();
  473.                         /* NOTREACHED */
  474.                 case 'e':
  475.                         addir(optarg, 0, 0);
  476.                         break;
  477.                 case 'n':
  478.                         nirs = 0;
  479.                         irs[nirs].rname = NULL;
  480.                         break;
  481.                 case 'f':
  482.                         getirfile(optarg);
  483.                         break;
  484.                 }
  485.         }
  486.  
  487.         /* tell lint the types of the percent_* routines */
  488.  
  489.         printf("#include \"%s\"\n", PERCENT_HEADERS);
  490.  
  491.         /* now process them files.... */
  492.  
  493.         if (optind == argc)
  494.                 treat(stdin, "stdin");
  495.         else {
  496.                 for (; optind < argc; ++optind) {
  497.                         if (strcmp(argv[optind], "-") == 0)
  498.                                 treat(stdin, "stdin");
  499.                         else {
  500.                                 filename = argv[optind];
  501.                                 fp = fopen(filename, "r");
  502.                                 if (fp == NULL)
  503.                                         filerror(filename);
  504.                                 treat(fp, filename);
  505.                                 fclose(fp);
  506.                         }
  507.                 }
  508.         }
  509.  
  510.         /* now include the bodies of the routines */
  511.  
  512.         printf("#include \"%s\"\n", PERCENT_ROUTINES);
  513.  
  514.         exit(0);
  515. }
  516.  
  517. treat(fp, file)
  518. FILE *fp;
  519. char *file;
  520. {
  521.         register int c;
  522.  
  523.         filename = file;
  524.         linenr = 0;
  525.         inp = fp;
  526.         printf("# line 1 \"%s\"\n", file);
  527.  
  528.         initgetcx();
  529.  
  530.         while((c = getcq()) != EOF) {
  531.  
  532.                 /* check for (interesting) identifiers */
  533.                 if(letter(c))
  534.                         rd_id(c);
  535.         }
  536.         filename = NULL;
  537.         linenr = 0;
  538.         irs[nirs].rname = NULL;
  539. }
  540.  
  541.  
  542. rd_id(first)
  543. register int first;
  544. {
  545.         char idf[256];
  546.         register char *ip = idf;
  547.         register int c;
  548.  
  549.         *ip++ = first;
  550.         while(letdig(c = getcx()))
  551.                 if(ip-idf < sizeof(idf)-1)
  552.                         *ip++ = c;
  553.         peekc = c;
  554.         *ip = 0;
  555.         handle(idf);
  556. }
  557.  
  558. /*VARARGS1*/
  559. error(s, x)
  560. char *s;
  561. {
  562.         printf("\n"); /* Finish incomplete output line */
  563.         fprintf(stderr, "%s: Error (", progname);
  564.         if (filename != NULL) fprintf(stderr, "%s, ", filename);
  565.         fprintf(stderr, "line %d): ", linenr);
  566.         fprintf(stderr, s, x);
  567.         fprintf(stderr, "\n");
  568.         exit(1);
  569. }
  570.  
  571. /*VARARGS1*/
  572. warning(s, x1, x2)
  573. char *s;
  574. {
  575.         fprintf(stderr, "%s: Warning (", progname);
  576.         if (filename != NULL) fprintf(stderr, "%s, ", filename);
  577.         fprintf(stderr, "line %d): ", linenr);
  578.         fprintf(stderr, s, x1, x2);
  579.         fprintf(stderr, "\n");
  580. }
  581.  
  582. filerror(file)
  583. char *file;
  584. {
  585.         fprintf(stderr, "%s: can't open ", progname);
  586.         perror(file);
  587.         exit(2);
  588. }
  589.  
  590. letter(c)
  591. register int c;
  592. {
  593.         return(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_');
  594. }
  595.  
  596. digit(c)
  597. register int c;
  598. {
  599.         return('0' <= c && c <= '9');
  600. }
  601.  
  602. letdig(c)
  603. register int c;
  604. {
  605.         return(letter(c) || digit(c));
  606. }
  607.  
  608. handle(idf)
  609. register char *idf;
  610. {
  611.         register struct ir *irp = irs;
  612.  
  613.         while(irp->rname) {
  614.                 if(!strcmp(idf, irp->rname)) {
  615.                         doit(irp);
  616.                         return;
  617.                 }
  618.                 irp++;
  619.         }
  620. }
  621.  
  622. skipspaces()
  623. {
  624.         register int c;
  625.  
  626.         while(1) {
  627.                 c = getcz();
  628.                 if(c == ' ' || c == '\t' || c == '\n')
  629.                         continue;
  630.                 peekc = c;
  631.                 return;
  632.         }
  633. }
  634.  
  635. doit(irp)
  636. register struct ir *irp;
  637. {
  638.         register int c, cnt = irp->pn;
  639.  
  640.         skipspaces();
  641.         if((c = getcz()) != '(') {
  642.                 peekc = c;
  643.                 warning("%s not followed by '('", irp->rname);
  644.                 return;
  645.         }
  646.  
  647.         while(cnt--) {
  648.                 c = skiparg();
  649.                 if(c != ',') {
  650.                         peekc = c;
  651.                         warning("arg of %s not followed by comma", irp->rname);
  652.                         return;
  653.                 }
  654.         }
  655.         skipspaces();
  656.  
  657.         /* now parse format string (if present) */
  658.         /* (here we also avoid defining occurrences) */
  659.         if((c = getcx()) != '"') {
  660.                 peekc = c;
  661.                 return;
  662.         }
  663.         if (irp->type == 0)
  664.                 do_printf(irp);
  665.         else
  666.                 do_scanf(irp);
  667. }
  668.  
  669. do_printf(irp)
  670. register struct ir *irp;
  671. {
  672.         char fmt[256];
  673.         register char *fp = fmt;
  674.         register int c;
  675.  
  676.         while(1) {
  677.                 c = getcx();
  678.                 if(c == '\n' || c == EOF)
  679.                         error("Unfinished format string");
  680.                 if(c == '"')
  681.                         break;
  682.                 if(c != '%') {
  683.                         if (c == '\\')
  684.                                 (void) getcx();
  685.                         continue;
  686.                 }
  687.                 c = getcx();
  688.                 if(c == '%')
  689.                         continue;
  690.                 if(c == '-')
  691.                         c = getcx();
  692.                 if(c == '*') {
  693.                         c = getcx();
  694.                         if(fp-fmt < sizeof(fmt)-1)
  695.                                 *fp++ = '*';
  696.                 } else while(digit(c))
  697.                         c = getcx();
  698.                 if(c == '.')
  699.                         c = getcx();
  700.                 if(c == '*') {
  701.                         c = getcx();
  702.                         if(fp-fmt < sizeof(fmt)-1)
  703.                                 *fp++ = '*';
  704.                 } else while(digit(c))
  705.                         c = getcx();
  706.                 if(c == '#')
  707.                         c = getcx();
  708.                 if(c == 'l') {
  709.                         c = getcx();
  710.                         if('a' <= c && c <= 'z')
  711.                                 c -= 'a'-'A';
  712.                         else
  713.                                 error("%%l not followed by lowercase");
  714.                 }
  715.                 if(fp-fmt < sizeof(fmt)-1)
  716.                         *fp++ = c;
  717.                 else
  718.                         warning("ridiculously long format");
  719.         }
  720.         *fp = 0;
  721.         fp = fmt;
  722.         skipspaces();
  723.         while((c = getcz()) == ',') {
  724.                 if(!*fp)
  725.                         error("%s has too many arguments", irp->rname);
  726.                 skipspaces();
  727.                 if (*fp == '*') {
  728.                         printf("percent_star(");
  729.                         fp++;
  730.                 } else
  731.                         printf("percent_%c(", *fp++);
  732.                 c = skiparg();
  733.                 printf(")");
  734.                 peekc = c;
  735.         }
  736.         if(c != ')')
  737.                 error("%s has ill-formed argument list", irp->rname);
  738.         if(*fp)
  739.                 error("%s has too few arguments", irp->rname);
  740. }
  741.  
  742.  
  743. do_scanf(irp)
  744. register struct ir *irp;
  745. {
  746.         char fmt[256], shorten;
  747.         register char *fp = fmt;
  748.         register int c;
  749.         int dummy = 0;
  750.  
  751.         while(1) {
  752.                 dummy = 0;
  753.                 c = getcx();
  754.                 if(c == '\n' || c == EOF)
  755.                         error("Unfinished format string");
  756.                 if(c == '"')
  757.                         break;
  758.                 if(c != '%') {
  759.                         if (c == '\\')
  760.                                 (void) getcx();
  761.                         continue;
  762.                 }
  763.                 c = getcx();
  764.                 if(c == '%')
  765.                         continue;       /* %% */
  766.  
  767.                 if(c == '*') {
  768.                         dummy = 1;      /* no corresponding argument */
  769.                         c = getcx();
  770.                 }
  771.                 while (digit(c) || c == '.' || c == '-') {
  772.                         c = getcx();
  773.                 }
  774.                 switch(c) {
  775.                 case '[':
  776.                         c = skipbracket();      /* scan to close bracket */
  777.                         break;
  778.                 case 'l':
  779.                         c = getcx();
  780.                         if('a' <= c && c <= 'z')
  781.                                 c -= 'a'-'A';
  782.                         else
  783.                                 error("%%l not followed by lowercase");
  784.                         break;
  785.                 case 'h':
  786.                         shorten = c;
  787.                         c = getcx();
  788.                         if('a' <= c && c <= 'z')
  789.                                 c -= 'a'-'A';
  790.                         else
  791.                                 error("%%h not followed by lowercase");
  792.                         break;
  793.                 default:
  794.                         /* must be a format letter */
  795.                         break;
  796.                 }
  797.                 if (!dummy) {
  798.                         if(fp-fmt < sizeof(fmt)-2) {
  799.                                if (shorten == 'h')
  800.                                         *fp++ = 'h';
  801.                                 *fp++ = c;
  802.                         } else
  803.                                 warning("ridiculously long format");
  804.                 }
  805.         }
  806.         *fp = 0;
  807.         fp = fmt;
  808.         skipspaces();
  809.         while((c = getcz()) == ',') {
  810.                 if(!*fp)
  811.                         error("%s has too many arguments", irp->rname);
  812.                 skipspaces();
  813.                 if ( (shorten = *fp) == 'h')
  814.                         fp++;
  815.                 switch (*fp) {
  816.                     case 's':  printf("percent_s(");   break;
  817.                     case ']':  printf("percent_bkt("); break;
  818.                     default:
  819.                         if (shorten == 'h')
  820.                                 printf("percent_h%c_ptr(", *fp);
  821.                         else
  822.                                 printf("percent_%c_ptr(", *fp);
  823.                 }
  824.                 fp++;
  825.                 c = skiparg();
  826.                 printf(")");
  827.                 peekc = c;
  828.         }
  829.         if(c != ')')
  830.                 error("%s has ill-formed argument list", irp->rname);
  831.         if(*fp)
  832.                 error("%s has too few arguments", irp->rname);
  833. }
  834.  
  835. skiparg()
  836. {
  837.         register int parenct = 0;
  838.         register int c;
  839.  
  840.         parenct = 0;
  841.         while(1) {
  842.                 c = getcq();
  843.                 if(c == EOF)
  844.                         error("eof in arg list");
  845.                 if(!parenct && (c == ',' || c == ')'))
  846.                         return(c);
  847.                 if(c == '(' || c == '[') {
  848.                         parenct++;
  849.                         continue;
  850.                 }
  851.                 if(c == ')' || c == ']') {
  852.                         parenct--;
  853.                         continue;
  854.                 }
  855.         }
  856. }
  857.  
  858. skipbracket()
  859. {
  860.         register int c;
  861.  
  862.         while(1) {
  863.                 c = getcx();
  864.                 if(c == EOF)
  865.                         error("eof during %%[ ]");
  866.                 if(c == '\\') {
  867.                         c = getcx();
  868.                         continue;       /* avoid escaped char */
  869.                 }
  870.                 if(c == ']') return c;
  871.         }
  872. }
  873.  
  874. char *strsave(s)
  875. char *s;
  876. {
  877.         char *t = malloc(strlen(s) + 1);
  878.         if (t == NULL) error("out of memory");
  879.         strcpy(t, s);
  880.         return t;
  881. }
  882.  
  883. getirfile(irfile)
  884. char *irfile;
  885. {
  886.         FILE *fp = fopen(irfile, "r");
  887.         char line[256];
  888.         char name[256];
  889.         char *end;
  890.         int n;
  891.         int cnt, type;
  892.  
  893.         if (fp == NULL) filerror(irfile);
  894.         filename = irfile;
  895.         linenr = 0;
  896.         while (fgets(line, sizeof line, fp)) {
  897.                 ++linenr;
  898.                 end = index(line, '#');
  899.                 if (end) *end = '\0';
  900.                 n= sscanf(line, " %s %d %d %s", name, &cnt, &type, name+1);
  901.                 if (n == 0 || name[0] == '\0')
  902.                         continue; /* Skip empty line or comment */
  903.                 if (n != 3 || cnt < 0 || (type != 0 && type != 1) )
  904.                         error("bad format (must be %%s %%u %%u)");
  905.                 /* Should also check for valid name... */
  906.                 addir(strsave(name), cnt, type);
  907.         }
  908.         fclose(fp);
  909.         filename = NULL;
  910.         linenr = 0;
  911. }
  912.  
  913. addir(name, cnt, type)
  914. char *name;
  915. int cnt, type;
  916. {
  917.         if (nirs >= MAXIRS) error("table overflow");
  918.         irs[nirs].rname = name;
  919.         irs[nirs].pn = cnt;
  920.         irs[nirs].type = type;
  921.         ++nirs;
  922. }
  923.  
  924. /*
  925.  * get option letter from argument vector
  926.  */
  927. int     opterr = 1,             /* useless, never set or used */
  928.         optind = 1,             /* index into parent argv vector */
  929.         optopt;                 /* character checked for validity */
  930. char    *optarg;                /* argument associated with option */
  931.  
  932. #define BADCH   (int)'?'
  933. #define EMSG    ""
  934. #define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \
  935.                 fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
  936.  
  937. getopt(nargc,nargv,ostr)
  938. int     nargc;
  939. char    **nargv,
  940.         *ostr;
  941. {
  942.         static char     *place = EMSG;  /* option letter processing */
  943.         register char   *oli;           /* option letter list index */
  944.         char    *index();
  945.  
  946.         if(!*place) {                   /* update scanning pointer */
  947.                 if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
  948.                 if (*place == '-') {    /* found "--" */
  949.                         ++optind;
  950.                         return(EOF);
  951.                 }
  952.         }                               /* option letter okay? */
  953.         if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
  954.                 if(!*place) ++optind;
  955.                 tell(": illegal option -- ");
  956.         }
  957.         if (*++oli != ':') {            /* don't need argument */
  958.                 optarg = NULL;
  959.                 if (!*place) ++optind;
  960.         }
  961.         else {                          /* need an argument */
  962.                 if (*place) optarg = place;     /* no white space */
  963.                 else if (nargc <= ++optind) {   /* no arg */
  964.                         place = EMSG;
  965.                         tell(": option requires an argument -- ");
  966.                 }
  967.                 else optarg = nargv[optind];    /* white space */
  968.                 place = EMSG;
  969.                 ++optind;
  970.         }
  971.         return(optopt);                 /* dump back option letter */
  972. }
  973.  
  974. SHAR_EOF
  975. if test 13824 -ne `wc -c < printfck.c`
  976. then
  977.     echo shar: error transmitting "printfck.c" '('should be 13824 chars')'
  978. else
  979.     echo printfck.c
  980. fi
  981. fi
  982. echo shar: extracting "printfck.h" '('132 chars')'
  983. if test -f printfck.h
  984. then
  985.     echo shar: will not overwrite existing file "printfck.h"
  986. else
  987. cat << \SHAR_EOF > printfck.h
  988. #define PERCENT_HEADERS         "/usr/src/net/printfck/percent.h"
  989. #define PERCENT_ROUTINES        "/usr/src/net/printfck/percent.c"
  990. SHAR_EOF
  991. if test 132 -ne `wc -c < printfck.h`
  992. then
  993.     echo shar: error transmitting "printfck.h" '('should be 132 chars')'
  994. else
  995.     echo printfck.h
  996. fi
  997. fi
  998. echo shar: extracting "percent.c" '('2094 chars')'
  999. if test -f percent.c
  1000. then
  1001.     echo shar: will not overwrite existing file "percent.c"
  1002. else
  1003. cat << \SHAR_EOF > percent.c
  1004. /*LINTLIBRARY*/
  1005. int      percent_d(x) int      x; { return x; }
  1006. int      percent_o(x) int      x; { return x; }
  1007. int      percent_x(x) int      x; { return x; }
  1008. long     percent_D(x) long     x; { return x; }
  1009. long     percent_O(x) long     x; { return x; }
  1010. long     percent_X(x) long     x; { return x; }
  1011. double   percent_e(x) double   x; { return x; }
  1012. double   percent_f(x) double   x; { return x; }
  1013. double   percent_g(x) double   x; { return x; }
  1014. double   percent_E(x) double   x; { return x; }
  1015. double   percent_F(x) double   x; { return x; }
  1016. double   percent_G(x) double   x; { return x; }
  1017. unsigned percent_u(x) unsigned x; { return x; }
  1018. int      percent_c(x) int      x; { return x; }
  1019. char *   percent_s(x) char *   x; { return x; }
  1020.  
  1021. int      percent_star(x) int   x; { return x; }
  1022.  
  1023. int *      percent_d_ptr(x)  int *      x; { return x; }
  1024. int *      percent_o_ptr(x)  int *      x; { return x; }
  1025. int *      percent_x_ptr(x)  int *      x; { return x; }
  1026. short *    percent_hd_ptr(x) short *    x; { return x; }
  1027. short *    percent_ho_ptr(x) short *    x; { return x; }
  1028. short *    percent_hx_ptr(x) short *    x; { return x; }
  1029. long *     percent_D_ptr(x)  long *     x; { return x; }
  1030. long *     percent_O_ptr(x)  long *     x; { return x; }
  1031. long *     percent_X_ptr(x)  long *     x; { return x; }
  1032. float *    percent_e_ptr(x)  float *    x; { return x; }
  1033. float *    percent_f_ptr(x)  float *    x; { return x; }
  1034. float *    percent_g_ptr(x)  float *    x; { return x; }
  1035. double *   percent_E_ptr(x)  double *   x; { return x; }
  1036. double *   percent_F_ptr(x)  double *   x; { return x; }
  1037. double *   percent_G_ptr(x)  double *   x; { return x; }
  1038. unsigned * percent_u_ptr(x)  unsigned * x; { return x; }
  1039.  
  1040. int *      percent_c_ptr(x)  int *      x; { return x; }
  1041. char *     percent_bkt(x)    char *     x; { return x; }
  1042.  
  1043. /* NOTE: not all C compilers support unsigned long! - If your compiler rejects
  1044.  * the following lines, replace "unsigned long" with just "long"
  1045.  */
  1046. unsigned long   percent_U(x)     unsigned long   x; { return x; }
  1047. unsigned long * percent_U_ptr(x) unsigned long * x; { return x; }
  1048. SHAR_EOF
  1049. if test 2094 -ne `wc -c < percent.c`
  1050. then
  1051.     echo shar: error transmitting "percent.c" '('should be 2094 chars')'
  1052. else
  1053.     echo percent.c
  1054. fi
  1055. fi
  1056. echo shar: extracting "Makefile" '('300 chars')'
  1057. if test -f Makefile
  1058. then
  1059.     echo shar: will not overwrite existing file "Makefile"
  1060. else
  1061. cat << \SHAR_EOF > Makefile
  1062. # Makefile for printfck
  1063. #
  1064. # Change printfck.h so that PERCENT_HEADERS and PERCENT_ROUTINES
  1065. # have the correct directory paths.
  1066.  
  1067.  
  1068. all: printfck percent.h
  1069.  
  1070.  
  1071. printfck: printfck.o printfck.h
  1072.         cc -o printfck printfck.o
  1073.  
  1074. percent.h: percent.c
  1075.         sed -e "s/LINTLIBRARY//" -e "s/(x.*/();/" percent.c >percent.h
  1076. SHAR_EOF
  1077. if test 300 -ne `wc -c < Makefile`
  1078. then
  1079.     echo shar: error transmitting "Makefile" '('should be 300 chars')'
  1080. else
  1081.     echo Makefile
  1082. fi
  1083. fi
  1084. #         End of shar archive
  1085. exit 0
  1086.