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