diff options
author | Jim Meyering <jim@meyering.net> | 1999-01-27 21:01:55 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1999-01-27 21:01:55 +0000 |
commit | 87ff33dd2c7c073212db1ac011e9cf85fc433be9 (patch) | |
tree | 78fa6b0ad1f73ed1f0118f05a69e3306cc34ffd8 | |
parent | 672666950c38e79a4dc4fb742f87904ff3900500 (diff) | |
download | idutils-87ff33dd2c7c073212db1ac011e9cf85fc433be9.tar.gz idutils-87ff33dd2c7c073212db1ac011e9cf85fc433be9.tar.bz2 idutils-87ff33dd2c7c073212db1ac011e9cf85fc433be9.zip |
Import new version.
-rw-r--r-- | src/ansi2knr.c | 395 |
1 files changed, 292 insertions, 103 deletions
diff --git a/src/ansi2knr.c b/src/ansi2knr.c index 9bcc4ad..df4bc77 100644 --- a/src/ansi2knr.c +++ b/src/ansi2knr.c @@ -1,72 +1,111 @@ -/* Copyright (C) 1989, 1991, 1993, 1994 Aladdin Enterprises. All rights reserved. */ +/* Copyright (C) 1989, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. */ -/* ansi2knr.c */ -/* Convert ANSI function declarations to K&R syntax */ +/*$Id$*/ +/* Convert ANSI C function definitions to K&R ("traditional C") syntax */ /* -ansi2knr is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY. No author or distributor accepts responsibility -to anyone for the consequences of using it or for whether it serves any -particular purpose or works at all, unless he says so in writing. Refer -to the GNU General Public License for full details. - -Everyone is granted permission to copy, modify and redistribute -ansi2knr, but only under the conditions described in the GNU -General Public License. A copy of this license is supposed to have been -given to you along with ansi2knr so you can know your rights and -responsibilities. It should be in a file named COPYLEFT. Among other +ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone for the +consequences of using it or for whether it serves any particular purpose or +works at all, unless he says so in writing. Refer to the GNU General Public +License (the "GPL") for full details. + +Everyone is granted permission to copy, modify and redistribute ansi2knr, +but only under the conditions described in the GPL. A copy of this license +is supposed to have been given to you along with ansi2knr so you can know +your rights and responsibilities. It should be in a file named COPYLEFT, +or, if there is no file named COPYLEFT, a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. + +We explicitly state here what we believe is already implied by the GPL: if +the ansi2knr program is distributed as a separate set of sources and a +separate executable file which are aggregated on a storage medium together +with another program, this in itself does not bring the other program under +the GPL, nor does the mere fact that such a program or the procedures for +constructing it invoke the ansi2knr executable bring any other part of the +program under the GPL. */ /* * Usage: - ansi2knr [--varargs] input_file [output_file] + ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]] + * --filename provides the file name for the #line directive in the output, + * overriding input_file (if present). + * If no input_file is supplied, input is read from stdin. * If no output_file is supplied, output goes to stdout. * 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. - * It will recognize a multi-line header provided that the last character - * of the last line of the header is a right parenthesis, - * and no intervening line ends with a left brace or a semicolon. + * 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 + * 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: * - Any other construct that starts at the left margin and * follows the above syntax (such as a macro or function call). - * - Macros that tinker with the syntax of the function header. - * - * If the --varargs switch is supplied, ansi2knr will attempt to - * convert a ... argument to va_alist and va_dcl. If this switch is not - * supplied, ansi2knr will simply drop any such arguments. + * - 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 94-10-10 removed CONFIG_BROKETS conditional - lpd 94-07-16 added some conditionals to help GNU `configure', + 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 + identifier1(identifier2) as *not* being procedures + lpd 1997-12-08 made input_file optional; only closes input and/or + output file if not stdin or stdout respectively; prints + usage message on stderr rather than stdout; adds + --filename switch (changes suggested by + <ceder@lysator.liu.se>) + lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with + compilers that don't understand void, as suggested by + Tom Lane + lpd 1996-01-15 changed to require that the first non-comment token + on the line following a function header be a left brace, + to reduce sensitivity to macros, as suggested by Tom Lane + <tgl@sss.pgh.pa.us> + lpd 1995-06-22 removed #ifndefs whose sole purpose was to define + undefined preprocessor symbols as 0; changed all #ifdefs + for configuration symbols to #ifs + lpd 1995-04-05 changed copyright notice to make it clear that + including ansi2knr in a program does not bring the entire + program under the GPL + lpd 1994-12-18 added conditionals for systems where ctype macros + don't handle 8-bit characters properly, suggested by + Francois Pinard <pinard@iro.umontreal.ca>; + removed --varargs switch (this is now the default) + lpd 1994-10-10 removed CONFIG_BROKETS conditional + lpd 1994-07-16 added some conditionals to help GNU `configure', suggested by Francois Pinard <pinard@iro.umontreal.ca>; properly erase prototype args in function parameters, contributed by Jim Avera <jima@netcom.com>; correct error in writeblanks (it shouldn't erase EOLs) - lpd 89-xx-xx original version + lpd 1989-xx-xx original version */ /* Most of the conditionals here are to make ansi2knr work with */ -/* the GNU configure machinery. */ +/* or without the GNU configure machinery. */ -#ifdef HAVE_CONFIG_H +#if HAVE_CONFIG_H # include <config.h> #endif #include <stdio.h> #include <ctype.h> -#ifdef HAVE_CONFIG_H +#if HAVE_CONFIG_H /* For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h). @@ -81,33 +120,67 @@ copies. #else /* not HAVE_CONFIG_H */ -/* - Without AC_CONFIG_HEADER, merely use <string.h> as in the original - Ghostscript distribution. This loses on older BSD systems. - */ +/* Otherwise do it the hard way */ -# include <string.h> +# ifdef BSD +# include <strings.h> +# else +# ifdef VMS + extern int strlen(), strncmp(); +# else +# include <string.h> +# endif +# endif #endif /* not HAVE_CONFIG_H */ -#ifdef STDC_HEADERS +#if STDC_HEADERS # include <stdlib.h> #else /* malloc and free should be declared in stdlib.h, but if you've got a K&R compiler, they probably aren't. */ -char *malloc(); -void free(); +# ifdef MSDOS +# include <malloc.h> +# else +# ifdef VMS + extern char *malloc(); + extern void free(); +# else + extern char *malloc(); + extern int free(); +# endif +# endif + +#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 +#else +#endif +#if STDC_HEADERS || !HAVE_ISASCII +# define is_ascii(c) 1 +#else +# define is_ascii(c) isascii(c) #endif +#define is_space(c) (is_ascii(c) && isspace(c)) +#define is_alpha(c) (is_ascii(c) && isalpha(c)) +#define is_alnum(c) (is_ascii(c) && isalnum(c)) + /* Scanning macros */ -#define isidchar(ch) (isalnum(ch) || (ch) == '_') -#define isidfirstchar(ch) (isalpha(ch) || (ch) == '_') +#define isidchar(ch) (is_alnum(ch) || (ch) == '_') +#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_') /* Forward references */ char *skipspace(); -void writeblanks(); +int writeblanks(); int test1(); int convert1(); @@ -116,69 +189,140 @@ int main(argc, argv) int argc; char *argv[]; -{ FILE *in, *out; +{ 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; - int convert_varargs = 0; - if ( argc > 1 && argv[1][0] == '-' ) - { if ( !strcmp(argv[1], "--varargs") ) - { convert_varargs = 1; - argc--; - argv++; - } - else - { fprintf(stderr, "Unrecognized switch: %s\n", argv[1]); - exit(1); - } + char *more; + char *usage = + "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n"; + /* + * In previous versions, ansi2knr recognized a --varargs switch. + * If this switch was supplied, ansi2knr would attempt to convert + * a ... argument to va_alist and va_dcl; if this switch was not + * supplied, ansi2knr would simply drop any such arguments. + * Now, ansi2knr always does this conversion, and we only + * 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") ) { + convert_varargs = 1; + argc--; + argv++; + continue; } + if ( !strcmp(argv[1], "--filename") && argc > 2 ) { + filename = argv[2]; + argc -= 2; + argv += 2; + continue; + } + fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name, + argv[1]); + fprintf(stderr, usage); + exit(1); + } switch ( argc ) { default: - printf("Usage: ansi2knr [--varargs] input_file [output_file]\n"); + fprintf(stderr, usage); exit(0); + case 3: + output_name = argv[2]; + out = fopen(output_name, "w"); + if ( out == NULL ) { + fprintf(stderr, "%s: Cannot open output file %s\n", + program_name, output_name); + exit(1); + } + /* falls through */ case 2: - out = stdout; + in = fopen(argv[1], "r"); + if ( in == NULL ) { + fprintf(stderr, "%s: Cannot open input file %s\n", + program_name, argv[1]); + exit(1); + } + if ( filename == 0 ) + filename = argv[1]; + /* falls through */ + case 1: break; - case 3: - out = fopen(argv[2], "w"); - if ( out == NULL ) - { fprintf(stderr, "Cannot open output file %s\n", argv[2]); - exit(1); - } - } - in = fopen(argv[1], "r"); - if ( in == NULL ) - { fprintf(stderr, "Cannot open input file %s\n", argv[1]); - exit(1); } - fprintf(out, "#line 1 \"%s\"\n", argv[1]); + if ( filename ) + fprintf(out, "#line 1 \"%s\"\n", filename); buf = malloc(bufsize); line = buf; while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL ) - { switch ( test1(buf) ) + { +test: line += strlen(line); + switch ( test1(buf) ) { case 2: /* a function header */ convert1(buf, out, 1, convert_varargs); break; case 1: /* a function */ - convert1(buf, out, 0, convert_varargs); + /* Check for a { at the start of the next line. */ + more = ++line; +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) ) + { + case '{': + /* Definitely a function header. */ + convert1(buf, out, 0, convert_varargs); + fputs(more, out); + break; + case 0: + /* The next line was blank or a comment: */ + /* keep scanning for a non-comment. */ + line += strlen(line); + goto f; + default: + /* buf isn't a function header, but */ + /* more might be. */ + fputs(buf, out); + strcpy(buf, more); + line = buf; + goto test; + } break; case -1: /* maybe the start of a function */ - line = buf + strlen(buf); if ( line != buf + (bufsize - 1) ) /* overflow check */ - continue; + continue; /* falls through */ default: /* not a function */ - fputs(buf, out); +wl: fputs(buf, out); break; } line = buf; } - if ( line != buf ) fputs(buf, out); + if ( line != buf ) + fputs(buf, out); free(buf); - fclose(out); - fclose(in); + 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; } @@ -188,11 +332,14 @@ skipspace(p, dir) register char *p; register int dir; /* 1 for forward, -1 for backward */ { for ( ; ; ) - { while ( isspace(*p) ) p += dir; - if ( !(*p == '/' && p[dir] == '*') ) break; + { 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?? */ + { if ( *p == 0 ) + return p; /* multi-line comment?? */ p += dir; } p += dir; p += dir; @@ -204,13 +351,15 @@ skipspace(p, dir) * Write blanks over part of a string. * Don't overwrite end-of-line characters. */ -void +int writeblanks(start, end) char *start; char *end; { char *p; for ( p = start; p < end; p++ ) - if ( *p != '\r' && *p != '\n' ) *p = ' '; + if ( *p != '\r' && *p != '\n' ) + *p = ' '; + return 0; } /* @@ -233,24 +382,27 @@ test1(buf) char *bend; char *endfn; int contin; + if ( !isidfirstchar(*p) ) - return 0; /* no name at left margin */ + return 0; /* no name at left margin */ bend = skipspace(buf + strlen(buf) - 1, -1); switch ( *bend ) { - case ';': contin = 0 /*2*/; break; - case ')': contin = 1; break; - case '{': return 0; /* not a function */ - default: contin = -1; + case ';': contin = 0 /*2*/; break; + case ')': contin = 1; break; + case '{': return 0; /* not a function */ + case '}': return 0; /* not a function */ + default: contin = -1; } - while ( isidchar(*p) ) p++; + while ( isidchar(*p) ) + p++; endfn = p; p = skipspace(p, 1); if ( *p++ != '(' ) - return 0; /* not a function */ + return 0; /* not a function */ p = skipspace(p, 1); if ( *p == ')' ) - return 0; /* no parameters */ + return 0; /* no parameters */ /* Check that the apparent function name isn't a keyword. */ /* We only need to check for keywords that could be followed */ /* by a left parenthesis (which, unfortunately, is most of them). */ @@ -264,12 +416,41 @@ test1(buf) char **key = words; char *kp; int len = endfn - buf; + while ( (kp = *key) != 0 ) { if ( strlen(kp) == len && !strncmp(kp, buf, len) ) - return 0; /* name is a keyword */ + return 0; /* name is a keyword */ key++; } } + { + char *id = p; + int len; + /* + * Check for identifier1(identifier2) and not + * identifier1(void). + */ + + while ( isidchar(*p) ) + p++; + len = p - id; + p = skipspace(p, 1); + if ( *p == ')' && (len != 4 || strncmp(id, "void", 4)) ) + return 0; /* not a function */ + } + /* + * If the last significant character was a ), we need to count + * parentheses, because it might be part of a formal parameter + * that is a procedure. + */ + if (contin > 0) { + int level = 0; + + for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1)) + level += (*p == '(' ? 1 : *p == ')' ? -1 : 0); + if (level > 0) + contin = -1; + } return contin; } @@ -282,15 +463,21 @@ convert1(buf, out, header, convert_varargs) int convert_varargs; /* Boolean */ { char *endfn; register char *p; + /* + * The breaks table contains pointers to the beginning and end + * of each argument. + */ char **breaks; unsigned num_breaks = 2; /* for testing */ char **btop; char **bp; char **ap; char *vararg = 0; + /* Pre-ANSI implementations don't agree on whether strchr */ /* is called strchr or index, so we open-code it here. */ - for ( endfn = buf; *(endfn++) != '('; ) ; + for ( endfn = buf; *(endfn++) != '('; ) + ; top: p = endfn; breaks = (char **)malloc(sizeof(char *) * num_breaks * 2); if ( breaks == 0 ) @@ -307,6 +494,7 @@ top: p = endfn; char *lp = NULL; char *rp; char *end = NULL; + if ( bp >= btop ) { /* Filled up break table. */ /* Allocate a bigger one and start over. */ @@ -319,21 +507,21 @@ top: p = endfn; for ( ; end == NULL; p++ ) { switch(*p) { - case ',': + case ',': if ( !level ) end = p; break; - case '(': + case '(': if ( !level ) lp = p; level++; break; - case ')': + case ')': if ( --level < 0 ) end = p; else rp = p; break; - case '/': + case '/': p = skipspace(p, 1) - 1; break; - default: + default: ; } } @@ -348,26 +536,27 @@ top: p = endfn; { p = skipspace(p - 1, -1); switch ( *p ) { - case ']': /* skip array dimension(s) */ - case ')': /* skip procedure args OR name */ + case ']': /* skip array dimension(s) */ + case ')': /* skip procedure args OR name */ { int level = 1; while ( level ) switch ( *--p ) { - case ']': case ')': level++; break; - case '[': case '(': level--; break; - case '/': p = skipspace(p, -1) + 1; break; - default: ; + case ']': case ')': level++; break; + case '[': case '(': level--; break; + case '/': p = skipspace(p, -1) + 1; break; + default: ; } } if ( *p == '(' && *skipspace(p + 1, 1) == '*' ) { /* We found the name being declared */ while ( !isidfirstchar(*p) ) - p = skipspace(p, 1) + 1; + p = skipspace(p, 1) + 1; goto found; } break; - default: goto found; + default: + goto found; } } found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' ) |