]> git.saurik.com Git - bison.git/blobdiff - lib/ansi2knr.c
* tests/regression.at, tests/torture.at, tests/calc.at: Adjust to
[bison.git] / lib / ansi2knr.c
index 196ba08ec21c2d91fbc2d3e902cdd1c46f205f06..2ff9d6198ebc77639012106e3b2c536f1521b500 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1989, 1997, 1998 Aladdin Enterprises.  All rights reserved. */
+/* Copyright (C) 1989, 2000 Aladdin Enterprises.  All rights reserved. */
 
 /*$Id$*/
 /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
@@ -37,27 +37,62 @@ program under the GPL.
  * There are no error messages.
  *
  * ansi2knr recognizes function definitions by seeing a non-keyword
- * identifier at the left margin, followed by a left parenthesis,
- * with a right parenthesis as the last character on the line,
- * and with a left brace as the first token on the following line
- * (ignoring possible intervening comments), except that a line
+ * identifier at the left margin, followed by a left parenthesis, with a
+ * right parenthesis as the last character on the line, and with a left
+ * brace as the first token on the following line (ignoring possible
+ * intervening comments and/or preprocessor directives), except that a line
  * consisting of only
  *     identifier1(identifier2)
  * will not be considered a function definition unless identifier2 is
- * the word "void".  ansi2knr will recognize a multi-line header provided
- * that no intervening line ends with a left or right brace or a semicolon.
- * These algorithms ignore whitespace and comments, except that
- * the function name must be the first thing on the line.
- * The following constructs will confuse it:
+ * the word "void", and a line consisting of
+ *     identifier1(identifier2, <<arbitrary>>)
+ * will not be considered a function definition.
+ * ansi2knr will recognize a multi-line header provided that no intervening
+ * line ends with a left or right brace or a semicolon.  These algorithms
+ * ignore whitespace, comments, and preprocessor directives, except that
+ * the function name must be the first thing on the line.  The following
+ * constructs will confuse it:
  *     - Any other construct that starts at the left margin and
  *         follows the above syntax (such as a macro or function call).
- *     - Some macros that tinker with the syntax of the function header.
+ *     - Some macros that tinker with the syntax of function headers.
  */
 
 /*
  * The original and principal author of ansi2knr is L. Peter Deutsch
  * <ghost@aladdin.com>.  Other authors are noted in the change history
  * that follows (in reverse chronological order):
+
+       lpd 2000-04-12 backs out Eggert's changes because of bugs:
+       - concatlits didn't declare the type of its bufend argument;
+       - concatlits didn't't recognize when it was inside a comment;
+       - scanstring could scan backward past the beginning of the string; when
+       - the check for \ + newline in scanstring was unnecessary.
+
+       2000-03-05  Paul Eggert  <eggert@twinsun.com>
+
+       Add support for concatenated string literals.
+       * ansi2knr.c (concatlits): New decl.
+       (main): Invoke concatlits to concatenate string literals.
+       (scanstring): Handle backslash-newline correctly.  Work with
+       character constants.  Fix bug when scanning backwards through
+       backslash-quote.  Check for unterminated strings.
+       (convert1): Parse character constants, too.
+       (appendline, concatlits): New functions.
+       * ansi2knr.1: Document this.
+
+       lpd 1999-08-17 added code to allow preprocessor directives
+               wherever comments are allowed
+       lpd 1999-04-12 added minor fixes from Pavel Roskin
+               <pavel_roskin@geocities.com> for clean compilation with
+               gcc -W -Wall
+       lpd 1999-03-22 added hack to recognize lines consisting of
+               identifier1(identifier2, xxx) as *not* being procedures
+       lpd 1999-02-03 made indentation of preprocessor commands consistent
+       lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
+               endless loop; quoted strings within an argument list
+               confused the parser
+       lpd 1999-01-24 added a check for write errors on the output,
+               suggested by Jim Meyering <meyering@ascend.com>
        lpd 1998-11-09 added further hack to recognize identifier(void)
                as being a procedure
        lpd 1998-10-23 added hack to recognize lines consisting of
@@ -153,19 +188,24 @@ program under the GPL.
 
 #endif
 
+/* Define NULL (for *very* old compilers). */
+#ifndef NULL
+# define NULL (0)
+#endif
+
 /*
  * The ctype macros don't always handle 8-bit characters correctly.
  * Compensate for this here.
  */
 #ifdef isascii
-#  undef HAVE_ISASCII          /* just in case */
-#  define HAVE_ISASCII 1
+# undef HAVE_ISASCII           /* just in case */
+# define HAVE_ISASCII 1
 #else
 #endif
 #if STDC_HEADERS || !HAVE_ISASCII
-#  define is_ascii(c) 1
+# define is_ascii(c) 1
 #else
-#  define is_ascii(c) isascii(c)
+# define is_ascii(c) isascii(c)
 #endif
 
 #define is_space(c) (is_ascii(c) && isspace(c))
@@ -177,7 +217,10 @@ program under the GPL.
 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
 
 /* Forward references */
+char *ppdirforward();
+char *ppdirbackward();
 char *skipspace();
+char *scanstring();
 int writeblanks();
 int test1();
 int convert1();
@@ -190,6 +233,8 @@ main(argc, argv)
 {      FILE *in = stdin;
        FILE *out = stdout;
        char *filename = 0;
+       char *program_name = argv[0];
+       char *output_name = 0;
 #define bufsize 5000                   /* arbitrary size */
        char *buf;
        char *line;
@@ -205,6 +250,7 @@ main(argc, argv)
         * check for this switch for backward compatibility.
         */
        int convert_varargs = 1;
+       int output_error;
 
        while ( argc > 1 && argv[1][0] == '-' ) {
          if ( !strcmp(argv[1], "--varargs") ) {
@@ -219,7 +265,8 @@ main(argc, argv)
            argv += 2;
            continue;
          }
-         fprintf(stderr, "Unrecognized switch: %s\n", argv[1]);
+         fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
+                 argv[1]);
          fprintf(stderr, usage);
          exit(1);
        }
@@ -229,16 +276,19 @@ main(argc, argv)
                fprintf(stderr, usage);
                exit(0);
        case 3:
-               out = fopen(argv[2], "w");
+               output_name = argv[2];
+               out = fopen(output_name, "w");
                if ( out == NULL ) {
-                 fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+                 fprintf(stderr, "%s: Cannot open output file %s\n",
+                         program_name, output_name);
                  exit(1);
                }
                /* falls through */
        case 2:
                in = fopen(argv[1], "r");
                if ( in == NULL ) {
-                 fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+                 fprintf(stderr, "%s: Cannot open input file %s\n",
+                         program_name, argv[1]);
                  exit(1);
                }
                if ( filename == 0 )
@@ -250,6 +300,11 @@ main(argc, argv)
        if ( filename )
          fprintf(out, "#line 1 \"%s\"\n", filename);
        buf = malloc(bufsize);
+       if ( buf == NULL )
+          {
+               fprintf(stderr, "Unable to allocate read buffer!\n");
+               exit(1);
+          }
        line = buf;
        while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
           {
@@ -266,7 +321,7 @@ f:                  if ( line >= buf + (bufsize - 1) ) /* overflow check */
                          goto wl;
                        if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
                          goto wl;
-                       switch ( *skipspace(more, 1) )
+                       switch ( *skipspace(ppdirforward(more), 1) )
                          {
                          case '{':
                            /* Definitely a function header. */
@@ -300,32 +355,91 @@ wl:                       fputs(buf, out);
        if ( line != buf )
          fputs(buf, out);
        free(buf);
-       if ( out != stdout )
-         fclose(out);
+       if ( output_name ) {
+         output_error = ferror(out);
+         output_error |= fclose(out);
+       } else {                /* out == stdout */
+         fflush(out);
+         output_error = ferror(out);
+       }
+       if ( output_error ) {
+         fprintf(stderr, "%s: error writing to %s\n", program_name,
+                 (output_name ? output_name : "stdout"));
+         exit(1);
+       }
        if ( in != stdin )
          fclose(in);
        return 0;
 }
 
-/* Skip over space and comments, in either direction. */
+/*
+ * Skip forward or backward over one or more preprocessor directives.
+ */
+char *
+ppdirforward(p)
+    char *p;
+{
+    for (; *p == '#'; ++p) {
+       for (; *p != '\r' && *p != '\n'; ++p)
+           if (*p == 0)
+               return p;
+       if (*p == '\r' && p[1] == '\n')
+           ++p;
+    }
+    return p;
+}
+char *
+ppdirbackward(p, limit)
+    char *p;
+    char *limit;
+{
+    char *np = p;
+
+    for (;; p = --np) {
+       if (*np == '\n' && np[-1] == '\r')
+           --np;
+       for (; np > limit && np[-1] != '\r' && np[-1] != '\n'; --np)
+           if (np[-1] == 0)
+               return np;
+       if (*np != '#')
+           return p;
+    }
+}
+
+/*
+ * Skip over whitespace, comments, and preprocessor directives,
+ * in either direction.
+ */
 char *
 skipspace(p, dir)
-    register char *p;
-    register int dir;                  /* 1 for forward, -1 for backward */
-{      for ( ; ; )
-          {    while ( is_space(*p) )
-                 p += dir;
-               if ( !(*p == '/' && p[dir] == '*') )
-                 break;
-               p += dir;  p += dir;
-               while ( !(*p == '*' && p[dir] == '/') )
-                  {    if ( *p == 0 )
-                         return p;     /* multi-line comment?? */
-                       p += dir;
-                  }
-               p += dir;  p += dir;
-          }
-       return p;
+    char *p;
+    int dir;                   /* 1 for forward, -1 for backward */
+{
+    for ( ; ; ) {
+       while ( is_space(*p) )
+           p += dir;
+       if ( !(*p == '/' && p[dir] == '*') )
+           break;
+       p += dir;  p += dir;
+       while ( !(*p == '*' && p[dir] == '/') ) {
+           if ( *p == 0 )
+               return p;       /* multi-line comment?? */
+           p += dir;
+       }
+       p += dir;  p += dir;
+    }
+    return p;
+}
+
+/* Scan over a quoted string, in either direction. */
+char *
+scanstring(p, dir)
+    char *p;
+    int dir;
+{
+    for (p += dir; ; p += dir)
+       if (*p == '"' && p[-dir] != '\\')
+           return p + dir;
 }
 
 /*
@@ -359,14 +473,14 @@ writeblanks(start, end)
 int
 test1(buf)
     char *buf;
-{      register char *p = buf;
+{      char *p = buf;
        char *bend;
        char *endfn;
        int contin;
 
        if ( !isidfirstchar(*p) )
          return 0;             /* no name at left margin */
-       bend = skipspace(buf + strlen(buf) - 1, -1);
+       bend = skipspace(ppdirbackward(buf + strlen(buf) - 1, buf), -1);
        switch ( *bend )
           {
           case ';': contin = 0 /*2*/; break;
@@ -396,7 +510,7 @@ test1(buf)
                   };
                char **key = words;
                char *kp;
-               int len = endfn - buf;
+               unsigned len = endfn - buf;
 
                while ( (kp = *key) != 0 )
                   {    if ( strlen(kp) == len && !strncmp(kp, buf, len) )
@@ -409,14 +523,16 @@ test1(buf)
               int len;
               /*
                * Check for identifier1(identifier2) and not
-               * identifier1(void).
+               * identifier1(void), or identifier1(identifier2, xxxx).
                */
 
               while ( isidchar(*p) )
                   p++;
               len = p - id;
               p = skipspace(p, 1);
-              if ( *p == ')' && (len != 4 || strncmp(id, "void", 4)) )
+              if (*p == ',' ||
+                  (*p == ')' && (len != 4 || strncmp(id, "void", 4)))
+                  )
                   return 0;    /* not a function */
           }
        /*
@@ -443,7 +559,7 @@ convert1(buf, out, header, convert_varargs)
     int header;                        /* Boolean */
     int convert_varargs;       /* Boolean */
 {      char *endfn;
-       register char *p;
+       char *p;
        /*
         * The breaks table contains pointers to the beginning and end
         * of each argument.
@@ -461,7 +577,7 @@ convert1(buf, out, header, convert_varargs)
          ;
 top:   p = endfn;
        breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
-       if ( breaks == 0 )
+       if ( breaks == NULL )
           {    /* Couldn't allocate break table, give up */
                fprintf(stderr, "Unable to allocate break table!\n");
                fputs(buf, out);
@@ -473,7 +589,7 @@ top:        p = endfn;
        do
           {    int level = 0;
                char *lp = NULL;
-               char *rp;
+               char *rp = NULL;
                char *end = NULL;
 
                if ( bp >= btop )
@@ -500,14 +616,18 @@ top:      p = endfn;
                                else rp = p;
                                break;
                           case '/':
-                               p = skipspace(p, 1) - 1;
+                               if (p[1] == '*')
+                                   p = skipspace(p, 1) - 1;
                                break;
+                          case '"':
+                              p = scanstring(p, 1) - 1;
+                              break;
                           default:
                                ;
                           }
                   }
                /* Erase any embedded prototype parameters. */
-               if ( lp )
+               if ( lp && rp )
                  writeblanks(lp + 1, rp);
                p--;                    /* back up over terminator */
                /* Find the name being declared. */
@@ -523,9 +643,19 @@ top:       p = endfn;
                                while ( level )
                                 switch ( *--p )
                                   {
-                                  case ']': case ')': level++; break;
-                                  case '[': case '(': level--; break;
-                                  case '/': p = skipspace(p, -1) + 1; break;
+                                  case ']': case ')':
+                                      level++;
+                                      break;
+                                  case '[': case '(':
+                                      level--;
+                                      break;
+                                  case '/':
+                                      if (p > buf && p[-1] == '*')
+                                          p = skipspace(p, -1) + 1;
+                                      break;
+                                  case '"':
+                                      p = scanstring(p, -1) + 1;
+                                      break;
                                   default: ;
                                   }
                           }