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