* suffix.test: New file.
[automake.git] / ansi2knr.c
blob061f696782f0ae9fa82ce31ec08a2a39df55d7a2
1 /* Copyright (C) 1989, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. */
3 /*$Id: ansi2knr.c,v 1.11 1999/01/29 14:22:15 tromey Exp $*/
4 /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
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.
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.
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.
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.
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". ansi2knr will recognize a multi-line header provided
48 * that no intervening line ends with a left or right brace or a semicolon.
49 * These algorithms ignore whitespace and comments, except that
50 * the function name must be the first thing on the line.
51 * The following constructs will confuse it:
52 * - Any other construct that starts at the left margin and
53 * follows the above syntax (such as a macro or function call).
54 * - Some macros that tinker with the syntax of function headers.
58 * The original and principal author of ansi2knr is L. Peter Deutsch
59 * <ghost@aladdin.com>. Other authors are noted in the change history
60 * that follows (in reverse chronological order):
61 lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
62 endless loop; quoted strings within an argument list
63 confused the parser
64 lpd 1999-01-24 added a check for write errors on the output,
65 suggested by Jim Meyering <meyering@ascend.com>
66 lpd 1998-11-09 added further hack to recognize identifier(void)
67 as being a procedure
68 lpd 1998-10-23 added hack to recognize lines consisting of
69 identifier1(identifier2) as *not* being procedures
70 lpd 1997-12-08 made input_file optional; only closes input and/or
71 output file if not stdin or stdout respectively; prints
72 usage message on stderr rather than stdout; adds
73 --filename switch (changes suggested by
74 <ceder@lysator.liu.se>)
75 lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
76 compilers that don't understand void, as suggested by
77 Tom Lane
78 lpd 1996-01-15 changed to require that the first non-comment token
79 on the line following a function header be a left brace,
80 to reduce sensitivity to macros, as suggested by Tom Lane
81 <tgl@sss.pgh.pa.us>
82 lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
83 undefined preprocessor symbols as 0; changed all #ifdefs
84 for configuration symbols to #ifs
85 lpd 1995-04-05 changed copyright notice to make it clear that
86 including ansi2knr in a program does not bring the entire
87 program under the GPL
88 lpd 1994-12-18 added conditionals for systems where ctype macros
89 don't handle 8-bit characters properly, suggested by
90 Francois Pinard <pinard@iro.umontreal.ca>;
91 removed --varargs switch (this is now the default)
92 lpd 1994-10-10 removed CONFIG_BROKETS conditional
93 lpd 1994-07-16 added some conditionals to help GNU `configure',
94 suggested by Francois Pinard <pinard@iro.umontreal.ca>;
95 properly erase prototype args in function parameters,
96 contributed by Jim Avera <jima@netcom.com>;
97 correct error in writeblanks (it shouldn't erase EOLs)
98 lpd 1989-xx-xx original version
101 /* Most of the conditionals here are to make ansi2knr work with */
102 /* or without the GNU configure machinery. */
104 #if HAVE_CONFIG_H
105 # include <config.h>
106 #endif
108 #include <stdio.h>
109 #include <ctype.h>
111 #if HAVE_CONFIG_H
114 For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
115 This will define HAVE_CONFIG_H and so, activate the following lines.
118 # if STDC_HEADERS || HAVE_STRING_H
119 # include <string.h>
120 # else
121 # include <strings.h>
122 # endif
124 #else /* not HAVE_CONFIG_H */
126 /* Otherwise do it the hard way */
128 # ifdef BSD
129 # include <strings.h>
130 # else
131 # ifdef VMS
132 extern int strlen(), strncmp();
133 # else
134 # include <string.h>
135 # endif
136 # endif
138 #endif /* not HAVE_CONFIG_H */
140 #if STDC_HEADERS
141 # include <stdlib.h>
142 #else
144 malloc and free should be declared in stdlib.h,
145 but if you've got a K&R compiler, they probably aren't.
147 # ifdef MSDOS
148 # include <malloc.h>
149 # else
150 # ifdef VMS
151 extern char *malloc();
152 extern void free();
153 # else
154 extern char *malloc();
155 extern int free();
156 # endif
157 # endif
159 #endif
162 * The ctype macros don't always handle 8-bit characters correctly.
163 * Compensate for this here.
165 #ifdef isascii
166 # undef HAVE_ISASCII /* just in case */
167 # define HAVE_ISASCII 1
168 #else
169 #endif
170 #if STDC_HEADERS || !HAVE_ISASCII
171 # define is_ascii(c) 1
172 #else
173 # define is_ascii(c) isascii(c)
174 #endif
176 #define is_space(c) (is_ascii(c) && isspace(c))
177 #define is_alpha(c) (is_ascii(c) && isalpha(c))
178 #define is_alnum(c) (is_ascii(c) && isalnum(c))
180 /* Scanning macros */
181 #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
182 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
184 /* Forward references */
185 char *skipspace();
186 char *scanstring();
187 int writeblanks();
188 int test1();
189 int convert1();
191 /* The main program */
193 main(argc, argv)
194 int argc;
195 char *argv[];
196 { FILE *in = stdin;
197 FILE *out = stdout;
198 char *filename = 0;
199 char *program_name = argv[0];
200 char *output_name = 0;
201 #define bufsize 5000 /* arbitrary size */
202 char *buf;
203 char *line;
204 char *more;
205 char *usage =
206 "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
208 * In previous versions, ansi2knr recognized a --varargs switch.
209 * If this switch was supplied, ansi2knr would attempt to convert
210 * a ... argument to va_alist and va_dcl; if this switch was not
211 * supplied, ansi2knr would simply drop any such arguments.
212 * Now, ansi2knr always does this conversion, and we only
213 * check for this switch for backward compatibility.
215 int convert_varargs = 1;
216 int output_error;
218 while ( argc > 1 && argv[1][0] == '-' ) {
219 if ( !strcmp(argv[1], "--varargs") ) {
220 convert_varargs = 1;
221 argc--;
222 argv++;
223 continue;
225 if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
226 filename = argv[2];
227 argc -= 2;
228 argv += 2;
229 continue;
231 fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
232 argv[1]);
233 fprintf(stderr, usage);
234 exit(1);
236 switch ( argc )
238 default:
239 fprintf(stderr, usage);
240 exit(0);
241 case 3:
242 output_name = argv[2];
243 out = fopen(output_name, "w");
244 if ( out == NULL ) {
245 fprintf(stderr, "%s: Cannot open output file %s\n",
246 program_name, output_name);
247 exit(1);
249 /* falls through */
250 case 2:
251 in = fopen(argv[1], "r");
252 if ( in == NULL ) {
253 fprintf(stderr, "%s: Cannot open input file %s\n",
254 program_name, argv[1]);
255 exit(1);
257 if ( filename == 0 )
258 filename = argv[1];
259 /* falls through */
260 case 1:
261 break;
263 if ( filename )
264 fprintf(out, "#line 1 \"%s\"\n", filename);
265 buf = malloc(bufsize);
266 line = buf;
267 while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
269 test: line += strlen(line);
270 switch ( test1(buf) )
272 case 2: /* a function header */
273 convert1(buf, out, 1, convert_varargs);
274 break;
275 case 1: /* a function */
276 /* Check for a { at the start of the next line. */
277 more = ++line;
278 f: if ( line >= buf + (bufsize - 1) ) /* overflow check */
279 goto wl;
280 if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
281 goto wl;
282 switch ( *skipspace(more, 1) )
284 case '{':
285 /* Definitely a function header. */
286 convert1(buf, out, 0, convert_varargs);
287 fputs(more, out);
288 break;
289 case 0:
290 /* The next line was blank or a comment: */
291 /* keep scanning for a non-comment. */
292 line += strlen(line);
293 goto f;
294 default:
295 /* buf isn't a function header, but */
296 /* more might be. */
297 fputs(buf, out);
298 strcpy(buf, more);
299 line = buf;
300 goto test;
302 break;
303 case -1: /* maybe the start of a function */
304 if ( line != buf + (bufsize - 1) ) /* overflow check */
305 continue;
306 /* falls through */
307 default: /* not a function */
308 wl: fputs(buf, out);
309 break;
311 line = buf;
313 if ( line != buf )
314 fputs(buf, out);
315 free(buf);
316 if ( output_name ) {
317 output_error = ferror(out);
318 output_error |= fclose(out);
319 } else { /* out == stdout */
320 fflush(out);
321 output_error = ferror(out);
323 if ( output_error ) {
324 fprintf(stderr, "%s: error writing to %s\n", program_name,
325 (output_name ? output_name : "stdout"));
326 exit(1);
328 if ( in != stdin )
329 fclose(in);
330 return 0;
333 /* Skip over whitespace and comments, in either direction. */
334 char *
335 skipspace(p, dir)
336 register char *p;
337 register int dir; /* 1 for forward, -1 for backward */
338 { for ( ; ; )
339 { while ( is_space(*p) )
340 p += dir;
341 if ( !(*p == '/' && p[dir] == '*') )
342 break;
343 p += dir; p += dir;
344 while ( !(*p == '*' && p[dir] == '/') )
345 { if ( *p == 0 )
346 return p; /* multi-line comment?? */
347 p += dir;
349 p += dir; p += dir;
351 return p;
354 /* Scan over a quoted string, in either direction. */
355 char *
356 scanstring(p, dir)
357 register char *p;
358 register int dir;
360 for (p += dir; ; p += dir)
361 if (*p == '"' && p[-dir] != '\\')
362 return p + dir;
366 * Write blanks over part of a string.
367 * Don't overwrite end-of-line characters.
370 writeblanks(start, end)
371 char *start;
372 char *end;
373 { char *p;
374 for ( p = start; p < end; p++ )
375 if ( *p != '\r' && *p != '\n' )
376 *p = ' ';
377 return 0;
381 * Test whether the string in buf is a function definition.
382 * The string may contain and/or end with a newline.
383 * Return as follows:
384 * 0 - definitely not a function definition;
385 * 1 - definitely a function definition;
386 * 2 - definitely a function prototype (NOT USED);
387 * -1 - may be the beginning of a function definition,
388 * append another line and look again.
389 * The reason we don't attempt to convert function prototypes is that
390 * Ghostscript's declaration-generating macros look too much like
391 * prototypes, and confuse the algorithms.
394 test1(buf)
395 char *buf;
396 { register char *p = buf;
397 char *bend;
398 char *endfn;
399 int contin;
401 if ( !isidfirstchar(*p) )
402 return 0; /* no name at left margin */
403 bend = skipspace(buf + strlen(buf) - 1, -1);
404 switch ( *bend )
406 case ';': contin = 0 /*2*/; break;
407 case ')': contin = 1; break;
408 case '{': return 0; /* not a function */
409 case '}': return 0; /* not a function */
410 default: contin = -1;
412 while ( isidchar(*p) )
413 p++;
414 endfn = p;
415 p = skipspace(p, 1);
416 if ( *p++ != '(' )
417 return 0; /* not a function */
418 p = skipspace(p, 1);
419 if ( *p == ')' )
420 return 0; /* no parameters */
421 /* Check that the apparent function name isn't a keyword. */
422 /* We only need to check for keywords that could be followed */
423 /* by a left parenthesis (which, unfortunately, is most of them). */
424 { static char *words[] =
425 { "asm", "auto", "case", "char", "const", "double",
426 "extern", "float", "for", "if", "int", "long",
427 "register", "return", "short", "signed", "sizeof",
428 "static", "switch", "typedef", "unsigned",
429 "void", "volatile", "while", 0
431 char **key = words;
432 char *kp;
433 int len = endfn - buf;
435 while ( (kp = *key) != 0 )
436 { if ( strlen(kp) == len && !strncmp(kp, buf, len) )
437 return 0; /* name is a keyword */
438 key++;
442 char *id = p;
443 int len;
445 * Check for identifier1(identifier2) and not
446 * identifier1(void).
449 while ( isidchar(*p) )
450 p++;
451 len = p - id;
452 p = skipspace(p, 1);
453 if ( *p == ')' && (len != 4 || strncmp(id, "void", 4)) )
454 return 0; /* not a function */
457 * If the last significant character was a ), we need to count
458 * parentheses, because it might be part of a formal parameter
459 * that is a procedure.
461 if (contin > 0) {
462 int level = 0;
464 for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
465 level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
466 if (level > 0)
467 contin = -1;
469 return contin;
472 /* Convert a recognized function definition or header to K&R syntax. */
474 convert1(buf, out, header, convert_varargs)
475 char *buf;
476 FILE *out;
477 int header; /* Boolean */
478 int convert_varargs; /* Boolean */
479 { char *endfn;
480 register char *p;
482 * The breaks table contains pointers to the beginning and end
483 * of each argument.
485 char **breaks;
486 unsigned num_breaks = 2; /* for testing */
487 char **btop;
488 char **bp;
489 char **ap;
490 char *vararg = 0;
492 /* Pre-ANSI implementations don't agree on whether strchr */
493 /* is called strchr or index, so we open-code it here. */
494 for ( endfn = buf; *(endfn++) != '('; )
496 top: p = endfn;
497 breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
498 if ( breaks == 0 )
499 { /* Couldn't allocate break table, give up */
500 fprintf(stderr, "Unable to allocate break table!\n");
501 fputs(buf, out);
502 return -1;
504 btop = breaks + num_breaks * 2 - 2;
505 bp = breaks;
506 /* Parse the argument list */
508 { int level = 0;
509 char *lp = NULL;
510 char *rp;
511 char *end = NULL;
513 if ( bp >= btop )
514 { /* Filled up break table. */
515 /* Allocate a bigger one and start over. */
516 free((char *)breaks);
517 num_breaks <<= 1;
518 goto top;
520 *bp++ = p;
521 /* Find the end of the argument */
522 for ( ; end == NULL; p++ )
523 { switch(*p)
525 case ',':
526 if ( !level ) end = p;
527 break;
528 case '(':
529 if ( !level ) lp = p;
530 level++;
531 break;
532 case ')':
533 if ( --level < 0 ) end = p;
534 else rp = p;
535 break;
536 case '/':
537 if (p[1] == '*')
538 p = skipspace(p, 1) - 1;
539 break;
540 case '"':
541 p = scanstring(p, 1) - 1;
542 break;
543 default:
547 /* Erase any embedded prototype parameters. */
548 if ( lp )
549 writeblanks(lp + 1, rp);
550 p--; /* back up over terminator */
551 /* Find the name being declared. */
552 /* This is complicated because of procedure and */
553 /* array modifiers. */
554 for ( ; ; )
555 { p = skipspace(p - 1, -1);
556 switch ( *p )
558 case ']': /* skip array dimension(s) */
559 case ')': /* skip procedure args OR name */
560 { int level = 1;
561 while ( level )
562 switch ( *--p )
564 case ']': case ')':
565 level++;
566 break;
567 case '[': case '(':
568 level--;
569 break;
570 case '/':
571 if (p > buf && p[-1] == '*')
572 p = skipspace(p, -1) + 1;
573 break;
574 case '"':
575 p = scanstring(p, -1) + 1;
576 break;
577 default: ;
580 if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
581 { /* We found the name being declared */
582 while ( !isidfirstchar(*p) )
583 p = skipspace(p, 1) + 1;
584 goto found;
586 break;
587 default:
588 goto found;
591 found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
592 { if ( convert_varargs )
593 { *bp++ = "va_alist";
594 vararg = p-2;
596 else
597 { p++;
598 if ( bp == breaks + 1 ) /* sole argument */
599 writeblanks(breaks[0], p);
600 else
601 writeblanks(bp[-1] - 1, p);
602 bp--;
605 else
606 { while ( isidchar(*p) ) p--;
607 *bp++ = p+1;
609 p = end;
611 while ( *p++ == ',' );
612 *bp = p;
613 /* Make a special check for 'void' arglist */
614 if ( bp == breaks+2 )
615 { p = skipspace(breaks[0], 1);
616 if ( !strncmp(p, "void", 4) )
617 { p = skipspace(p+4, 1);
618 if ( p == breaks[2] - 1 )
619 { bp = breaks; /* yup, pretend arglist is empty */
620 writeblanks(breaks[0], p + 1);
624 /* Put out the function name and left parenthesis. */
625 p = buf;
626 while ( p != endfn ) putc(*p, out), p++;
627 /* Put out the declaration. */
628 if ( header )
629 { fputs(");", out);
630 for ( p = breaks[0]; *p; p++ )
631 if ( *p == '\r' || *p == '\n' )
632 putc(*p, out);
634 else
635 { for ( ap = breaks+1; ap < bp; ap += 2 )
636 { p = *ap;
637 while ( isidchar(*p) )
638 putc(*p, out), p++;
639 if ( ap < bp - 1 )
640 fputs(", ", out);
642 fputs(") ", out);
643 /* Put out the argument declarations */
644 for ( ap = breaks+2; ap <= bp; ap += 2 )
645 (*ap)[-1] = ';';
646 if ( vararg != 0 )
647 { *vararg = 0;
648 fputs(breaks[0], out); /* any prior args */
649 fputs("va_dcl", out); /* the final arg */
650 fputs(bp[0], out);
652 else
653 fputs(breaks[0], out);
655 free((char *)breaks);
656 return 0;