home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 208_01 / cb.c < prev    next >
Text File  |  1987-10-13  |  10KB  |  468 lines

  1. /*
  2. NAME
  3.     cb - C program beautifier
  4. SYNOPSIS
  5.     cb
  6. DESCRIPTION
  7.     `cb' places a copy of the C program from the standard input
  8.     on the standard output with spacing and indentation that displays
  9.     the structure of the program.
  10. BUG
  11.     almost ignorant of the C syntax.
  12. AUTHOR
  13.     William C. Colley, III swiped from the CIPG's UNIX system
  14.     and modified to run under BDS C around July 1980.
  15.     restored to Portable C by Yoshimasa Tsuji on 18 June 1987
  16. DIAGNOSTICS
  17.     there used to be none.
  18.     the pairing of braces, parentheses, quotes and comments is checked.
  19.     when a closing curly brace is found at column 1, the pairing
  20.       balance must be zero. otherwise a warning message is printed on
  21.       the standard error file.
  22.     nested comments will invoke an error message.
  23.     preprocessor directive('#') must be at column 1.
  24.     funny characters are detected.
  25.     warning messages will not be issued too often because "include" files
  26.       or preprocessor conditionals can contain unpaired parentheses etc.
  27. BUG FIX
  28.     EOF within comments used to be caught in an endless loop.
  29.     preprocessor statements were not allowed to continue to the next line.
  30.     many other bugs of the old version have been killed.
  31. RESTRICTION
  32.     trouble may be caused if the input file ends with a '\\','\n',EOF
  33.     sequence.
  34.     not fit for non-C format (e.g. "#define begin {" ).
  35. */
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39.  
  40. typedef int bool;
  41.  
  42. #define MAXCURL 20        /* {} may nest this deep */
  43. #define MAXLEVL (MAXCURL/2)    /* `if' etc may nest this deep */
  44. #define MAXLEN 200    /*line buffer length*/
  45. #define MAXWARN 4    /*not more than MAXWARN will be printed*/
  46.  
  47. int clevel;    /* curly brace count */
  48. int spflg[MAXCURL][MAXLEVL];
  49. int sind[MAXCURL][MAXLEVL];
  50. int iflev;        /* `if' nesting depth */
  51. int ifflg = -1;
  52. int level;
  53. int ind[MAXLEVL];
  54. int paren;    /* parenthesis count */
  55. int pflg[MAXLEVL];
  56. int pchar;
  57. bool aflg;    /*flag to print a few more spaces after tabbing*/
  58. int stabs[MAXCURL][MAXLEVL];
  59.  
  60. char string[MAXLEN];
  61. char *cptr= string;
  62. bool sflg = 1;    /* print the beginning part of a line */
  63. int peek;    /* I like 0 better than EOF */
  64. int tabs;    /* indent count    */
  65. int lineno = 1;
  66. int column;
  67. int errcount;
  68.  
  69.  
  70. main() {
  71. static int slevel[MAXLEVL] = { 0 };
  72. static int siflev[MAXLEVL] = { 0 };
  73. static int sifflg[MAXLEVL] = { 0 };
  74. int lchar;
  75. register bool qflg = 0;
  76. static char *wif[] = { "if",0 };
  77. static char *welse[] = { "else",0 };
  78. static char *wfor[] = { "for", 0 };
  79. static char *wds[] = { "case","default",0 };
  80. register int c, cc;
  81.  
  82. static char unmatched[] = "%d: unmatched () before %s\n";
  83.  
  84.     while((c = getchr()) != EOF){
  85.         switch(c){
  86.         case '\\':
  87.         case '@':
  88.         case '$':
  89.             fprintf(stderr,"%d: illegal char %c\n",lineno, c);
  90.         default:
  91.             *cptr++ = c;
  92.             if(c != ',')lchar = c;
  93.             continue;
  94.         case ' ':
  95.         case '\t':
  96.             if(lookup(welse) == 1){
  97.                 gotelse();
  98.                 if(sflg == 0 || cptr > string) *cptr++ = c;
  99.                 putst();
  100.                 sflg = 0;
  101.                 continue;
  102.             }
  103.             if(sflg == 0 || cptr > string) *cptr++ = c;
  104.             continue;
  105.         case '\n':
  106.         {
  107.             register bool eflg;
  108.             if((eflg = lookup(welse)) == 1)
  109.                 gotelse();
  110.             putst();
  111.             putchar('\n');
  112.             sflg++;
  113.             if(eflg == 1){
  114.                 pflg[level]++;
  115.                 tabs++;
  116.             }
  117.             else if(pchar == lchar)
  118.                 aflg++;
  119.             continue;
  120.             }
  121.         case '{':
  122.             if(paren!= 0 && ++errcount <= MAXWARN)
  123.                 fprintf(stderr,unmatched,lineno,"{"),
  124.                 paren=0;
  125.             if(lookup(welse) == 1)gotelse();
  126.             siflev[clevel] = iflev;
  127.             sifflg[clevel] = ifflg;
  128.             iflev = ifflg = 0;
  129.             if( ++clevel > MAXCURL)
  130.                 fprintf(stderr,"%d: {} nested too deeply\n",lineno),
  131.                 exit(1);
  132.             if(sflg != 0 && pflg[level] != 0){
  133.                 pflg[level]--;
  134.                 tabs--;
  135.             }
  136.             *cptr++ = c;
  137.             putst();
  138.             getnl();
  139.             putst();
  140.             putchar('\n');
  141.             tabs++;
  142.             sflg++;
  143.             if(pflg[level] > 0){
  144.                 ind[level] = 1;
  145.                 if(++level > MAXLEVL)
  146.                     fprintf(stderr,"%d: nesting too deep\n",lineno),
  147.                     exit(1);
  148.                 slevel[level] = clevel;
  149.             }
  150.             continue;
  151.         case '}':
  152.             if(paren != 0 && ++errcount <= MAXWARN)
  153.                 fprintf(stderr,unmatched,lineno,"}"),
  154.                 paren=0;/*recover*/
  155.             if(--clevel < 0 && ++errcount <= MAXWARN)
  156.                 fprintf(stderr,"%d: too many }\n",lineno);
  157.             if(column == 1 && clevel != 0 && ++errcount <= MAXWARN)
  158.                 fprintf(stderr,"%d: unmatched {}\n",lineno);
  159.             if((iflev = siflev[clevel]-1) < 0)iflev = 0;
  160.             ifflg = sifflg[clevel];
  161.             putst();
  162.             ptabs(--tabs);
  163.             putchar(c);
  164.             if((peek = getchr()) == ';'){
  165.                 putchar(';');
  166.                 peek = 0;
  167.             }
  168.             getnl();
  169.             putst();
  170.             putchar('\n');
  171.             sflg++;
  172.  
  173.             if(clevel < slevel[level] && level > 0)
  174.                 level--;
  175.             if(ind[level] != 0){
  176.                 tabs -= pflg[level];
  177.                 ind[level] = pflg[level] = 0;
  178.             }
  179.             continue;
  180.         case '"':
  181.         case '\'':
  182.             *cptr++ = c;
  183.             while((cc = getchr()) != c){
  184.                 *cptr++ = cc;
  185.                 if(cc == '\\')
  186.                     *cptr++ = getchr();
  187.                 if(cc == '\n'){
  188.                     fprintf(stderr,
  189.                     "%d: %c crossed line\n",lineno,c);
  190.                     putst();
  191.                     sflg++;break;/*bug tolerant*/
  192.                 }
  193.             }
  194.             *cptr++ = cc;
  195.             if(getnl() == 1){
  196.                 lchar = cc;
  197.                 peek = '\n';
  198.             }
  199.             continue;
  200.         case ';':
  201.             *cptr++ = c;
  202.             putst();
  203.             if(pflg[level] > 0 && ind[level] == 0){
  204.                 tabs -= pflg[level];
  205.                 pflg[level] = 0;
  206.             }
  207.             getnl();
  208.             putst();
  209.             putchar('\n');
  210.             sflg++;
  211.             if(iflev > 0)
  212.                 if(ifflg == 1){
  213.                     iflev--;
  214.                     ifflg = 0;
  215.                 }
  216.                 else iflev = 0;
  217.             continue;
  218.         case '?':
  219.             qflg = 1;
  220.             *cptr++ = c;
  221.             continue;
  222.         case ':':
  223.             *cptr++ = c;
  224.             /* "A? B:C */
  225.             if(qflg == 1){
  226.                 qflg = 0;
  227.                 continue;
  228.             }
  229.             /* bit field or label*/
  230.             if(paren != 0 && ++errcount <= MAXWARN)
  231.                 fprintf(stderr,unmatched,lineno,":"),
  232.                 paren = 0;
  233.             if(lookup(wds) == 0){
  234.                 sflg = 0;
  235.                 putst();
  236.             }
  237.             else{
  238.                 tabs--;
  239.                 putst();
  240.                 tabs++;
  241.             }
  242.             if((peek = getchr()) == ';'){
  243.                 putchar(';');
  244.                 peek = 0;
  245.             }
  246.             getnl();
  247.             putst();
  248.             putchar('\n');
  249.             sflg++;
  250.             continue;
  251.         case '/':
  252.             if(pchar == '*' && ++errcount <= MAXWARN)
  253.                 fprintf(stderr,"%d: nested comments\n",
  254.                     lineno);
  255.             *cptr++ = c;
  256.             if((peek = getchr()) != '*')continue;
  257.             *cptr++ = peek;
  258.             peek = 0;
  259.             comment();
  260.             continue;
  261.         case ')':
  262.             paren--;
  263.             *cptr++ = c;
  264.             putst();
  265.             if(getnl() == 1){
  266.                 peek = '\n';
  267.                 if(paren != 0)aflg++;
  268.                 else if(tabs > 0){
  269.                     pflg[level]++;
  270.                     tabs++;
  271.                     ind[level] = 0;
  272.                 }
  273.             }
  274.             continue;
  275.         case '#':
  276.             if(column != 1)
  277.                 fprintf(stderr,"%d: '#' at illegal column\n",lineno);
  278.             *cptr++ = c;
  279.             while((c = getchr()) != '\n') {
  280.                 /* the last character before EOF
  281.                  * is always a newline and it is not preceded
  282.                  * by an escape character.
  283.                  */
  284.                 *cptr++ = c;
  285.                 if(c == '\\')
  286.                     *cptr++ = getchr();
  287.             }
  288.             *cptr++ = c;
  289.             sflg = 0;
  290.             putst();
  291.             sflg++;
  292.             continue;
  293.         case '(':
  294.             *cptr++ = c;
  295.             paren++;
  296.             if(lookup(wfor) == 1){
  297.                 register int ct;
  298.                 while((c = getst()) != ';');
  299.                 for(ct=0;;) {
  300.                     if ((c = getst()) == ')'){
  301.                         if(ct==0)break;
  302.                         ct--;
  303.                         continue;
  304.                     }
  305.                     if(c == '(') ct++;
  306.                 }
  307.                 paren--;
  308.                 putst();
  309.                 if(getnl() == 1){
  310.                     peek = '\n';
  311.                     pflg[level]++;
  312.                     tabs++;
  313.                     ind[level] = 0;
  314.                 }
  315.                 continue;
  316.             }
  317.             if(lookup(wif) == 1){
  318.                 putst();
  319.                 stabs[clevel][iflev] = tabs;
  320.                 spflg[clevel][iflev] = pflg[level];
  321.                 sind[clevel][iflev] = ind[level];
  322.                 iflev++;
  323.                 ifflg = 1;
  324.             }
  325.         }
  326.     }
  327.     if(clevel != 0 && ++errcount <= MAXWARN)
  328.         fprintf(stderr,"unmatched {} at end\n");
  329.     exit(errcount ==0 ? 0 :1);
  330. }
  331.  
  332. ptabs(tabbs)
  333. register int tabbs;
  334. {
  335.     while(--tabbs >= 0)putchar('\t');
  336. }
  337.  
  338. getchr() {
  339. static int lastchar = 0;
  340. register int c;
  341.     if (peek == 0) {
  342.         if(lastchar != ' ' && lastchar != '\t')
  343.             pchar = lastchar;
  344.         if((c = getchar()) == '\n')lineno++,column = 0;
  345.         else if(c != '\t' && (c < 040 || c >= 0177) && c != EOF)
  346.             fprintf(stderr,"%d: funny character '\\%03o'\n",lineno, c);
  347.         else column++;
  348.     } else {
  349.         c = peek; peek = 0;
  350.     }
  351.     return(lastchar = c);
  352. }
  353.  
  354. putst() {
  355.     if(cptr > string){
  356.         if(sflg != 0){
  357.             ptabs(tabs);
  358.             sflg = 0;
  359.             if(aflg != 0){
  360.                 aflg = 0;
  361.                 if(tabs > 0)printf("    ");
  362.             }
  363.         }
  364.         *cptr = '\0';
  365.         if(cptr > string + MAXLEN)
  366.             fprintf(stderr,"%d: line too long\n",lineno),
  367.             exit(1);
  368.         printf("%s",string);
  369.         cptr = string;
  370.     }
  371.     else if(sflg != 0)
  372.             aflg= sflg = 0;
  373. }
  374.  
  375. bool
  376. lookup(tab)
  377. register char **tab;
  378. {
  379.     register char r;
  380.     register char *strptr, *sptr, *tptr;
  381.  
  382.     if(cptr <= string)return(0);
  383.     sptr = string -1;
  384.     while(*++sptr == ' ' || *sptr == '\t');
  385.     while(tptr = *tab++) {
  386.         strptr=sptr -1; /*dont optimise*/
  387.         while((r = *tptr++) == *++strptr && r != '\0');
  388.         if(r == '\0' && !isalnum(*strptr) &&  *strptr != '_')
  389.             return(1);
  390.     }
  391.     return(0);
  392. }
  393. /*getst returns the first normal character after a parenthesis */
  394. getst(){
  395.     register int ch, cc;
  396.  
  397.     for(;;) {
  398.         *cptr++ = ch = getchr();
  399.         if(ch == '\'' || ch == '"'){
  400.             while((*cptr++ = cc= getchr()) != ch)
  401.                 if(cc == '\\') {
  402.                     *cptr++ = getchr();
  403.                     putst(); /*trim*/
  404.                 }
  405.                 else if(cc== '\n') {
  406.                     fprintf(stderr,
  407.                     "%d: %c crossed line\n",lineno,ch);
  408.                     break; /*bug tolerant*/
  409.                 }
  410.             continue;
  411.         }
  412.         if(ch == '\n') {
  413.             putst();
  414.             aflg++;
  415.             continue;
  416.         }
  417.         return(ch);
  418.     }
  419. }
  420.  
  421. gotelse()
  422. {
  423.     tabs = stabs[clevel][iflev];
  424.     pflg[level] = spflg[clevel][iflev];
  425.     ind[level] = sind[clevel][iflev];
  426.     ifflg = 1;
  427. }
  428. getnl()
  429. {
  430.     while((peek = getchr()) == '\t' || peek == ' '){
  431.         *cptr++ = peek;
  432.         peek = 0;
  433.     }
  434.     if((peek = getchr()) == '/'){
  435.         peek = 0;
  436.         *cptr++ = '/';
  437.         if((peek = getchr()) == '*'){
  438.             *cptr++ = '*';
  439.             peek = 0;
  440.             comment();
  441.         }
  442.     }
  443.     if((peek = getchr()) == '\n'){
  444.         peek = 0;
  445.         return(1);
  446.     }
  447.     return(0);
  448. }
  449. comment()
  450. {
  451. register int c;
  452. register int line;
  453.     line= lineno;
  454.     for(;;) {
  455.         if((*cptr++ = c = getchr()) != '*') {
  456.             if(c == '\n') putst(), sflg++;
  457.             else if(c== EOF) {
  458.                 fprintf(stderr,"%d: unmatched comment\n",line);
  459.                 exit(1);
  460.             }
  461.             continue;
  462.         }
  463.         while ((*cptr++ = c = getchr()) == '*')
  464.             ;
  465.         if(c == '/')break;
  466.     }
  467. }
  468.