home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / ccmt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-20  |  19.9 KB  |  561 lines

  1. /*===========================================================================*
  2.  * == main program ==
  3.  * ccmt.c - C comment checker.
  4.  *
  5.  * (C)Copyright IBM Corporation, 1990, 1991.               Brian E. Yoder
  6.  *
  7.  * This program checks for mismatched comments in C source code.
  8.  *
  9.  * Each filename listed on the command line is assumed to be a C source file
  10.  * (.c, .h, etc.) and is checked for problems with comments.
  11.  *
  12.  * This program follows the ANSI standard that doesn't allow the nesting of
  13.  * comments.  A compiler that supports nested comments would make this utility
  14.  * unnecessary.
  15.  *
  16.  * The Ingres embedded SQL requires some strings to be entered in single quotes.
  17.  * Therefore, character constants are treated as single-quote strings by ccmt.
  18.  *
  19.  * 02/26/90 - Created.
  20.  * 02/27/90 - Initial version completed.
  21.  * 02/28/90 - Updated to use a state machine.
  22.  * 03/02/90 - Updated to support double-slash comments that stop at end of line.
  23.  * 04/03/91 - Ported from AIX to DOS C/2.
  24.  *===========================================================================*/
  25.  
  26. char ver[] = "ccmt: C comment checker.  (C)IBM Corp. 1990";
  27. char author[] = "Brian E. Yoder";
  28.  
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <memory.h>
  32. #include <sys/types.h>
  33.  
  34. #define MINARGS  1             /* Mimimum command line arguments required */
  35.  
  36. #define BUFFLEN  2048          /* Length of input line buffer */
  37.  
  38. #define NO       0             /* Yes/no definitions */
  39. #define YES      1
  40.  
  41. /*===========================================================================*
  42.  * Function prototypes for subroutines in this module
  43.  *===========================================================================*/
  44.  
  45. static void syntax();
  46. static int  chkfile(FILE *);
  47. static int  get_char();
  48. static int  put_char();
  49. static void errmsg(long, char *);
  50. static int  pstate(int);
  51.  
  52. /*===========================================================================*
  53.  * Misc data
  54.  *===========================================================================*/
  55.  
  56. static char *pgm;              /* Pointer to name of program */
  57. static int   debug = NO;       /* 'debug' flag */
  58.  
  59. /*===========================================================================*
  60.  * Data used to process each source file:
  61.  *===========================================================================*/
  62.  
  63. static char *fname = NULL;     /* Pointer to file name */
  64. static FILE *sfile = NULL;     /* Input source file */
  65.  
  66. static long  linenum;          /* Current line number */
  67. static char *cptr;             /* Ptr to a char in line buffer */
  68.  
  69. static char  lbuff[BUFFLEN];   /* Input line buffer */
  70.  
  71. /*===========================================================================*
  72.  * List of states for the state machine in the pstate() subroutine.
  73.  *
  74.  * The state machine is entered in the 'Text' state.  This is the normal
  75.  * state.
  76.  *
  77.  * Characters are read from the input file one-by-one.  The get_char() routine
  78.  * not returns a character and sets some global (within this module) variable
  79.  * with information about the character, such as the current line.
  80.  *
  81.  * The pstate() subroutine is called with the value of the character as its
  82.  * argument.  Note that the subroutine is called once for each character
  83.  * that is obtained from the input file.
  84.  *
  85.  * Depending upon the character, the state machine in pstate() performs a
  86.  * specific action (such as storing the current line number or writing an
  87.  * error message).  The state machine then sets the next state as appropriate.
  88.  *
  89.  * The return code from the pstate() subroutine tells the calling program
  90.  * whether or not to continue: 0 = continue, other = stop.
  91.  *===========================================================================*/
  92.  
  93. enum CCMT_STATES {
  94.  
  95.         Text,             /* Text mode */
  96.  
  97.         String,           /* Inside double-quote string */
  98.         StringEsc,        /* Inside escape sequence of a double-quote string */
  99.  
  100.         CharConst,        /* Inside single-quote string */
  101.         CharConstEsc,     /* Inside escape sequence of a single-quote string */
  102.  
  103.         PUEndComment,     /* Possible unexpected end of comment */
  104.  
  105.         PStartComment,    /* Possible start of comment */
  106.         Comment,          /* Inside a comment */
  107.         PEndComment,      /* Possible end of comment */
  108.         PNestComment,     /* Possible start of nested comment */
  109.  
  110.         EOLComment        /* Inside a comment that stops at end of line (EOL) */
  111. };
  112.  
  113. /*===========================================================================*
  114.  * Data for the state machine
  115.  *===========================================================================*/
  116.  
  117. static  int  state;            /* Current state */
  118. static  long start_line;       /* Line on which string, comment, etc. started */
  119.  
  120. /*===========================================================================*
  121.  * Main program entry point
  122.  *===========================================================================*/
  123.  
  124. main(argc, argv)
  125.  
  126. int    argc;
  127. char **argv;
  128.  
  129. {
  130.   int rc;                      /* return code storage */
  131.  
  132.   pgm = *argv;                 /* Store pointer to name of program, then: */
  133.   argv++;                      /* Ignore 1st argument (program name) */
  134.   argc--;
  135.  
  136.   if (argc <  MINARGS)         /* If not enough arguments: display syntax */
  137.      syntax();
  138.  
  139.   if (strcmp(*argv, "-d") == 0)
  140.   {                            /* If -d flag: */
  141.      debug = YES;                /* Set 'debug' flag = yes */
  142.      argv++;                     /* Bump to next argument */
  143.      argc--;
  144.      if (argc <  MINARGS)      /* If not enough arguments: display syntax */
  145.         syntax();
  146.   }
  147.  
  148.  /*==========================================================================*
  149.   * Check each file whose name is found on the command line:
  150.   *==========================================================================*/
  151.  
  152.   while (argc > 0)             /* For each argument passed by the shell: */
  153.   {
  154.      fname = *argv;                 /* Store pointer to name of file */
  155.  
  156.      sfile = fopen(fname, "r");     /* Open source file */
  157.      if (sfile == NULL)             /* If error: */
  158.      {                                   /* Display message */
  159.         fprintf(stderr, "%s: Cannot open file '%s'.\n",
  160.                 pgm, fname);
  161.      }
  162.      else                           /* If no error: */
  163.      {
  164.         chkfile(sfile);                  /* Check file */
  165.         fclose(sfile);                   /* Close file */
  166.      }
  167.  
  168.      argv++;                        /* Point to next argument */
  169.      argc--;
  170.   }                            /* end of main while loop */
  171.  
  172.   return(0);                   /* Done: return */
  173. }
  174.  
  175. /*===========================================================================*
  176.  * syntax() - Display command syntax and EXIT TO OS!
  177.  *===========================================================================*/
  178. static void syntax()
  179. {
  180.   printf("Usage: ccmt sfile [. . . sfile]\n");
  181.   printf("\n");
  182.   printf("  One or more source files are checked to ensure that comments\n");
  183.   printf("  are properly terminated and are not nested.\n");
  184.   printf("\n");
  185.   exit(1);
  186. }
  187.  
  188.  
  189.  
  190. /*===========================================================================*
  191.  * chkfile() - Check a file for correct syntax
  192.  *
  193.  * This subroutine calls pstate() for each character obtained.
  194.  *===========================================================================*/
  195. static int chkfile(sfile)
  196.  
  197. FILE *sfile;                   /* Input source file */
  198.  
  199. {
  200.   int   rc;                    /* Return code */
  201.   int   c;                     /* Character obtained from get_char() */
  202.  
  203.   linenum = 0L;                /* Initialize line number to zero, */
  204.   cptr = lbuff;                /* Point cptr to line buffer, and */
  205.   lbuff[0] = '\0';             /* Store null string, to set up get_char() */
  206.  
  207.   state = Text;                /* Initialize state to 'text mode' */
  208.  
  209.   for (;;)                     /* For each character in the input file: */
  210.   {
  211.      c = get_char();                /* Get the character */
  212.      rc = pstate(c);                /* Process state machine for character */
  213.      if (rc != 0)                   /* If quit returned: */
  214.         break;                           /* Break out of loop */
  215.   }
  216.  
  217.   return(0);
  218. }
  219.  
  220. /*===========================================================================*
  221.  * get_char() - Get next character from 'sfile'.
  222.  *
  223.  * Returns:
  224.  *   -1 if end of file.
  225.  *   Otherwise, the next character from 'sfile' is returned.
  226.  *===========================================================================*/
  227. static int get_char()
  228. {
  229.   int   rc;                    /* Return code */
  230.   char *s;                     /* Return from fgets() */
  231.  
  232.   if (*cptr == '\0')           /* If at end of input line string: */
  233.   {
  234.      s = fgets(lbuff, BUFFLEN,      /* Get next line */
  235.                 sfile);
  236.      if (s == NULL)                 /* If no more: */
  237.         return(-1);                    /* Return end-of-file */
  238.  
  239.      cptr = lbuff;                  /* Point cptr to start of line buffer */
  240.      linenum++;                     /* Increment line number */
  241.   }
  242.  
  243.   rc = *cptr;                  /* Get (next) character from line buffer */
  244.   cptr++;                      /* Bump character pointer */
  245.  
  246.   return(rc);                  /* Return the character */
  247. }
  248.  
  249. /*===========================================================================*
  250.  * put_char() - Puts back previous character.
  251.  *
  252.  * This routine is only guarenteed to put back the last character obtained
  253.  * by a previous call to get_char().
  254.  *===========================================================================*/
  255. static int put_char()
  256. {
  257.   int   rc;                    /* Return code */
  258.  
  259.   if (cptr > lbuff)            /* If cptr is past the start of lbuff: */
  260.      cptr--;                   /* It's safe to back it up! */
  261.  
  262.   return(0);
  263. }
  264.  
  265. /*===========================================================================*
  266.  * errmsg() - Write error message to stderr.
  267.  *===========================================================================*/
  268. static void errmsg(line, msg)
  269.  
  270. long  line;                    /* Line number within file */
  271. char *msg;                     /* Pointer to error message */
  272. {
  273.    fprintf(stderr,
  274.            "%s: file '%s' line %ld: %s\n",
  275.            pgm, fname, line, msg);
  276.  
  277.   return;                      /* Return */
  278. }
  279.  
  280. /*===========================================================================*
  281.  * pstate() - Process state machine.
  282.  *
  283.  * This subroutine checks the state in 'state' and the character passed to it.
  284.  * It performs a specific action and advances the 'state' variable to the
  285.  * next state.
  286.  *
  287.  * When some errors are found (including when a -1 character is found), the
  288.  * state machine issues a 'return(1)' to tell the calling program to stop
  289.  * processing this file.  If no return is specified, then the state machine
  290.  * issues a 'return(0)' by default, telling the calling program that it's ok
  291.  * to continue.
  292.  *
  293.  * Returns:
  294.  *   0, if it is ok to continue.
  295.  *   1, if input file processing should stop.
  296.  *===========================================================================*/
  297. static int pstate(ch)
  298.  
  299. int ch;                        /* Character obtained from the input file */
  300.  
  301. {
  302.   int   rc;                    /* Return code */
  303.  
  304.   switch (state)
  305.   {
  306.      /*======================================================================*/
  307.      case Text:                /* In normal text mode */
  308.         switch (ch)
  309.         {
  310.            case -1:                 /* No more characters to read: Done! */
  311.               return(1);
  312.               break;
  313.  
  314.            case '"':                /* Double quote found */
  315.               start_line = linenum;
  316.               state = String;
  317.               break;
  318.  
  319.            case '\'':               /* Single quote found */
  320.               start_line = linenum;
  321.               state = CharConst;
  322.               break;
  323.  
  324.            case '/':                /* Possible start of comment found */
  325.               start_line = linenum;
  326.               state = PStartComment;
  327.               break;
  328.  
  329.            case '*':                /* Possible unexpected end of comment found */
  330.               state = PUEndComment;
  331.               break;
  332.  
  333.            default:                 /* Anything else */
  334.               break;
  335.         }
  336.         break;
  337.  
  338.      /*======================================================================*/
  339.      case String:              /* Inside double-quote string */
  340.         switch (ch)
  341.         {
  342.            case -1:                 /* No more characters to read */
  343.               errmsg(start_line, "String is not terminated before end of file.");
  344.               return(1);
  345.               break;
  346.  
  347.            case '\n':               /* Newline character found */
  348.               errmsg(start_line, "String is not terminated before end of line.");
  349.               state = Text;
  350.               break;
  351.  
  352.            case '"':                /* Double quote found */
  353.               state = Text;
  354.               break;
  355.  
  356.            case '\\':               /* Escape character found */
  357.               state = StringEsc;
  358.               break;
  359.  
  360.            default:                 /* Anything else */
  361.               break;
  362.         }
  363.         break;
  364.  
  365.      /*======================================================================*/
  366.      case StringEsc:           /* Inside escape sequence of a double-quote string */
  367.         switch (ch)
  368.         {
  369.            case -1:                 /* No more characters to read */
  370.               errmsg(start_line, "String is not terminated before end of file.");
  371.               return(1);
  372.               break;
  373.  
  374.            default:                 /* Anything else */
  375.               state = String;
  376.               break;
  377.         }
  378.         break;
  379.  
  380.      /*======================================================================*/
  381.      case CharConst:           /* Inside single-quote string */
  382.         switch (ch)
  383.         {
  384.            case -1:                 /* No more characters to read */
  385.               errmsg(start_line, "Character constant is not terminated before end of file.");
  386.               return(1);
  387.               break;
  388.  
  389.            case '\n':               /* Newline character found */
  390.               errmsg(start_line, "Character constant is not terminated before end of line.");
  391.               state = Text;
  392.               break;
  393.  
  394.            case '\'':               /* Single quote found */
  395.               state = Text;
  396.               break;
  397.  
  398.            case '\\':               /* Escape character found */
  399.               state = CharConstEsc;
  400.               break;
  401.  
  402.            default:                 /* Anything else */
  403.               break;
  404.         }
  405.         break;
  406.  
  407.      /*======================================================================*/
  408.      case CharConstEsc:        /* Inside escape sequence of a single-quote string */
  409.         switch (ch)
  410.         {
  411.            case -1:                 /* No more characters to read */
  412.               errmsg(start_line, "Character constant is not terminated before end of file.");
  413.               return(1);
  414.               break;
  415.  
  416.            default:                 /* Anything else */
  417.               state = CharConst;
  418.               break;
  419.         }
  420.         break;
  421.  
  422.      /*======================================================================*/
  423.      case PUEndComment:        /* Possible unexpected end of comment */
  424.         switch (ch)
  425.         {
  426.            case -1:                 /* No more characters to read */
  427.               return(1);
  428.               break;
  429.  
  430.            case '/':                /* End of comment was found */
  431.               errmsg(linenum, "Unexpected end-of-comment found.");
  432.               state = Text;
  433.               break;
  434.  
  435.            default:                 /* Anything else */
  436.               put_char();                /* Put character back */
  437.               state = Text;
  438.               break;
  439.         }
  440.         break;
  441.  
  442.      /*======================================================================*/
  443.      case PStartComment:       /* Possible start of comment */
  444.         switch (ch)
  445.         {
  446.            case -1:                 /* No more characters to read */
  447.               return(1);
  448.               break;
  449.  
  450.            case '*':                /* Start of comment was found */
  451.               state = Comment;
  452.               break;
  453.  
  454.            case '/':                /* Start of 'EOL' comment was found */
  455.               state = EOLComment;
  456.               break;
  457.  
  458.            default:                 /* Anything else */
  459.               put_char();                /* Put character back */
  460.               state = Text;
  461.               break;
  462.         }
  463.         break;
  464.  
  465.      /*======================================================================*/
  466.      case Comment:             /* Inside a comment */
  467.         switch (ch)
  468.         {
  469.            case -1:                 /* No more characters to read */
  470.               errmsg(start_line, "Comment is not terminated before end of file.");
  471.               return(1);
  472.               break;
  473.  
  474.            case '*':                /* Possible end of comment was found */
  475.               state = PEndComment;
  476.               break;
  477.  
  478.            case '/':                /* Possible start of nested comment was found */
  479.               state = PNestComment;
  480.               break;
  481.  
  482.            default:                 /* Anything else */
  483.               break;
  484.         }
  485.         break;
  486.  
  487.      /*======================================================================*/
  488.      case PEndComment:         /* Possible end of comment */
  489.         switch (ch)
  490.         {
  491.            case -1:                 /* No more characters to read */
  492.               errmsg(start_line, "Comment is not terminated before end of file.");
  493.               return(1);
  494.               break;
  495.  
  496.            case '/':                /* End of comment was found */
  497.               state = Text;
  498.               break;
  499.  
  500.            case '*':                /* Possible end of comment found */
  501.               state = PEndComment;
  502.               break;
  503.  
  504.            default:                 /* Anything else */
  505.               state = Comment;
  506.               break;
  507.         }
  508.         break;
  509.  
  510.      /*======================================================================*/
  511.      case PNestComment:        /* Possible start of nested comment */
  512.         switch (ch)
  513.         {
  514.            case -1:                 /* No more characters to read */
  515.               errmsg(start_line, "Comment is not terminated before end of file.");
  516.               return(1);
  517.               break;
  518.  
  519.            case '*':                /* Start of nested comment was found */
  520.               errmsg(start_line, "Comment started.");
  521.               errmsg(linenum, "Nested comment started.");
  522.               state = Comment;
  523.               break;
  524.  
  525.            case '/':                /* Possible start of nested comment was found */
  526.               state = PNestComment;
  527.               break;
  528.  
  529.            default:                 /* Anything else */
  530.               state = Comment;
  531.               break;
  532.         }
  533.         break;
  534.  
  535.      /*======================================================================*/
  536.      case EOLComment:          /* Inside a comment that stops at end of line */
  537.         switch (ch)
  538.         {
  539.            case -1:                 /* No more characters to read */
  540.               return(1);
  541.               break;
  542.  
  543.            case '\n':               /* End of line (EOL) was found */
  544.               state = Text;
  545.               break;
  546.  
  547.            default:                 /* Anything else */
  548.               break;
  549.         }
  550.         break;
  551.  
  552.      /*======================================================================*/
  553.      default:
  554.         errmsg(linenum, "INTERNAL ERROR: Invalid state encountered.");
  555.         return(1);
  556.         break;
  557.   }
  558.  
  559.   return(0);                   /* Return and tell caller to keep going */
  560. }
  561.