source: trunk/third/enscript/src/ansi2knr.c @ 17620

Revision 17620, 13.0 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17619, which included commits to RCS files with non-trunk default branches.
Line 
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/*
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.
17Among other things, the copyright notice and this notice must be preserved
18on all copies.
19
20We explicitly state here what we believe is already implied by the GPL: if
21the ansi2knr program is distributed as a separate set of sources and a
22separate executable file which are aggregated on a storage medium together
23with another program, this in itself does not bring the other program under
24the GPL, nor does the mere fact that such a program or the procedures for
25constructing it invoke the ansi2knr executable bring any other part of the
26program 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 */
110char *malloc();
111void 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 */
144char *skipspace();
145void writeblanks();
146int test1();
147int convert1();
148
149/* The main program */
150int
151main(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. */
230char *
231skipspace(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 */
251void
252writeblanks(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 */
273int
274test1(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. */
322int
323convert1(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++) != '('; ) ;
339top:    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                   }
418found:          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}
Note: See TracBrowser for help on using the repository browser.