Fixes from Gord
[platform/upstream/automake.git] / ansi2knr.c
1 /* Copyright (C) 1989, 1991, 1993, 1994, 1995 Aladdin Enterprises. All rights reserved. */
2
3 /* ansi2knr.c */
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 Among other things, the copyright notice and this notice must be preserved
18 on all copies.
19
20 We explicitly state here what we believe is already implied by the GPL: if
21 the ansi2knr program is distributed as a separate set of sources and a
22 separate executable file which are aggregated on a storage medium together
23 with another program, this in itself does not bring the other program under
24 the GPL, nor does the mere fact that such a program or the procedures for
25 constructing it invoke the ansi2knr executable bring any other part of the
26 program under the GPL.
27 */
28
29 /*
30  * Usage:
31         ansi2knr input_file [output_file]
32  * If no output_file is supplied, output goes to stdout.
33  * There are no error messages.
34  *
35  * ansi2knr recognizes function definitions by seeing a non-keyword
36  * identifier at the left margin, followed by a left parenthesis,
37  * with a right parenthesis as the last character on the line.
38  * It will recognize a multi-line header provided that the last character
39  * of the last line of the header is a right parenthesis,
40  * and no intervening line ends with a left or right brace or a semicolon.
41  * These algorithms ignore whitespace and comments, except that
42  * the function name must be the first thing on the line.
43  * The following constructs will confuse it:
44  *      - Any other construct that starts at the left margin and
45  *          follows the above syntax (such as a macro or function call).
46  *      - Macros that tinker with the syntax of the function header.
47  */
48
49 /*
50  * The original and principal author of ansi2knr is L. Peter Deutsch
51  * <ghost@aladdin.com>.  Other authors are noted in the change history
52  * that follows (in reverse chronological order):
53         lpd 95-04-05 changed copyright notice to make it clear that
54                 including ansi2knr in a program does not bring the entire
55                 program under the GPL
56         lpd 94-12-18 added conditionals for systems where ctype macros
57                 don't handle 8-bit characters properly, suggested by
58                 Francois Pinard <pinard@iro.umontreal.ca>;
59                 removed --varargs switch (this is now the default)
60         lpd 94-10-10 removed CONFIG_BROKETS conditional
61         lpd 94-07-16 added some conditionals to help GNU `configure',
62                 suggested by Francois Pinard <pinard@iro.umontreal.ca>;
63                 properly erase prototype args in function parameters,
64                 contributed by Jim Avera <jima@netcom.com>;
65                 correct error in writeblanks (it shouldn't erase EOLs)
66         lpd 89-xx-xx original version
67  */
68
69 /* Most of the conditionals here are to make ansi2knr work with */
70 /* the GNU configure machinery. */
71
72 #ifdef HAVE_CONFIG_H
73 # include <config.h>
74 #endif
75
76 #include <stdio.h>
77 #include <ctype.h>
78
79 #ifdef HAVE_CONFIG_H
80
81 /*
82    For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
83    This will define HAVE_CONFIG_H and so, activate the following lines.
84  */
85
86 # if STDC_HEADERS || HAVE_STRING_H
87 #  include <string.h>
88 # else
89 #  include <strings.h>
90 # endif
91
92 #else /* not HAVE_CONFIG_H */
93
94 /*
95    Without AC_CONFIG_HEADER, merely use <string.h> as in the original
96    Ghostscript distribution.  This loses on older BSD systems.
97  */
98
99 # include <string.h>
100
101 #endif /* not HAVE_CONFIG_H */
102
103 #ifdef STDC_HEADERS
104 # include <stdlib.h>
105 #else
106 /*
107    malloc and free should be declared in stdlib.h,
108    but if you've got a K&R compiler, they probably aren't.
109  */
110 char *malloc();
111 void free();
112 #endif
113
114 /*
115  * The ctype macros don't always handle 8-bit characters correctly.
116  * Compensate for this here.
117  */
118 #ifndef STDC_HEADERS
119 #  define STDC_HEADERS 0
120 #endif
121 #ifdef isascii
122 #  undef HAVE_ISASCII           /* just in case */
123 #  define HAVE_ISASCII 1
124 #else
125 #  ifndef HAVE_ISASCII
126 #    define HAVE_ISASCII 0
127 #  endif
128 #endif
129 #if STDC_HEADERS || !HAVE_ISASCII
130 #  define is_ascii(c) 1
131 #else
132 #  define is_ascii(c) isascii(c)
133 #endif
134
135 #define is_space(c) (is_ascii(c) && isspace(c))
136 #define is_alpha(c) (is_ascii(c) && isalpha(c))
137 #define is_alnum(c) (is_ascii(c) && isalnum(c))
138
139 /* Scanning macros */
140 #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
141 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
142
143 /* Forward references */
144 char *skipspace();
145 void writeblanks();
146 int test1();
147 int convert1();
148
149 /* The main program */
150 int
151 main(argc, argv)
152     int argc;
153     char *argv[];
154 {       FILE *in, *out;
155 #define bufsize 5000                    /* arbitrary size */
156         char *buf;
157         char *line;
158         /*
159          * In previous versions, ansi2knr recognized a --varargs switch.
160          * If this switch was supplied, ansi2knr would attempt to convert
161          * a ... argument to va_alist and va_dcl; if this switch was not
162          * supplied, ansi2knr would simply drop any such arguments.
163          * Now, ansi2knr always does this conversion, and we only
164          * check for this switch for backward compatibility.
165          */
166         int convert_varargs = 1;
167
168         if ( argc > 1 && argv[1][0] == '-' )
169           {     if ( !strcmp(argv[1], "--varargs") )
170                   {     convert_varargs = 1;
171                         argc--;
172                         argv++;
173                   }
174                 else
175                   {     fprintf(stderr, "Unrecognized switch: %s\n", argv[1]);
176                         exit(1);
177                   }
178           }
179         switch ( argc )
180            {
181         default:
182                 printf("Usage: ansi2knr input_file [output_file]\n");
183                 exit(0);
184         case 2:
185                 out = stdout;
186                 break;
187         case 3:
188                 out = fopen(argv[2], "w");
189                 if ( out == NULL )
190                    {    fprintf(stderr, "Cannot open output file %s\n", argv[2]);
191                         exit(1);
192                    }
193            }
194         in = fopen(argv[1], "r");
195         if ( in == NULL )
196            {    fprintf(stderr, "Cannot open input file %s\n", argv[1]);
197                 exit(1);
198            }
199         fprintf(out, "#line 1 \"%s\"\n", argv[1]);
200         buf = malloc(bufsize);
201         line = buf;
202         while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
203            {    switch ( test1(buf) )
204                    {
205                 case 2:                 /* a function header */
206                         convert1(buf, out, 1, convert_varargs);
207                         break;
208                 case 1:                 /* a function */
209                         convert1(buf, out, 0, convert_varargs);
210                         break;
211                 case -1:                /* maybe the start of a function */
212                         line = buf + strlen(buf);
213                         if ( line != buf + (bufsize - 1) ) /* overflow check */
214                                 continue;
215                         /* falls through */
216                 default:                /* not a function */
217                         fputs(buf, out);
218                         break;
219                    }
220                 line = buf;
221            }
222         if ( line != buf ) fputs(buf, out);
223         free(buf);
224         fclose(out);
225         fclose(in);
226         return 0;
227 }
228
229 /* Skip over space and comments, in either direction. */
230 char *
231 skipspace(p, dir)
232     register char *p;
233     register int dir;                   /* 1 for forward, -1 for backward */
234 {       for ( ; ; )
235            {    while ( is_space(*p) ) p += dir;
236                 if ( !(*p == '/' && p[dir] == '*') ) break;
237                 p += dir;  p += dir;
238                 while ( !(*p == '*' && p[dir] == '/') )
239                    {    if ( *p == 0 ) return p;        /* multi-line comment?? */
240                         p += dir;
241                    }
242                 p += dir;  p += dir;
243            }
244         return p;
245 }
246
247 /*
248  * Write blanks over part of a string.
249  * Don't overwrite end-of-line characters.
250  */
251 void
252 writeblanks(start, end)
253     char *start;
254     char *end;
255 {       char *p;
256         for ( p = start; p < end; p++ )
257           if ( *p != '\r' && *p != '\n' ) *p = ' ';
258 }
259
260 /*
261  * Test whether the string in buf is a function definition.
262  * The string may contain and/or end with a newline.
263  * Return as follows:
264  *      0 - definitely not a function definition;
265  *      1 - definitely a function definition;
266  *      2 - definitely a function prototype (NOT USED);
267  *      -1 - may be the beginning of a function definition,
268  *              append another line and look again.
269  * The reason we don't attempt to convert function prototypes is that
270  * Ghostscript's declaration-generating macros look too much like
271  * prototypes, and confuse the algorithms.
272  */
273 int
274 test1(buf)
275     char *buf;
276 {       register char *p = buf;
277         char *bend;
278         char *endfn;
279         int contin;
280         if ( !isidfirstchar(*p) )
281                 return 0;               /* no name at left margin */
282         bend = skipspace(buf + strlen(buf) - 1, -1);
283         switch ( *bend )
284            {
285         case ';': contin = 0 /*2*/; break;
286         case ')': contin = 1; break;
287         case '{': return 0;             /* not a function */
288         case '}': return 0;             /* not a function */
289         default: contin = -1;
290            }
291         while ( isidchar(*p) ) p++;
292         endfn = p;
293         p = skipspace(p, 1);
294         if ( *p++ != '(' )
295                 return 0;               /* not a function */
296         p = skipspace(p, 1);
297         if ( *p == ')' )
298                 return 0;               /* no parameters */
299         /* Check that the apparent function name isn't a keyword. */
300         /* We only need to check for keywords that could be followed */
301         /* by a left parenthesis (which, unfortunately, is most of them). */
302            {    static char *words[] =
303                    {    "asm", "auto", "case", "char", "const", "double",
304                         "extern", "float", "for", "if", "int", "long",
305                         "register", "return", "short", "signed", "sizeof",
306                         "static", "switch", "typedef", "unsigned",
307                         "void", "volatile", "while", 0
308                    };
309                 char **key = words;
310                 char *kp;
311                 int len = endfn - buf;
312                 while ( (kp = *key) != 0 )
313                    {    if ( strlen(kp) == len && !strncmp(kp, buf, len) )
314                                 return 0;       /* name is a keyword */
315                         key++;
316                    }
317            }
318         return contin;
319 }
320
321 /* Convert a recognized function definition or header to K&R syntax. */
322 int
323 convert1(buf, out, header, convert_varargs)
324     char *buf;
325     FILE *out;
326     int header;                 /* Boolean */
327     int convert_varargs;        /* Boolean */
328 {       char *endfn;
329         register char *p;
330         char **breaks;
331         unsigned num_breaks = 2;        /* for testing */
332         char **btop;
333         char **bp;
334         char **ap;
335         char *vararg = 0;
336         /* Pre-ANSI implementations don't agree on whether strchr */
337         /* is called strchr or index, so we open-code it here. */
338         for ( endfn = buf; *(endfn++) != '('; ) ;
339 top:    p = endfn;
340         breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
341         if ( breaks == 0 )
342            {    /* Couldn't allocate break table, give up */
343                 fprintf(stderr, "Unable to allocate break table!\n");
344                 fputs(buf, out);
345                 return -1;
346            }
347         btop = breaks + num_breaks * 2 - 2;
348         bp = breaks;
349         /* Parse the argument list */
350         do
351            {    int level = 0;
352                 char *lp = NULL;
353                 char *rp;
354                 char *end = NULL;
355                 if ( bp >= btop )
356                    {    /* Filled up break table. */
357                         /* Allocate a bigger one and start over. */
358                         free((char *)breaks);
359                         num_breaks <<= 1;
360                         goto top;
361                    }
362                 *bp++ = p;
363                 /* Find the end of the argument */
364                 for ( ; end == NULL; p++ )
365                    {    switch(*p)
366                            {
367                         case ',':
368                                 if ( !level ) end = p;
369                                 break;
370                         case '(':
371                                 if ( !level ) lp = p;
372                                 level++;
373                                 break;
374                         case ')':
375                                 if ( --level < 0 ) end = p;
376                                 else rp = p;
377                                 break;
378                         case '/':
379                                 p = skipspace(p, 1) - 1;
380                                 break;
381                         default:
382                                 ;
383                            }
384                    }
385                 /* Erase any embedded prototype parameters. */
386                 if ( lp )
387                   writeblanks(lp + 1, rp);
388                 p--;                    /* back up over terminator */
389                 /* Find the name being declared. */
390                 /* This is complicated because of procedure and */
391                 /* array modifiers. */
392                 for ( ; ; )
393                    {    p = skipspace(p - 1, -1);
394                         switch ( *p )
395                            {
396                         case ']':       /* skip array dimension(s) */
397                         case ')':       /* skip procedure args OR name */
398                            {    int level = 1;
399                                 while ( level )
400                                  switch ( *--p )
401                                    {
402                                 case ']': case ')': level++; break;
403                                 case '[': case '(': level--; break;
404                                 case '/': p = skipspace(p, -1) + 1; break;
405                                 default: ;
406                                    }
407                            }
408                                 if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
409                                    {    /* We found the name being declared */
410                                         while ( !isidfirstchar(*p) )
411                                                 p = skipspace(p, 1) + 1;
412                                         goto found;
413                                    }
414                                 break;
415                         default: goto found;
416                            }
417                    }
418 found:          if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
419                   {     if ( convert_varargs )
420                           {     *bp++ = "va_alist";
421                                 vararg = p-2;
422                           }
423                         else
424                           {     p++;
425                                 if ( bp == breaks + 1 ) /* sole argument */
426                                   writeblanks(breaks[0], p);
427                                 else
428                                   writeblanks(bp[-1] - 1, p);
429                                 bp--;
430                           }
431                    }
432                 else
433                    {    while ( isidchar(*p) ) p--;
434                         *bp++ = p+1;
435                    }
436                 p = end;
437            }
438         while ( *p++ == ',' );
439         *bp = p;
440         /* Make a special check for 'void' arglist */
441         if ( bp == breaks+2 )
442            {    p = skipspace(breaks[0], 1);
443                 if ( !strncmp(p, "void", 4) )
444                    {    p = skipspace(p+4, 1);
445                         if ( p == breaks[2] - 1 )
446                            {    bp = breaks;    /* yup, pretend arglist is empty */
447                                 writeblanks(breaks[0], p + 1);
448                            }
449                    }
450            }
451         /* Put out the function name and left parenthesis. */
452         p = buf;
453         while ( p != endfn ) putc(*p, out), p++;
454         /* Put out the declaration. */
455         if ( header )
456           {     fputs(");", out);
457                 for ( p = breaks[0]; *p; p++ )
458                   if ( *p == '\r' || *p == '\n' )
459                     putc(*p, out);
460           }
461         else
462           {     for ( ap = breaks+1; ap < bp; ap += 2 )
463                   {     p = *ap;
464                         while ( isidchar(*p) )
465                           putc(*p, out), p++;
466                         if ( ap < bp - 1 )
467                           fputs(", ", out);
468                   }
469                 fputs(")  ", out);
470                 /* Put out the argument declarations */
471                 for ( ap = breaks+2; ap <= bp; ap += 2 )
472                   (*ap)[-1] = ';';
473                 if ( vararg != 0 )
474                   {     *vararg = 0;
475                         fputs(breaks[0], out);          /* any prior args */
476                         fputs("va_dcl", out);           /* the final arg */
477                         fputs(bp[0], out);
478                   }
479                 else
480                   fputs(breaks[0], out);
481           }
482         free((char *)breaks);
483         return 0;
484 }