Annotation of OpenXM_contrib/gmp/ansi2knr.c, Revision 1.1.1.1
1.1 maekawa 1: /* Copyright (C) 1989, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. */
2:
3: /*$Id: ansi2knr.c,v 1.1 2000/04/22 23:32:38 kevin Exp $*/
4: /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
5:
6: /*
7: ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
8: WARRANTY. No author or distributor accepts responsibility to anyone for the
9: consequences of using it or for whether it serves any particular purpose or
10: works at all, unless he says so in writing. Refer to the GNU General Public
11: License (the "GPL") for full details.
12:
13: Everyone is granted permission to copy, modify and redistribute ansi2knr,
14: but only under the conditions described in the GPL. A copy of this license
15: is supposed to have been given to you along with ansi2knr so you can know
16: your rights and responsibilities. It should be in a file named COPYLEFT,
17: or, if there is no file named COPYLEFT, a file named COPYING. Among other
18: things, the copyright notice and this notice must be preserved on all
19: copies.
20:
21: We explicitly state here what we believe is already implied by the GPL: if
22: the ansi2knr program is distributed as a separate set of sources and a
23: separate executable file which are aggregated on a storage medium together
24: with another program, this in itself does not bring the other program under
25: the GPL, nor does the mere fact that such a program or the procedures for
26: constructing it invoke the ansi2knr executable bring any other part of the
27: program under the GPL.
28: */
29:
30: /*
31: * Usage:
32: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]
33: * --filename provides the file name for the #line directive in the output,
34: * overriding input_file (if present).
35: * If no input_file is supplied, input is read from stdin.
36: * If no output_file is supplied, output goes to stdout.
37: * There are no error messages.
38: *
39: * ansi2knr recognizes function definitions by seeing a non-keyword
40: * identifier at the left margin, followed by a left parenthesis,
41: * with a right parenthesis as the last character on the line,
42: * and with a left brace as the first token on the following line
43: * (ignoring possible intervening comments), except that a line
44: * consisting of only
45: * identifier1(identifier2)
46: * will not be considered a function definition unless identifier2 is
47: * the word "void", and a line consisting of
48: * identifier1(identifier2, <<arbitrary>>)
49: * will not be considered a function definition.
50: * ansi2knr will recognize a multi-line header provided
51: * that no intervening line ends with a left or right brace or a semicolon.
52: * These algorithms ignore whitespace and comments, except that
53: * the function name must be the first thing on the line.
54: * The following constructs will confuse it:
55: * - Any other construct that starts at the left margin and
56: * follows the above syntax (such as a macro or function call).
57: * - Some macros that tinker with the syntax of function headers.
58: */
59:
60: /*
61: * The original and principal author of ansi2knr is L. Peter Deutsch
62: * <ghost@aladdin.com>. Other authors are noted in the change history
63: * that follows (in reverse chronological order):
64: lpd 1999-04-12 added minor fixes from Pavel Roskin
65: <pavel_roskin@geocities.com> for clean compilation with
66: gcc -W -Wall
67: lpd 1999-03-22 added hack to recognize lines consisting of
68: identifier1(identifier2, xxx) as *not* being procedures
69: lpd 1999-02-03 made indentation of preprocessor commands consistent
70: lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
71: endless loop; quoted strings within an argument list
72: confused the parser
73: lpd 1999-01-24 added a check for write errors on the output,
74: suggested by Jim Meyering <meyering@ascend.com>
75: lpd 1998-11-09 added further hack to recognize identifier(void)
76: as being a procedure
77: lpd 1998-10-23 added hack to recognize lines consisting of
78: identifier1(identifier2) as *not* being procedures
79: lpd 1997-12-08 made input_file optional; only closes input and/or
80: output file if not stdin or stdout respectively; prints
81: usage message on stderr rather than stdout; adds
82: --filename switch (changes suggested by
83: <ceder@lysator.liu.se>)
84: lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
85: compilers that don't understand void, as suggested by
86: Tom Lane
87: lpd 1996-01-15 changed to require that the first non-comment token
88: on the line following a function header be a left brace,
89: to reduce sensitivity to macros, as suggested by Tom Lane
90: <tgl@sss.pgh.pa.us>
91: lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
92: undefined preprocessor symbols as 0; changed all #ifdefs
93: for configuration symbols to #ifs
94: lpd 1995-04-05 changed copyright notice to make it clear that
95: including ansi2knr in a program does not bring the entire
96: program under the GPL
97: lpd 1994-12-18 added conditionals for systems where ctype macros
98: don't handle 8-bit characters properly, suggested by
99: Francois Pinard <pinard@iro.umontreal.ca>;
100: removed --varargs switch (this is now the default)
101: lpd 1994-10-10 removed CONFIG_BROKETS conditional
102: lpd 1994-07-16 added some conditionals to help GNU `configure',
103: suggested by Francois Pinard <pinard@iro.umontreal.ca>;
104: properly erase prototype args in function parameters,
105: contributed by Jim Avera <jima@netcom.com>;
106: correct error in writeblanks (it shouldn't erase EOLs)
107: lpd 1989-xx-xx original version
108: */
109:
110: /* Most of the conditionals here are to make ansi2knr work with */
111: /* or without the GNU configure machinery. */
112:
113: #if HAVE_CONFIG_H
114: # include <config.h>
115: #endif
116:
117: #include <stdio.h>
118: #include <ctype.h>
119:
120: #if HAVE_CONFIG_H
121:
122: /*
123: For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
124: This will define HAVE_CONFIG_H and so, activate the following lines.
125: */
126:
127: # if STDC_HEADERS || HAVE_STRING_H
128: # include <string.h>
129: # else
130: # include <strings.h>
131: # endif
132:
133: #else /* not HAVE_CONFIG_H */
134:
135: /* Otherwise do it the hard way */
136:
137: # ifdef BSD
138: # include <strings.h>
139: # else
140: # ifdef VMS
141: extern int strlen(), strncmp();
142: # else
143: # include <string.h>
144: # endif
145: # endif
146:
147: #endif /* not HAVE_CONFIG_H */
148:
149: #if STDC_HEADERS
150: # include <stdlib.h>
151: #else
152: /*
153: malloc and free should be declared in stdlib.h,
154: but if you've got a K&R compiler, they probably aren't.
155: */
156: # ifdef MSDOS
157: # include <malloc.h>
158: # else
159: # ifdef VMS
160: extern char *malloc();
161: extern void free();
162: # else
163: extern char *malloc();
164: extern int free();
165: # endif
166: # endif
167:
168: #endif
169:
170: /* Define NULL (for *very* old compilers). */
171: #ifndef NULL
172: # define NULL (0)
173: #endif
174:
175: /*
176: * The ctype macros don't always handle 8-bit characters correctly.
177: * Compensate for this here.
178: */
179: #ifdef isascii
180: # undef HAVE_ISASCII /* just in case */
181: # define HAVE_ISASCII 1
182: #else
183: #endif
184: #if STDC_HEADERS || !HAVE_ISASCII
185: # define is_ascii(c) 1
186: #else
187: # define is_ascii(c) isascii(c)
188: #endif
189:
190: #define is_space(c) (is_ascii(c) && isspace(c))
191: #define is_alpha(c) (is_ascii(c) && isalpha(c))
192: #define is_alnum(c) (is_ascii(c) && isalnum(c))
193:
194: /* Scanning macros */
195: #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
196: #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
197:
198: /* Forward references */
199: char *skipspace();
200: char *scanstring();
201: int writeblanks();
202: int test1();
203: int convert1();
204:
205: /* The main program */
206: int
207: main(argc, argv)
208: int argc;
209: char *argv[];
210: { FILE *in = stdin;
211: FILE *out = stdout;
212: char *filename = 0;
213: char *program_name = argv[0];
214: char *output_name = 0;
215: #define bufsize 5000 /* arbitrary size */
216: char *buf;
217: char *line;
218: char *more;
219: char *usage =
220: "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
221: /*
222: * In previous versions, ansi2knr recognized a --varargs switch.
223: * If this switch was supplied, ansi2knr would attempt to convert
224: * a ... argument to va_alist and va_dcl; if this switch was not
225: * supplied, ansi2knr would simply drop any such arguments.
226: * Now, ansi2knr always does this conversion, and we only
227: * check for this switch for backward compatibility.
228: */
229: int convert_varargs = 1;
230: int output_error;
231:
232: while ( argc > 1 && argv[1][0] == '-' ) {
233: if ( !strcmp(argv[1], "--varargs") ) {
234: convert_varargs = 1;
235: argc--;
236: argv++;
237: continue;
238: }
239: if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
240: filename = argv[2];
241: argc -= 2;
242: argv += 2;
243: continue;
244: }
245: fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
246: argv[1]);
247: fprintf(stderr, usage);
248: exit(1);
249: }
250: switch ( argc )
251: {
252: default:
253: fprintf(stderr, usage);
254: exit(0);
255: case 3:
256: output_name = argv[2];
257: out = fopen(output_name, "w");
258: if ( out == NULL ) {
259: fprintf(stderr, "%s: Cannot open output file %s\n",
260: program_name, output_name);
261: exit(1);
262: }
263: /* falls through */
264: case 2:
265: in = fopen(argv[1], "r");
266: if ( in == NULL ) {
267: fprintf(stderr, "%s: Cannot open input file %s\n",
268: program_name, argv[1]);
269: exit(1);
270: }
271: if ( filename == 0 )
272: filename = argv[1];
273: /* falls through */
274: case 1:
275: break;
276: }
277: if ( filename )
278: fprintf(out, "#line 1 \"%s\"\n", filename);
279: buf = malloc(bufsize);
280: if ( buf == NULL )
281: {
282: fprintf(stderr, "Unable to allocate read buffer!\n");
283: exit(1);
284: }
285: line = buf;
286: while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
287: {
288: test: line += strlen(line);
289: switch ( test1(buf) )
290: {
291: case 2: /* a function header */
292: convert1(buf, out, 1, convert_varargs);
293: break;
294: case 1: /* a function */
295: /* Check for a { at the start of the next line. */
296: more = ++line;
297: f: if ( line >= buf + (bufsize - 1) ) /* overflow check */
298: goto wl;
299: if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
300: goto wl;
301: switch ( *skipspace(more, 1) )
302: {
303: case '{':
304: /* Definitely a function header. */
305: convert1(buf, out, 0, convert_varargs);
306: fputs(more, out);
307: break;
308: case 0:
309: /* The next line was blank or a comment: */
310: /* keep scanning for a non-comment. */
311: line += strlen(line);
312: goto f;
313: default:
314: /* buf isn't a function header, but */
315: /* more might be. */
316: fputs(buf, out);
317: strcpy(buf, more);
318: line = buf;
319: goto test;
320: }
321: break;
322: case -1: /* maybe the start of a function */
323: if ( line != buf + (bufsize - 1) ) /* overflow check */
324: continue;
325: /* falls through */
326: default: /* not a function */
327: wl: fputs(buf, out);
328: break;
329: }
330: line = buf;
331: }
332: if ( line != buf )
333: fputs(buf, out);
334: free(buf);
335: if ( output_name ) {
336: output_error = ferror(out);
337: output_error |= fclose(out);
338: } else { /* out == stdout */
339: fflush(out);
340: output_error = ferror(out);
341: }
342: if ( output_error ) {
343: fprintf(stderr, "%s: error writing to %s\n", program_name,
344: (output_name ? output_name : "stdout"));
345: exit(1);
346: }
347: if ( in != stdin )
348: fclose(in);
349: return 0;
350: }
351:
352: /* Skip over whitespace and comments, in either direction. */
353: char *
354: skipspace(p, dir)
355: register char *p;
356: register int dir; /* 1 for forward, -1 for backward */
357: { for ( ; ; )
358: { while ( is_space(*p) )
359: p += dir;
360: if ( !(*p == '/' && p[dir] == '*') )
361: break;
362: p += dir; p += dir;
363: while ( !(*p == '*' && p[dir] == '/') )
364: { if ( *p == 0 )
365: return p; /* multi-line comment?? */
366: p += dir;
367: }
368: p += dir; p += dir;
369: }
370: return p;
371: }
372:
373: /* Scan over a quoted string, in either direction. */
374: char *
375: scanstring(p, dir)
376: register char *p;
377: register int dir;
378: {
379: for (p += dir; ; p += dir)
380: if (*p == '"' && p[-dir] != '\\')
381: return p + dir;
382: }
383:
384: /*
385: * Write blanks over part of a string.
386: * Don't overwrite end-of-line characters.
387: */
388: int
389: writeblanks(start, end)
390: char *start;
391: char *end;
392: { char *p;
393: for ( p = start; p < end; p++ )
394: if ( *p != '\r' && *p != '\n' )
395: *p = ' ';
396: return 0;
397: }
398:
399: /*
400: * Test whether the string in buf is a function definition.
401: * The string may contain and/or end with a newline.
402: * Return as follows:
403: * 0 - definitely not a function definition;
404: * 1 - definitely a function definition;
405: * 2 - definitely a function prototype (NOT USED);
406: * -1 - may be the beginning of a function definition,
407: * append another line and look again.
408: * The reason we don't attempt to convert function prototypes is that
409: * Ghostscript's declaration-generating macros look too much like
410: * prototypes, and confuse the algorithms.
411: */
412: int
413: test1(buf)
414: char *buf;
415: { register char *p = buf;
416: char *bend;
417: char *endfn;
418: int contin;
419:
420: if ( !isidfirstchar(*p) )
421: return 0; /* no name at left margin */
422: bend = skipspace(buf + strlen(buf) - 1, -1);
423: switch ( *bend )
424: {
425: case ';': contin = 0 /*2*/; break;
426: case ')': contin = 1; break;
427: case '{': return 0; /* not a function */
428: case '}': return 0; /* not a function */
429: default: contin = -1;
430: }
431: while ( isidchar(*p) )
432: p++;
433: endfn = p;
434: p = skipspace(p, 1);
435: if ( *p++ != '(' )
436: return 0; /* not a function */
437: p = skipspace(p, 1);
438: if ( *p == ')' )
439: return 0; /* no parameters */
440: /* Check that the apparent function name isn't a keyword. */
441: /* We only need to check for keywords that could be followed */
442: /* by a left parenthesis (which, unfortunately, is most of them). */
443: { static char *words[] =
444: { "asm", "auto", "case", "char", "const", "double",
445: "extern", "float", "for", "if", "int", "long",
446: "register", "return", "short", "signed", "sizeof",
447: "static", "switch", "typedef", "unsigned",
448: "void", "volatile", "while", 0
449: };
450: char **key = words;
451: char *kp;
452: unsigned len = endfn - buf;
453:
454: while ( (kp = *key) != 0 )
455: { if ( strlen(kp) == len && !strncmp(kp, buf, len) )
456: return 0; /* name is a keyword */
457: key++;
458: }
459: }
460: {
461: char *id = p;
462: int len;
463: /*
464: * Check for identifier1(identifier2) and not
465: * identifier1(void), or identifier1(identifier2, xxxx).
466: */
467:
468: while ( isidchar(*p) )
469: p++;
470: len = p - id;
471: p = skipspace(p, 1);
472: if (*p == ',' ||
473: (*p == ')' && (len != 4 || strncmp(id, "void", 4)))
474: )
475: return 0; /* not a function */
476: }
477: /*
478: * If the last significant character was a ), we need to count
479: * parentheses, because it might be part of a formal parameter
480: * that is a procedure.
481: */
482: if (contin > 0) {
483: int level = 0;
484:
485: for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
486: level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
487: if (level > 0)
488: contin = -1;
489: }
490: return contin;
491: }
492:
493: /* Convert a recognized function definition or header to K&R syntax. */
494: int
495: convert1(buf, out, header, convert_varargs)
496: char *buf;
497: FILE *out;
498: int header; /* Boolean */
499: int convert_varargs; /* Boolean */
500: { char *endfn;
501: register char *p;
502: /*
503: * The breaks table contains pointers to the beginning and end
504: * of each argument.
505: */
506: char **breaks;
507: unsigned num_breaks = 2; /* for testing */
508: char **btop;
509: char **bp;
510: char **ap;
511: char *vararg = 0;
512:
513: /* Pre-ANSI implementations don't agree on whether strchr */
514: /* is called strchr or index, so we open-code it here. */
515: for ( endfn = buf; *(endfn++) != '('; )
516: ;
517: top: p = endfn;
518: breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
519: if ( breaks == NULL )
520: { /* Couldn't allocate break table, give up */
521: fprintf(stderr, "Unable to allocate break table!\n");
522: fputs(buf, out);
523: return -1;
524: }
525: btop = breaks + num_breaks * 2 - 2;
526: bp = breaks;
527: /* Parse the argument list */
528: do
529: { int level = 0;
530: char *lp = NULL;
531: char *rp = NULL;
532: char *end = NULL;
533:
534: if ( bp >= btop )
535: { /* Filled up break table. */
536: /* Allocate a bigger one and start over. */
537: free((char *)breaks);
538: num_breaks <<= 1;
539: goto top;
540: }
541: *bp++ = p;
542: /* Find the end of the argument */
543: for ( ; end == NULL; p++ )
544: { switch(*p)
545: {
546: case ',':
547: if ( !level ) end = p;
548: break;
549: case '(':
550: if ( !level ) lp = p;
551: level++;
552: break;
553: case ')':
554: if ( --level < 0 ) end = p;
555: else rp = p;
556: break;
557: case '/':
558: if (p[1] == '*')
559: p = skipspace(p, 1) - 1;
560: break;
561: case '"':
562: p = scanstring(p, 1) - 1;
563: break;
564: default:
565: ;
566: }
567: }
568: /* Erase any embedded prototype parameters. */
569: if ( lp && rp )
570: writeblanks(lp + 1, rp);
571: p--; /* back up over terminator */
572: /* Find the name being declared. */
573: /* This is complicated because of procedure and */
574: /* array modifiers. */
575: for ( ; ; )
576: { p = skipspace(p - 1, -1);
577: switch ( *p )
578: {
579: case ']': /* skip array dimension(s) */
580: case ')': /* skip procedure args OR name */
581: { int level = 1;
582: while ( level )
583: switch ( *--p )
584: {
585: case ']': case ')':
586: level++;
587: break;
588: case '[': case '(':
589: level--;
590: break;
591: case '/':
592: if (p > buf && p[-1] == '*')
593: p = skipspace(p, -1) + 1;
594: break;
595: case '"':
596: p = scanstring(p, -1) + 1;
597: break;
598: default: ;
599: }
600: }
601: if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
602: { /* We found the name being declared */
603: while ( !isidfirstchar(*p) )
604: p = skipspace(p, 1) + 1;
605: goto found;
606: }
607: break;
608: default:
609: goto found;
610: }
611: }
612: found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
613: { if ( convert_varargs )
614: { *bp++ = "va_alist";
615: vararg = p-2;
616: }
617: else
618: { p++;
619: if ( bp == breaks + 1 ) /* sole argument */
620: writeblanks(breaks[0], p);
621: else
622: writeblanks(bp[-1] - 1, p);
623: bp--;
624: }
625: }
626: else
627: { while ( isidchar(*p) ) p--;
628: *bp++ = p+1;
629: }
630: p = end;
631: }
632: while ( *p++ == ',' );
633: *bp = p;
634: /* Make a special check for 'void' arglist */
635: if ( bp == breaks+2 )
636: { p = skipspace(breaks[0], 1);
637: if ( !strncmp(p, "void", 4) )
638: { p = skipspace(p+4, 1);
639: if ( p == breaks[2] - 1 )
640: { bp = breaks; /* yup, pretend arglist is empty */
641: writeblanks(breaks[0], p + 1);
642: }
643: }
644: }
645: /* Put out the function name and left parenthesis. */
646: p = buf;
647: while ( p != endfn ) putc(*p, out), p++;
648: /* Put out the declaration. */
649: if ( header )
650: { fputs(");", out);
651: for ( p = breaks[0]; *p; p++ )
652: if ( *p == '\r' || *p == '\n' )
653: putc(*p, out);
654: }
655: else
656: { for ( ap = breaks+1; ap < bp; ap += 2 )
657: { p = *ap;
658: while ( isidchar(*p) )
659: putc(*p, out), p++;
660: if ( ap < bp - 1 )
661: fputs(", ", out);
662: }
663: fputs(") ", out);
664: /* Put out the argument declarations */
665: for ( ap = breaks+2; ap <= bp; ap += 2 )
666: (*ap)[-1] = ';';
667: if ( vararg != 0 )
668: { *vararg = 0;
669: fputs(breaks[0], out); /* any prior args */
670: fputs("va_dcl", out); /* the final arg */
671: fputs(bp[0], out);
672: }
673: else
674: fputs(breaks[0], out);
675: }
676: free((char *)breaks);
677: return 0;
678: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>