home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / MISC / ccheck.lzh / ccheck.c < prev    next >
C/C++ Source or Header  |  1991-09-19  |  18KB  |  884 lines

  1. /*
  2.  * CCHECK.C -- Merged UNIX/BDS C Version of CCHK, 1/9/83
  3.  *
  4.  *
  5.  *    Copyright:    The Regents of the University of California
  6.  *            [Note - since Steve Draper distributed cchk.c over
  7.  *             Usenet, I assume it's effectively in the public
  8.  *             domain. JCM]
  9.  *            
  10.  *    Title:        ccheck
  11.  *
  12.  *    Purpose:    To find and report all badly matched openers and
  13.  *            closers plus assignment/equality confusions
  14.  *            in a c source file.
  15.  *
  16.  *    Author:        Steve Draper, expanding cnest by Tom Anderson
  17.  *
  18.  *    Usage:        ccheck [-q] [-v] <filename1> <filename2> ...
  19.  *
  20.  *    History (in reverse order to minimize reading time for updates):
  21.  *
  22.  *            December 15, 1990    Transferred from MS-DOS to OS9/68K and
  23.  *                                added option conversion to lower case.
  24.  *                                -- Martin Gregorie
  25.  *
  26.  *                To compile with Microware C specify
  27.  *                -i and -dUNIX options to cc
  28.  *                -? option added to meet OS9 utility standards
  29.  *
  30.  *            January 9, 1983        Single BDS/UNIX source created
  31.  *                        as CCHECK.C  -- Jeff Martin
  32.  *
  33.  *                To compile for BDS C, do as in 12/29 note.
  34.  *                 [Because of an apparent bug in the BDS
  35.  *                 preprocessor, you'll probably have to comment
  36.  *                 out the #ifdefs and the UNIX #includes at
  37.  *                 the top of the program, or else create empty
  38.  *                "stdio.h" and "ctype.h" files: i.e.,
  39.  *                    SAVE 0 STDIO.H and
  40.  *                    SAVE 0 CTYPE.H
  41.  *                on the disk that has the CCHECK.C source.
  42.  *                Thanks to Herb Schulz for this work-around.]
  43.  *
  44.  *                To compile for UNIX, specify -DUNIX to cc.
  45.  *
  46.  *            December 29, 1982    Adapted for BDS C --
  47.  *                            Jeff Martin at home.
  48.  *                The BDS version should be compiled with the
  49.  *                -e and -o options, for speed, and linked with
  50.  *                the DIO package, to enable redirection of
  51.  *                standard input and standard output; e.g.:
  52.  *
  53.  *                    CC1 CCHECK.C -E 4000 -O
  54.  *                    L2 CCHECK DIO -or- CLINK CCHECK -F DIO
  55.  *
  56.  *                [For this program to work properly with input
  57.  *                redirected, you'll need to link it with a
  58.  *                version of DIO that includes the ungetch()
  59.  *                function.  See "UDIO.CQ" on the RCPM's.]
  60.  *
  61.  *            December 20, 1982    Converted to cchk --
  62.  *                            Steve Draper at UCSD
  63.  *
  64.  *            December 9, 1982    Jeffrey Mogul at Stanford
  65.  *                - checks for unclosed comment at end of file.
  66.  *
  67.  *            December 3, 1982    creation date --
  68.  *                        Tom Anderson at microsof!fluke
  69.  */
  70.  
  71. #ifdef UNIX
  72. #include <stdio.h>
  73. #include <ctype.h>
  74. #else
  75. #include <a:bdscio.h>
  76. #include <a:dio.h>
  77. #endif
  78.  
  79. #define TRUE 1
  80. #define FALSE 0
  81. #define SPACE 32
  82.  
  83. #define BRACE    1
  84. #define SQBRAK     2
  85. #define PAREN    3
  86. #define IF    4
  87. #define IFCOND    5
  88. #define WHLCOND    6
  89. #define THEN    7
  90. #define ELSE    8
  91.  
  92. #define STACKSIZ 40
  93.  
  94. struct brak 
  95. {
  96.     int type, b_indent, b_ln;
  97. }
  98. stack[STACKSIZ];
  99.  
  100. #define rmbrak(N) (top -= N, stackc -= N)
  101. #define myungetc(C)  ungetc(((C) == '\n' ? SPACE : (C)), infile)
  102.  
  103. #ifndef UNIX
  104. /*
  105.  * Kludge defines for BDS C
  106.  */
  107. #define void int
  108. #define stdin 0
  109. #define stdout 1
  110. int eofdet;
  111. #endif
  112.  
  113. #define VFLAG "-v"
  114. #define QFLAG "-q"
  115. #define SFLAG "-s"
  116. #define HFLAG "-?"
  117.  
  118. int firsttime; /* This was a static in mygetchar() */
  119.  
  120. int mygetchar(), pr();
  121. void checkelse(), newbrak(), checkcloser(), prtype();
  122.  
  123. #ifndef UNIX
  124. FILE fbuff;
  125. #endif
  126.  
  127. FILE *infile;
  128. int ln, indent, commindent, stackc, commln;
  129. int singlequoterr, oddsinglequote, bracecnt, parencnt, sqbrakcnt;
  130. int errstatus, wstatus;
  131. int errnmb, wnmb;
  132. int verbose;
  133. char *filename;
  134. struct brak *top;
  135.  
  136. main(argc,argv)
  137. unsigned argc ;
  138. char *argv[] ;
  139. {
  140.     register int c ;
  141.     int i;
  142.     int doubleqflag;
  143.     unsigned file_index;
  144.  
  145. #ifndef UNIX
  146.     dioinit(&argc, argv); /* Initialize redirection for BDS C */
  147.     argv[0] = "ccheck"; /* Sigh... */
  148.     infile = fbuff;
  149. #endif
  150.     wnmb = 0;
  151.     verbose = 0;
  152.     file_index = 1;
  153.  
  154.     while (argc > 1  &&  argv[file_index][0] == '-') 
  155.     {
  156.         argv[file_index][1] = tolower(argv[file_index][1]);
  157.  
  158.         if (strcmp(argv[file_index], VFLAG) == 0)
  159.             verbose++;
  160.         if (strcmp(argv[file_index], QFLAG) == 0)
  161.             wnmb = -2;
  162.         if (strcmp(argv[file_index], SFLAG) == 0)
  163.             wnmb = -2;
  164.         if (strcmp(argv[file_index], HFLAG) == 0) 
  165.         {
  166.             printf("\nSyntax  : ccheck [<opts>] <file>...\n");
  167.             printf("Function: To find and report all badly matched openers\n");
  168.             printf("          and closers plus assignment/equality\n");
  169.             printf("          confusions in a C source file.\n");
  170.             printf("Options :\n");
  171.             printf("     -q           quiet mode (suppress warnings)\n");
  172.             printf("     -s           synomym of quiet mode\n");
  173.             printf("     -v           verbose mode\n");
  174.             exit(0);
  175.         }
  176.         file_index++;
  177.         argc--;
  178.     }
  179.  
  180.     do {
  181.     /* INIT for each file */
  182.     firsttime = 1;
  183.     doubleqflag = 0;
  184.     errstatus = wstatus = 0;
  185.     ln = 1;
  186.     indent = 0;
  187.     commindent = 0;
  188.     singlequoterr = oddsinglequote = parencnt = sqbrakcnt = bracecnt = 0;
  189.     errnmb = 0;
  190.     if (wnmb > -2)
  191.         wnmb = 0;
  192.     newbrak(0);
  193. #ifndef UNIX
  194.     eofdet = 0;
  195. #endif
  196.  
  197.     if (argc == 1) {
  198.         infile = stdin;
  199.         filename = NULL;
  200.     }
  201.     else {
  202. #ifdef UNIX
  203.         if ((infile = fopen(argv[file_index],"r")) == (FILE *) NULL)
  204. #else
  205.         if (fopen(argv[file_index], infile) == ERROR)
  206. #endif
  207.         {
  208.         fprintf (stdout,"%s: Can't access %s\n",argv[0], argv[file_index]);
  209.         continue;
  210.         }
  211.         filename = argv[file_index];
  212.     }
  213.  
  214.     while ( ( c = mygetchar()) !=  EOF ) {
  215.         if (verbose == 2) {
  216.         for (i = stackc; i>0; i--) {
  217.             printf("%c %d: type ", c, i);
  218.             prtype(stack[i].type);
  219.             printf(", indent %d, line %d.\n", stack[i].b_indent, stack[i].b_ln);
  220.         }
  221.         }
  222.  
  223.         switch (c) {
  224.         case ';':
  225.         ungetc(SPACE, infile);
  226.         while (top->type == ELSE)
  227.             rmbrak(1);
  228.         if (top->type == THEN) {
  229.             rmbrak(1);
  230.             checkelse();
  231.         }
  232.         break;
  233.  
  234.         case '!':
  235.         case '>':
  236.         case '<':
  237.         /* swallow legit. '=' chars */
  238.         c = mygetchar();
  239.         if (c != '=')
  240.             myungetc(c);
  241.         break;
  242.  
  243.         case '=':
  244.         if ((top-1)->type == IFCOND  ||  (top-1)->type == WHLCOND) {
  245.             c = mygetchar();
  246.             if (c != '=') {
  247.             myungetc(c);
  248.             if (pr(1))
  249.                 printf("Assignment instead of equals in conditional, line %d.\n", ln);
  250.             }
  251.         }
  252.         break;
  253.  
  254.         case '\n':
  255.         case SPACE:
  256.         c = mygetchar();
  257.         switch (c) {
  258.         case 'i':
  259.             /* if */
  260.             c = mygetchar();
  261.             if (c == 'f'
  262.             && !isalpha(c = fgetc(infile)) && !isdigit(c)) {
  263.             ungetc(c, infile);
  264.             newbrak(IF);
  265.             while ((c = mygetchar()) == SPACE ||  c == '\n')
  266.                 ;
  267.             if (c != '(') {
  268.                 if (pr(1))
  269.                 printf("Bad if (no condition) line %d.\n", ln);
  270.                 rmbrak(1);
  271.             }
  272.             else
  273.                 newbrak(IFCOND);
  274.             myungetc(c);
  275.             }
  276.             else
  277.             myungetc(c);
  278.             break;
  279.         case 'w':
  280.             /* while */
  281.             if ((c = mygetchar()) == 'h'
  282.             &&  (c = mygetchar()) == 'i'
  283.             &&  (c = mygetchar()) == 'l'
  284.             &&  (c = mygetchar()) == 'e'
  285.             &&  !isalpha(c = fgetc(infile))  &&  !isdigit(c)) {
  286.             ungetc(c, infile);
  287.             while ((c = mygetchar()) == SPACE ||  c == '\n')
  288.                 ;
  289.             if (c != '(') {
  290.                 if (pr(1))
  291.                 printf("Bad while (no condition) line %d.\n", ln);
  292.             }
  293.             else
  294.                 newbrak(WHLCOND);
  295.             myungetc(c);
  296.             }
  297.             else
  298.             myungetc(c);
  299.             break;
  300.         case 'e':
  301.             /* else */
  302.             myungetc(c);
  303.             checkelse();
  304.             break;
  305.  
  306.         default:
  307.             myungetc(c);
  308.             break;
  309.         }
  310.         break;
  311.  
  312.         case '*':
  313.         /* close comment ? */
  314.         c = mygetchar();
  315.         if (c != '/') {
  316.             myungetc(c);
  317.             break;
  318.         }
  319.  
  320.         if (pr(1))
  321.             printf
  322.             ("Line %d: Comment close without open, indent %d\n",
  323.             ln, indent);
  324.  
  325.         break;
  326.  
  327.  
  328.         case '\'':
  329.         if ((c = fgetc(infile)) != '\\') {
  330.             if (c == '\''  ||  (c = fgetc(infile)) != '\'') {
  331.             if (pr(1))
  332.                 printf("Bad character constant line %d\n", ln);
  333.             singlequoterr = 1;
  334.             }
  335.         }
  336.         else if (!isdigit(c = fgetc(infile))) {
  337.             if ((c = fgetc(infile)) != '\'') {
  338.             if (pr(1))
  339.                 printf("Bad character constant with \\ line %d\n", ln);
  340.             }
  341.         }
  342.         else {
  343.             if (isdigit(c = fgetc(infile)))
  344.             if (isdigit(c = fgetc(infile)))
  345.                 c = fgetc(infile);
  346.             if (c != '\'')
  347.             if (pr(1))
  348.                 printf("Bad character constant with \\0 line %d\n", ln);
  349.         }
  350.  
  351.         if (c != '\'') {
  352.             ungetc(c, infile);
  353.             oddsinglequote = !oddsinglequote;
  354.             singlequoterr = 1;
  355.         }
  356.         break;
  357.  
  358.         case '"':
  359.         do {
  360.             c = fgetc(infile);
  361.             if (c == EOF) {
  362.             if (pr(2))
  363.                 printf("Error: '\"' quoted string not ended by end of file.\n");
  364.             break;
  365.             }
  366.             else if (c == '\n') {
  367.             if (doubleqflag == 0)
  368.                 if (pr(0))
  369.                 printf("Warning: '\"' quoted string not ended by end of line %d.\n", ln);
  370.             doubleqflag = 1;
  371.             ln++;
  372.             }
  373.             else if (c == '\\') {
  374.             c = SPACE;
  375.             fgetc(infile);
  376.             }
  377.         } while (c != '"');
  378.         doubleqflag = 0;
  379.         break;
  380.      
  381.         case '{':
  382.         if (stackc  &&  indent < top->b_indent)
  383.             if (pr(0))
  384.             printf("Indent jumps backwards line %d.\n", ln);
  385.         newbrak(BRACE);
  386.         break;
  387.  
  388.         case '}':
  389.         checkcloser(BRACE);
  390.         while (top->type == ELSE)
  391.             rmbrak(1);
  392.         if (top->type == THEN) {
  393.             rmbrak(1);
  394.             checkelse();
  395.         }
  396.         break;
  397.  
  398.         case '(':
  399.         if (stackc  &&  indent < top->b_indent)
  400.             if (pr(0))
  401.             printf("Indent jumps backwards line %d.\n", ln);
  402.         newbrak(PAREN);
  403.         break;
  404.  
  405.         case ')':
  406.         checkcloser(PAREN);
  407.         if (top->type == IFCOND) {
  408.             rmbrak(1);
  409.             newbrak(THEN);
  410.         }
  411.         else if (top->type == WHLCOND)
  412.             rmbrak(1);
  413.         break;
  414.  
  415.         case '[':
  416.         if (stackc  &&   indent < top->b_indent)
  417.             if (pr(0))
  418.             printf("Indent jumps backwards line %d.\n", ln);
  419.         newbrak(SQBRAK);
  420.         break;
  421.  
  422.         case ']':
  423.         checkcloser(SQBRAK);
  424.         break;
  425.  
  426.         default:
  427.         break;
  428.  
  429.         }
  430.     }
  431.  
  432.     fclose(infile);
  433.  
  434.     while (stackc > 0) {
  435.         pr(2);
  436.         fputs("Unclosed brak at EOF: ", stdout);
  437.         prtype(top->type);
  438.         printf(" opened on line %d.\n", top->b_ln);
  439.         switch (top->type) {
  440.         case BRACE:
  441.             bracecnt++;
  442.             break;
  443.         case SQBRAK:
  444.             sqbrakcnt++;
  445.             break;
  446.         case PAREN:
  447.             parencnt++;
  448.             break;
  449.         default:
  450.             break;
  451.         }
  452.         rmbrak(1);
  453.     }
  454.  
  455.     if ((i = (oddsinglequote || bracecnt || sqbrakcnt || parencnt)) || errstatus) {
  456.         pr(2);
  457.         printf("Summary:\n");
  458.     }
  459.     else {
  460.         if (filename != NULL) {
  461.         fputs(filename, stdout);
  462.         fputs(": ", stdout);
  463.         }
  464.         printf(" OK\n");
  465.     }
  466.     if (oddsinglequote) 
  467.         printf("\tOdd number of single quotes.\n");
  468.     if (bracecnt)
  469.         printf("\t%d too few %s braces.\n", abs(bracecnt), (bracecnt>0 ? "closing" : "opening"));
  470.     if (sqbrakcnt)
  471.         printf("\t%d too few %s square brackets.\n", abs(sqbrakcnt), (sqbrakcnt>0 ? "closing" : "opening"));
  472.     if (parencnt)
  473.         printf("\t%d too few %s parentheses.\n", abs(parencnt), (parencnt>0 ? "closing" : "opening"));
  474.     if (errstatus && !i)
  475.         printf("\tPossible error(s), but no net delimiter imbalance.\n");
  476.     putchar('\n');
  477.     } while (++file_index < argc);
  478.  
  479. #ifndef UNIX
  480.     dioflush(); /* Clean up BDS C redirection */
  481. #endif
  482.     exit(errstatus ? 2 : wstatus);
  483. }
  484.  
  485. /*
  486.  *
  487.  */
  488. int mygetchar()
  489. {
  490.     register int c;
  491.  
  492.     c = fgetc(infile);
  493.  
  494.     /*
  495.     if (c == ';') {
  496.     ungetc(SPACE, infile);
  497.     return(';');
  498.     }
  499.     */
  500.     if (c == '/') {             /* open comment ? */
  501.     c = fgetc(infile);
  502.     if (c != '*') {
  503.         ungetc(c, infile);
  504.         return('/');
  505.     }
  506.     commln = ln;
  507.     commindent = indent;
  508.  
  509.     while (1) {
  510.         c = fgetc(infile);
  511.  
  512.         if (c == EOF) {         /* last comment never ended */
  513.         if (pr(2))
  514.             printf
  515.             ("Comment opened line %d unclosed by end of file.\n",
  516.             commln);
  517.         break; /* Get out of this loop! */
  518.         }
  519.  
  520.         else if (c == '/') {        /* nested comment ? */
  521.         if ((c = fgetc(infile)) == '*') {
  522.             if (pr(0))
  523.             fprintf(stdout,
  524.             "Nested comment: line %d, indent %d.  First open: line %d, indent %d\n",
  525.             ln, indent, commln, commindent);
  526.         }
  527.         else
  528.             ungetc(c, infile);
  529.         }
  530.         else if (c == '*') {        /* end comment ? */
  531.         if ((c = fgetc(infile)) == '/') {
  532.             if (indent != commindent  &&  indent-1 != commindent)
  533.             if (pr(0))
  534.                 printf(
  535.                 "Indent of comment close doesn't match open: lines %d, %d, indents %d, %d\n",
  536.                 commln, ln, commindent, indent);
  537.  
  538.             break;       /* only exit from loop, except EOF */
  539.         }
  540.         else
  541.             ungetc(c, infile);
  542.         }
  543.         else if (c == '\n') {
  544.         do {
  545.             if (c == SPACE)
  546.             indent++;
  547.             else if (c == '\t')
  548.             indent = ((indent+8)/8)*8;
  549.             else if (c == '\n') { 
  550.             ln++;
  551.             indent = 0;
  552.             } 
  553.         } while (isspace(c = fgetc(infile)));
  554.         ungetc(c, infile);
  555.         }
  556.     }
  557.     return(SPACE);
  558.  
  559.     }
  560.  
  561.     if (c == '\n'  ||  firsttime == 1) {
  562.     firsttime = 0;
  563. lf:
  564.     while (1) {
  565.         if (c == SPACE)
  566.         indent++;
  567.         else if (c == '\t')
  568.         indent = ((indent+8)/8)*8;
  569.         else if (c == '\n') { 
  570.         ln++;
  571.         indent = 0;
  572.         singlequoterr = 0;
  573.         } 
  574.         else {
  575.         ungetc(c, infile);
  576.         return('\n');
  577.         /*NOTREACHED*/
  578.         break;
  579.         }
  580.         c = fgetc(infile);
  581.     }
  582.     }
  583.  
  584.     if (c == SPACE  ||  c == '\t') {
  585.     do
  586.         c = fgetc(infile);
  587.     while (c == SPACE  ||  c == '\t');
  588.     if (c != '\n') {
  589.         ungetc(c, infile);
  590.         return(SPACE);
  591.     }
  592.     else
  593.         goto lf;
  594.     }
  595.     return(c);
  596. }
  597.  
  598. /*
  599.  *    administer count of error msgs. and suppress if too many
  600.  *    administer the status var.s
  601.  *    prepend file name to msg.
  602.  *    flag error msg.s (not warnings) with '*'
  603.  */
  604. int pr(error)
  605. int error;
  606. {
  607.     int i;
  608.     
  609.     if (singlequoterr)
  610.     return(0);
  611.  
  612.     if (verbose) {
  613.     for (i = stackc; i>0; i--) {
  614.         printf("%d: type ", i);
  615.         prtype(stack[i].type);
  616.         printf(", indent %d, line %d.\n", stack[i].b_indent, stack[i].b_ln);
  617.     }
  618.     }
  619.  
  620.     if (error == 2) {
  621.     errnmb = 0;
  622.     errstatus = 1;
  623.     }
  624.     else if (error) {
  625.     errstatus = 1;
  626.     if (errnmb < 0)
  627.         return(0);
  628.     else if (errnmb >= 9) {
  629.         errnmb = -1;
  630.         printf("Other error messages being suppressed.\n");
  631.         return(0);
  632.     }
  633.     }
  634.     else {
  635.     wstatus = 1;
  636.     if (wnmb < 0)
  637.         return(0);
  638.     else if (errnmb + wnmb >= 9) {
  639.         wnmb = -1;
  640.         puts("Further warning messages being suppressed.\n");
  641.         return(0);
  642.     }
  643.     }
  644.  
  645.     if (filename != NULL) {
  646.     fputs(filename, stdout);
  647.     fputs(": ", stdout);
  648.     }
  649.     if (error)
  650.     putchar('*');
  651.     if (error)
  652.     errnmb++;
  653.     else
  654.     wnmb++;
  655.     return(1);
  656. }
  657.  
  658. #ifndef UNIX
  659. /*
  660.  * Special fgetc() for BDS C
  661.  *
  662.  * It turns out that, using DIO, getc() is "cooked" (eats CRs, looks for
  663.  * ^z) if input is from redirected stdin, and "raw" if input is from
  664.  * an explicitly opened file.  This fgetc() manages to look the same
  665.  * in either case.
  666.  */
  667. int fgetc(iobuf)
  668. FILE *iobuf;
  669. {
  670.     int c;
  671.  
  672.     if (eofdet)
  673.     return (EOF);
  674.     while ((c = getc(iobuf)) == '\r')
  675.     ;
  676.     if (c == EOF || c == CPMEOF) {
  677.     c = EOF;
  678.     eofdet++;
  679.     }
  680.     return (c);
  681. }
  682. #endif
  683.  
  684. /*
  685.  *
  686.  */
  687. void newbrak(newtype)
  688. int newtype;
  689. {
  690.     if (newtype == 0) {
  691.     top = stack;
  692.     stackc = 0;
  693.     }
  694.     else {
  695.     top++;
  696.     stackc++;
  697.     }
  698.     if (stackc >= STACKSIZ) {
  699.     if (pr(2))
  700.         printf("***stack overflow, line %d.\n", ln);
  701.     exit(3);
  702.     }
  703.  
  704.     top->type = newtype;
  705.     top->b_indent = indent;
  706.     top->b_ln = ln;
  707.  
  708. }
  709.  
  710. /*
  711.  *
  712.  */
  713. void prtype(typ)
  714. int typ;
  715. {
  716.     switch(typ) {
  717.     case BRACE:
  718.     putchar('}');
  719.     break;
  720.     case PAREN:
  721.     putchar(')');
  722.     break;
  723.     case SQBRAK:
  724.     putchar(']');
  725.     break;
  726.     case IF:
  727.     fputs("if", stdout);
  728.     break;
  729.     case IFCOND:
  730.     fputs("if-condition", stdout);
  731.     break;
  732.     case THEN:
  733.     fputs("then", stdout);
  734.     break;
  735.     case ELSE:
  736.     fputs("else", stdout);
  737.     break;
  738.     case WHLCOND:
  739.     fputs("while-condition", stdout);
  740.     break;
  741.     default:
  742.     fputs("'NULL'", stdout);
  743.     break;
  744.     }
  745. }
  746.  
  747. /*
  748.  *
  749.  */
  750. void checkcloser(typ)
  751. int (typ);
  752. {
  753.     int i, found;
  754.  
  755.     i = found = 0;
  756.     if (typ == top->type  &&  top->b_indent == indent) {
  757.     rmbrak(1);
  758.     return;
  759.     }
  760.  
  761.     while (!found  &&  ++i < stackc  &&  indent <= (top-i)->b_indent)
  762.     if (typ == (top-i)->type  &&  (top-i)->b_indent == indent)
  763.         found = 1;
  764.  
  765.     if (found) {
  766.     if (pr(1))
  767.         printf("Missing closer%s detected line %d:\n", (i>1?"s":""), ln);
  768.     while (i--) {
  769.         if (pr(1)) {
  770.         fputs("\tMissing closing ", stdout);
  771.         prtype(top->type);
  772.         printf(" opened line %d.\n", top->b_ln);
  773.         }
  774.         switch (top->type) {
  775.         case BRACE:
  776.             bracecnt++;
  777.             break;
  778.         case SQBRAK:
  779.             sqbrakcnt++;
  780.             break;
  781.         case PAREN:
  782.             parencnt++;
  783.             break;
  784.         default:
  785.         break;
  786.         }
  787.         rmbrak(1);
  788.     }
  789.     rmbrak(1);    /* the matching brak */
  790.     }
  791.     else if (typ == top->type) {
  792.     if (indent != top->b_indent) {
  793.         if (pr(0)) {
  794.         fputs("Mismatched indent on closing ", stdout);
  795.         prtype(typ);
  796.         printf
  797.         (" lines %d, %d; indents %d, %d.\n",
  798.          top->b_ln, ln, top->b_indent, indent);
  799.         }
  800.     }
  801.     rmbrak(1);
  802.     }
  803.  
  804.     else {
  805.     switch (typ) {
  806.     case BRACE:
  807.         bracecnt--;
  808.         break;
  809.     case SQBRAK:
  810.         sqbrakcnt--;
  811.         break;
  812.     case PAREN:
  813.         parencnt--;
  814.         break;
  815.     default:
  816.         break;
  817.     }
  818.  
  819.     if (pr(1)) {
  820.         fputs("Muddle detected at unmatched closing ", stdout);
  821.         prtype(typ);
  822.         printf(" line %d.\n", ln);
  823.     }
  824.     }
  825. }
  826.  
  827. /*
  828.  *    removes IF from stack
  829.  *    checks else's indent
  830.  */
  831. void checkelse()
  832. {
  833.     int c;
  834.  
  835.     while ((c = mygetchar()) == SPACE  || c == '\n')
  836.     ;
  837.     if (c == 'e'
  838.     &&  (c = mygetchar()) == 'l'
  839.     &&  (c = mygetchar()) == 's'
  840.     &&  (c = mygetchar()) == 'e'
  841.     &&  !isalpha(c = fgetc(infile))  &&  !isdigit(c)) {
  842.     ungetc(c, infile);
  843.     if (top->type == THEN)
  844.         rmbrak(1);
  845.     if (top->type != IF) {
  846.         if (pr(1))
  847.         printf("Else with no if line %d.\n", ln);
  848.     }
  849.  
  850.     else if (indent+2 < top->b_indent) {
  851.         if (pr(1))
  852.         printf("Dangling else -- bound to wrong if?  \"if\" line %d, \"else\" line %d.\n", top->b_ln, ln);
  853.     }
  854.  
  855.     else if (indent != top->b_indent) {
  856.         if (pr(0)) {
  857.         fputs("Wrong indent for else", stdout);
  858.         if (indent-2 >  top->b_indent)
  859.             fputs(" (missing if?)", stdout);
  860.         printf(".  \"if\" line %d, \"else\" line %d.\n", top->b_ln, ln);
  861.         }
  862.     }
  863.  
  864.     if (top->type == IF)
  865.         rmbrak(1);
  866.     newbrak(ELSE);
  867.     }
  868.  
  869.     else {
  870.     myungetc(c);
  871.     ungetc(SPACE, infile);  /* BUG?? */
  872.     /* no else so terminate the IF */
  873.     if (top->type == IF) {
  874.         rmbrak(1);
  875.         while (top->type == ELSE)
  876.         rmbrak(1);
  877.         if (top->type == THEN) {
  878.         rmbrak(1);
  879.         checkelse();
  880.         }
  881.     }
  882.     }
  883. }
  884.