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