quoting fix from Per
[automake.git] / ansi2knr.c
blob64a4578a2fa63d92463a77c2107f397d3ebdd03c
1 /* ansi2knr.c */
2 /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
4 /*
5 ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
6 WARRANTY. No author or distributor accepts responsibility to anyone for the
7 consequences of using it or for whether it serves any particular purpose or
8 works at all, unless he says so in writing. Refer to the GNU General Public
9 License (the "GPL") for full details.
11 Everyone is granted permission to copy, modify and redistribute ansi2knr,
12 but only under the conditions described in the GPL. A copy of this license
13 is supposed to have been given to you along with ansi2knr so you can know
14 your rights and responsibilities. It should be in a file named COPYLEFT.
15 Among other things, the copyright notice and this notice must be preserved
16 on all copies.
18 We explicitly state here what we believe is already implied by the GPL: if
19 the ansi2knr program is distributed as a separate set of sources and a
20 separate executable file which are aggregated on a storage medium together
21 with another program, this in itself does not bring the other program under
22 the GPL, nor does the mere fact that such a program or the procedures for
23 constructing it invoke the ansi2knr executable bring any other part of the
24 program under the GPL.
28 * Usage:
29 ansi2knr input_file [output_file]
30 * If no output_file is supplied, output goes to stdout.
31 * There are no error messages.
33 * ansi2knr recognizes function definitions by seeing a non-keyword
34 * identifier at the left margin, followed by a left parenthesis,
35 * with a right parenthesis as the last character on the line,
36 * and with a left brace as the first token on the following line
37 * (ignoring possible intervening comments).
38 * It will recognize a multi-line header provided that no intervening
39 * line ends with a left or right brace or a semicolon.
40 * These algorithms ignore whitespace and comments, except that
41 * the function name must be the first thing on the line.
42 * The following constructs will confuse it:
43 * - Any other construct that starts at the left margin and
44 * follows the above syntax (such as a macro or function call).
45 * - Some macros that tinker with the syntax of the function header.
49 * The original and principal author of ansi2knr is L. Peter Deutsch
50 * <ghost@aladdin.com>. Other authors are noted in the change history
51 * that follows (in reverse chronological order):
52 lpd 96-01-21 added code to cope with not HAVE_CONFIG_H and with
53 compilers that don't understand void, as suggested by
54 Tom Lane
55 lpd 96-01-15 changed to require that the first non-comment token
56 on the line following a function header be a left brace,
57 to reduce sensitivity to macros, as suggested by Tom Lane
58 <tgl@sss.pgh.pa.us>
59 lpd 95-06-22 removed #ifndefs whose sole purpose was to define
60 undefined preprocessor symbols as 0; changed all #ifdefs
61 for configuration symbols to #ifs
62 lpd 95-04-05 changed copyright notice to make it clear that
63 including ansi2knr in a program does not bring the entire
64 program under the GPL
65 lpd 94-12-18 added conditionals for systems where ctype macros
66 don't handle 8-bit characters properly, suggested by
67 Francois Pinard <pinard@iro.umontreal.ca>;
68 removed --varargs switch (this is now the default)
69 lpd 94-10-10 removed CONFIG_BROKETS conditional
70 lpd 94-07-16 added some conditionals to help GNU `configure',
71 suggested by Francois Pinard <pinard@iro.umontreal.ca>;
72 properly erase prototype args in function parameters,
73 contributed by Jim Avera <jima@netcom.com>;
74 correct error in writeblanks (it shouldn't erase EOLs)
75 lpd 89-xx-xx original version
78 /* Most of the conditionals here are to make ansi2knr work with */
79 /* or without the GNU configure machinery. */
81 #if HAVE_CONFIG_H
82 # include <config.h>
83 #endif
85 #include <stdio.h>
86 #include <ctype.h>
88 #if HAVE_CONFIG_H
91 For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
92 This will define HAVE_CONFIG_H and so, activate the following lines.
95 # if STDC_HEADERS || HAVE_STRING_H
96 # include <string.h>
97 # else
98 # include <strings.h>
99 # endif
101 #else /* not HAVE_CONFIG_H */
103 /* Otherwise do it the hard way */
105 # ifdef BSD
106 # include <strings.h>
107 # else
108 # ifdef VMS
109 extern int strlen(), strncmp();
110 # else
111 # include <string.h>
112 # endif
113 # endif
115 #endif /* not HAVE_CONFIG_H */
117 #if STDC_HEADERS
118 # include <stdlib.h>
119 #else
121 malloc and free should be declared in stdlib.h,
122 but if you've got a K&R compiler, they probably aren't.
124 # ifdef MSDOS
125 # include <malloc.h>
126 # else
127 # ifdef VMS
128 extern char *malloc();
129 extern void free();
130 # else
131 extern char *malloc();
132 extern int free();
133 # endif
134 # endif
136 #endif
139 * The ctype macros don't always handle 8-bit characters correctly.
140 * Compensate for this here.
142 #ifdef isascii
143 # undef HAVE_ISASCII /* just in case */
144 # define HAVE_ISASCII 1
145 #else
146 #endif
147 #if STDC_HEADERS || !HAVE_ISASCII
148 # define is_ascii(c) 1
149 #else
150 # define is_ascii(c) isascii(c)
151 #endif
153 #define is_space(c) (is_ascii(c) && isspace(c))
154 #define is_alpha(c) (is_ascii(c) && isalpha(c))
155 #define is_alnum(c) (is_ascii(c) && isalnum(c))
157 /* Scanning macros */
158 #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
159 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
161 /* Forward references */
162 char *skipspace();
163 int writeblanks();
164 int test1();
165 int convert1();
167 /* The main program */
169 main(argc, argv)
170 int argc;
171 char *argv[];
172 { FILE *in, *out;
173 #define bufsize 5000 /* arbitrary size */
174 char *buf;
175 char *line;
176 char *more;
178 * In previous versions, ansi2knr recognized a --varargs switch.
179 * If this switch was supplied, ansi2knr would attempt to convert
180 * a ... argument to va_alist and va_dcl; if this switch was not
181 * supplied, ansi2knr would simply drop any such arguments.
182 * Now, ansi2knr always does this conversion, and we only
183 * check for this switch for backward compatibility.
185 int convert_varargs = 1;
187 if ( argc > 1 && argv[1][0] == '-' )
188 { if ( !strcmp(argv[1], "--varargs") )
189 { convert_varargs = 1;
190 argc--;
191 argv++;
193 else
194 { fprintf(stderr, "Unrecognized switch: %s\n", argv[1]);
195 exit(1);
198 switch ( argc )
200 default:
201 printf("Usage: ansi2knr input_file [output_file]\n");
202 exit(0);
203 case 2:
204 out = stdout;
205 break;
206 case 3:
207 out = fopen(argv[2], "w");
208 if ( out == NULL )
209 { fprintf(stderr, "Cannot open output file %s\n", argv[2]);
210 exit(1);
213 in = fopen(argv[1], "r");
214 if ( in == NULL )
215 { fprintf(stderr, "Cannot open input file %s\n", argv[1]);
216 exit(1);
218 fprintf(out, "#line 1 \"%s\"\n", argv[1]);
219 buf = malloc(bufsize);
220 line = buf;
221 while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
223 test: line += strlen(line);
224 switch ( test1(buf) )
226 case 2: /* a function header */
227 convert1(buf, out, 1, convert_varargs);
228 break;
229 case 1: /* a function */
230 /* Check for a { at the start of the next line. */
231 more = ++line;
232 f: if ( line >= buf + (bufsize - 1) ) /* overflow check */
233 goto wl;
234 if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
235 goto wl;
236 switch ( *skipspace(more, 1) )
238 case '{':
239 /* Definitely a function header. */
240 convert1(buf, out, 0, convert_varargs);
241 fputs(more, out);
242 break;
243 case 0:
244 /* The next line was blank or a comment: */
245 /* keep scanning for a non-comment. */
246 line += strlen(line);
247 goto f;
248 default:
249 /* buf isn't a function header, but */
250 /* more might be. */
251 fputs(buf, out);
252 strcpy(buf, more);
253 line = buf;
254 goto test;
256 break;
257 case -1: /* maybe the start of a function */
258 if ( line != buf + (bufsize - 1) ) /* overflow check */
259 continue;
260 /* falls through */
261 default: /* not a function */
262 wl: fputs(buf, out);
263 break;
265 line = buf;
267 if ( line != buf )
268 fputs(buf, out);
269 free(buf);
270 fclose(out);
271 fclose(in);
272 return 0;
275 /* Skip over space and comments, in either direction. */
276 char *
277 skipspace(p, dir)
278 register char *p;
279 register int dir; /* 1 for forward, -1 for backward */
280 { for ( ; ; )
281 { while ( is_space(*p) )
282 p += dir;
283 if ( !(*p == '/' && p[dir] == '*') )
284 break;
285 p += dir; p += dir;
286 while ( !(*p == '*' && p[dir] == '/') )
287 { if ( *p == 0 )
288 return p; /* multi-line comment?? */
289 p += dir;
291 p += dir; p += dir;
293 return p;
297 * Write blanks over part of a string.
298 * Don't overwrite end-of-line characters.
301 writeblanks(start, end)
302 char *start;
303 char *end;
304 { char *p;
305 for ( p = start; p < end; p++ )
306 if ( *p != '\r' && *p != '\n' )
307 *p = ' ';
308 return 0;
312 * Test whether the string in buf is a function definition.
313 * The string may contain and/or end with a newline.
314 * Return as follows:
315 * 0 - definitely not a function definition;
316 * 1 - definitely a function definition;
317 * 2 - definitely a function prototype (NOT USED);
318 * -1 - may be the beginning of a function definition,
319 * append another line and look again.
320 * The reason we don't attempt to convert function prototypes is that
321 * Ghostscript's declaration-generating macros look too much like
322 * prototypes, and confuse the algorithms.
325 test1(buf)
326 char *buf;
327 { register char *p = buf;
328 char *bend;
329 char *endfn;
330 int contin;
332 if ( !isidfirstchar(*p) )
333 return 0; /* no name at left margin */
334 bend = skipspace(buf + strlen(buf) - 1, -1);
335 switch ( *bend )
337 case ';': contin = 0 /*2*/; break;
338 case ')': contin = 1; break;
339 case '{': return 0; /* not a function */
340 case '}': return 0; /* not a function */
341 default: contin = -1;
343 while ( isidchar(*p) )
344 p++;
345 endfn = p;
346 p = skipspace(p, 1);
347 if ( *p++ != '(' )
348 return 0; /* not a function */
349 p = skipspace(p, 1);
350 if ( *p == ')' )
351 return 0; /* no parameters */
352 /* Check that the apparent function name isn't a keyword. */
353 /* We only need to check for keywords that could be followed */
354 /* by a left parenthesis (which, unfortunately, is most of them). */
355 { static char *words[] =
356 { "asm", "auto", "case", "char", "const", "double",
357 "extern", "float", "for", "if", "int", "long",
358 "register", "return", "short", "signed", "sizeof",
359 "static", "switch", "typedef", "unsigned",
360 "void", "volatile", "while", 0
362 char **key = words;
363 char *kp;
364 int len = endfn - buf;
366 while ( (kp = *key) != 0 )
367 { if ( strlen(kp) == len && !strncmp(kp, buf, len) )
368 return 0; /* name is a keyword */
369 key++;
372 return contin;
375 /* Convert a recognized function definition or header to K&R syntax. */
377 convert1(buf, out, header, convert_varargs)
378 char *buf;
379 FILE *out;
380 int header; /* Boolean */
381 int convert_varargs; /* Boolean */
382 { char *endfn;
383 register char *p;
385 * The breaks table contains pointers to the beginning and end
386 * of each argument.
388 char **breaks;
389 unsigned num_breaks = 2; /* for testing */
390 char **btop;
391 char **bp;
392 char **ap;
393 char *vararg = 0;
395 /* Pre-ANSI implementations don't agree on whether strchr */
396 /* is called strchr or index, so we open-code it here. */
397 for ( endfn = buf; *(endfn++) != '('; )
399 top: p = endfn;
400 breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
401 if ( breaks == 0 )
402 { /* Couldn't allocate break table, give up */
403 fprintf(stderr, "Unable to allocate break table!\n");
404 fputs(buf, out);
405 return -1;
407 btop = breaks + num_breaks * 2 - 2;
408 bp = breaks;
409 /* Parse the argument list */
411 { int level = 0;
412 char *lp = NULL;
413 char *rp;
414 char *end = NULL;
416 if ( bp >= btop )
417 { /* Filled up break table. */
418 /* Allocate a bigger one and start over. */
419 free((char *)breaks);
420 num_breaks <<= 1;
421 goto top;
423 *bp++ = p;
424 /* Find the end of the argument */
425 for ( ; end == NULL; p++ )
426 { switch(*p)
428 case ',':
429 if ( !level ) end = p;
430 break;
431 case '(':
432 if ( !level ) lp = p;
433 level++;
434 break;
435 case ')':
436 if ( --level < 0 ) end = p;
437 else rp = p;
438 break;
439 case '/':
440 p = skipspace(p, 1) - 1;
441 break;
442 default:
446 /* Erase any embedded prototype parameters. */
447 if ( lp )
448 writeblanks(lp + 1, rp);
449 p--; /* back up over terminator */
450 /* Find the name being declared. */
451 /* This is complicated because of procedure and */
452 /* array modifiers. */
453 for ( ; ; )
454 { p = skipspace(p - 1, -1);
455 switch ( *p )
457 case ']': /* skip array dimension(s) */
458 case ')': /* skip procedure args OR name */
459 { int level = 1;
460 while ( level )
461 switch ( *--p )
463 case ']': case ')': level++; break;
464 case '[': case '(': level--; break;
465 case '/': p = skipspace(p, -1) + 1; break;
466 default: ;
469 if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
470 { /* We found the name being declared */
471 while ( !isidfirstchar(*p) )
472 p = skipspace(p, 1) + 1;
473 goto found;
475 break;
476 default:
477 goto found;
480 found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
481 { if ( convert_varargs )
482 { *bp++ = "va_alist";
483 vararg = p-2;
485 else
486 { p++;
487 if ( bp == breaks + 1 ) /* sole argument */
488 writeblanks(breaks[0], p);
489 else
490 writeblanks(bp[-1] - 1, p);
491 bp--;
494 else
495 { while ( isidchar(*p) ) p--;
496 *bp++ = p+1;
498 p = end;
500 while ( *p++ == ',' );
501 *bp = p;
502 /* Make a special check for 'void' arglist */
503 if ( bp == breaks+2 )
504 { p = skipspace(breaks[0], 1);
505 if ( !strncmp(p, "void", 4) )
506 { p = skipspace(p+4, 1);
507 if ( p == breaks[2] - 1 )
508 { bp = breaks; /* yup, pretend arglist is empty */
509 writeblanks(breaks[0], p + 1);
513 /* Put out the function name and left parenthesis. */
514 p = buf;
515 while ( p != endfn ) putc(*p, out), p++;
516 /* Put out the declaration. */
517 if ( header )
518 { fputs(");", out);
519 for ( p = breaks[0]; *p; p++ )
520 if ( *p == '\r' || *p == '\n' )
521 putc(*p, out);
523 else
524 { for ( ap = breaks+1; ap < bp; ap += 2 )
525 { p = *ap;
526 while ( isidchar(*p) )
527 putc(*p, out), p++;
528 if ( ap < bp - 1 )
529 fputs(", ", out);
531 fputs(") ", out);
532 /* Put out the argument declarations */
533 for ( ap = breaks+2; ap <= bp; ap += 2 )
534 (*ap)[-1] = ';';
535 if ( vararg != 0 )
536 { *vararg = 0;
537 fputs(breaks[0], out); /* any prior args */
538 fputs("va_dcl", out); /* the final arg */
539 fputs(bp[0], out);
541 else
542 fputs(breaks[0], out);
544 free((char *)breaks);
545 return 0;