.
[coreutils.git] / src / ansi2knr.c
blob8be5296c38fa03cde7e12b37540133910a0c519d
1 /* Copyright (C) 1989, 1991, 1993, 1994, 1995 Aladdin Enterprises. All rights reserved. */
3 /* ansi2knr.c */
4 /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
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.
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.
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.
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.
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.
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-06-22 removed #ifndefs whose sole purpose was to define
54 undefined preprocessor symbols as 0; changed all #ifdefs
55 for configuration symbols to #ifs
56 lpd 95-04-05 changed copyright notice to make it clear that
57 including ansi2knr in a program does not bring the entire
58 program under the GPL
59 lpd 94-12-18 added conditionals for systems where ctype macros
60 don't handle 8-bit characters properly, suggested by
61 Francois Pinard <pinard@iro.umontreal.ca>;
62 removed --varargs switch (this is now the default)
63 lpd 94-10-10 removed CONFIG_BROKETS conditional
64 lpd 94-07-16 added some conditionals to help GNU `configure',
65 suggested by Francois Pinard <pinard@iro.umontreal.ca>;
66 properly erase prototype args in function parameters,
67 contributed by Jim Avera <jima@netcom.com>;
68 correct error in writeblanks (it shouldn't erase EOLs)
69 lpd 89-xx-xx original version
72 /* Most of the conditionals here are to make ansi2knr work with */
73 /* the GNU configure machinery. */
75 #if HAVE_CONFIG_H
76 # include <config.h>
77 #endif
79 #include <stdio.h>
80 #include <ctype.h>
82 #if HAVE_CONFIG_H
85 For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
86 This will define HAVE_CONFIG_H and so, activate the following lines.
89 # if STDC_HEADERS || HAVE_STRING_H
90 # include <string.h>
91 # else
92 # include <strings.h>
93 # endif
95 #else /* not HAVE_CONFIG_H */
98 Without AC_CONFIG_HEADER, merely use <string.h> as in the original
99 Ghostscript distribution. This loses on older BSD systems.
102 # include <string.h>
104 #endif /* not HAVE_CONFIG_H */
106 #if STDC_HEADERS
107 # include <stdlib.h>
108 #else
110 malloc and free should be declared in stdlib.h,
111 but if you've got a K&R compiler, they probably aren't.
113 char *malloc();
114 void free();
115 #endif
118 * The ctype macros don't always handle 8-bit characters correctly.
119 * Compensate for this here.
121 #ifdef isascii
122 # undef HAVE_ISASCII /* just in case */
123 # define HAVE_ISASCII 1
124 #else
125 #endif
126 #if STDC_HEADERS || !HAVE_ISASCII
127 # define is_ascii(c) 1
128 #else
129 # define is_ascii(c) isascii(c)
130 #endif
132 #define is_space(c) (is_ascii(c) && isspace(c))
133 #define is_alpha(c) (is_ascii(c) && isalpha(c))
134 #define is_alnum(c) (is_ascii(c) && isalnum(c))
136 /* Scanning macros */
137 #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
138 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
140 /* Forward references */
141 char *skipspace();
142 void writeblanks();
143 int test1();
144 int convert1();
146 /* The main program */
148 main(argc, argv)
149 int argc;
150 char *argv[];
151 { FILE *in, *out;
152 #define bufsize 5000 /* arbitrary size */
153 char *buf;
154 char *line;
156 * In previous versions, ansi2knr recognized a --varargs switch.
157 * If this switch was supplied, ansi2knr would attempt to convert
158 * a ... argument to va_alist and va_dcl; if this switch was not
159 * supplied, ansi2knr would simply drop any such arguments.
160 * Now, ansi2knr always does this conversion, and we only
161 * check for this switch for backward compatibility.
163 int convert_varargs = 1;
165 if ( argc > 1 && argv[1][0] == '-' )
166 { if ( !strcmp(argv[1], "--varargs") )
167 { convert_varargs = 1;
168 argc--;
169 argv++;
171 else
172 { fprintf(stderr, "Unrecognized switch: %s\n", argv[1]);
173 exit(1);
176 switch ( argc )
178 default:
179 printf("Usage: ansi2knr input_file [output_file]\n");
180 exit(0);
181 case 2:
182 out = stdout;
183 break;
184 case 3:
185 out = fopen(argv[2], "w");
186 if ( out == NULL )
187 { fprintf(stderr, "Cannot open output file %s\n", argv[2]);
188 exit(1);
191 in = fopen(argv[1], "r");
192 if ( in == NULL )
193 { fprintf(stderr, "Cannot open input file %s\n", argv[1]);
194 exit(1);
196 fprintf(out, "#line 1 \"%s\"\n", argv[1]);
197 buf = malloc(bufsize);
198 line = buf;
199 while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
200 { switch ( test1(buf) )
202 case 2: /* a function header */
203 convert1(buf, out, 1, convert_varargs);
204 break;
205 case 1: /* a function */
206 convert1(buf, out, 0, convert_varargs);
207 break;
208 case -1: /* maybe the start of a function */
209 line = buf + strlen(buf);
210 if ( line != buf + (bufsize - 1) ) /* overflow check */
211 continue;
212 /* falls through */
213 default: /* not a function */
214 fputs(buf, out);
215 break;
217 line = buf;
219 if ( line != buf ) fputs(buf, out);
220 free(buf);
221 fclose(out);
222 fclose(in);
223 return 0;
226 /* Skip over space and comments, in either direction. */
227 char *
228 skipspace(p, dir)
229 register char *p;
230 register int dir; /* 1 for forward, -1 for backward */
231 { for ( ; ; )
232 { while ( is_space(*p) ) p += dir;
233 if ( !(*p == '/' && p[dir] == '*') ) break;
234 p += dir; p += dir;
235 while ( !(*p == '*' && p[dir] == '/') )
236 { if ( *p == 0 ) return p; /* multi-line comment?? */
237 p += dir;
239 p += dir; p += dir;
241 return p;
245 * Write blanks over part of a string.
246 * Don't overwrite end-of-line characters.
248 void
249 writeblanks(start, end)
250 char *start;
251 char *end;
252 { char *p;
253 for ( p = start; p < end; p++ )
254 if ( *p != '\r' && *p != '\n' ) *p = ' ';
258 * Test whether the string in buf is a function definition.
259 * The string may contain and/or end with a newline.
260 * Return as follows:
261 * 0 - definitely not a function definition;
262 * 1 - definitely a function definition;
263 * 2 - definitely a function prototype (NOT USED);
264 * -1 - may be the beginning of a function definition,
265 * append another line and look again.
266 * The reason we don't attempt to convert function prototypes is that
267 * Ghostscript's declaration-generating macros look too much like
268 * prototypes, and confuse the algorithms.
271 test1(buf)
272 char *buf;
273 { register char *p = buf;
274 char *bend;
275 char *endfn;
276 int contin;
277 if ( !isidfirstchar(*p) )
278 return 0; /* no name at left margin */
279 bend = skipspace(buf + strlen(buf) - 1, -1);
280 switch ( *bend )
282 case ';': contin = 0 /*2*/; break;
283 case ')': contin = 1; break;
284 case '{': return 0; /* not a function */
285 case '}': return 0; /* not a function */
286 default: contin = -1;
288 while ( isidchar(*p) ) p++;
289 endfn = p;
290 p = skipspace(p, 1);
291 if ( *p++ != '(' )
292 return 0; /* not a function */
293 p = skipspace(p, 1);
294 if ( *p == ')' )
295 return 0; /* no parameters */
296 /* Check that the apparent function name isn't a keyword. */
297 /* We only need to check for keywords that could be followed */
298 /* by a left parenthesis (which, unfortunately, is most of them). */
299 { static char *words[] =
300 { "asm", "auto", "case", "char", "const", "double",
301 "extern", "float", "for", "if", "int", "long",
302 "register", "return", "short", "signed", "sizeof",
303 "static", "switch", "typedef", "unsigned",
304 "void", "volatile", "while", 0
306 char **key = words;
307 char *kp;
308 int len = endfn - buf;
309 while ( (kp = *key) != 0 )
310 { if ( strlen(kp) == len && !strncmp(kp, buf, len) )
311 return 0; /* name is a keyword */
312 key++;
315 return contin;
318 /* Convert a recognized function definition or header to K&R syntax. */
320 convert1(buf, out, header, convert_varargs)
321 char *buf;
322 FILE *out;
323 int header; /* Boolean */
324 int convert_varargs; /* Boolean */
325 { char *endfn;
326 register char *p;
327 char **breaks;
328 unsigned num_breaks = 2; /* for testing */
329 char **btop;
330 char **bp;
331 char **ap;
332 char *vararg = 0;
333 /* Pre-ANSI implementations don't agree on whether strchr */
334 /* is called strchr or index, so we open-code it here. */
335 for ( endfn = buf; *(endfn++) != '('; ) ;
336 top: p = endfn;
337 breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
338 if ( breaks == 0 )
339 { /* Couldn't allocate break table, give up */
340 fprintf(stderr, "Unable to allocate break table!\n");
341 fputs(buf, out);
342 return -1;
344 btop = breaks + num_breaks * 2 - 2;
345 bp = breaks;
346 /* Parse the argument list */
348 { int level = 0;
349 char *lp = NULL;
350 char *rp;
351 char *end = NULL;
352 if ( bp >= btop )
353 { /* Filled up break table. */
354 /* Allocate a bigger one and start over. */
355 free((char *)breaks);
356 num_breaks <<= 1;
357 goto top;
359 *bp++ = p;
360 /* Find the end of the argument */
361 for ( ; end == NULL; p++ )
362 { switch(*p)
364 case ',':
365 if ( !level ) end = p;
366 break;
367 case '(':
368 if ( !level ) lp = p;
369 level++;
370 break;
371 case ')':
372 if ( --level < 0 ) end = p;
373 else rp = p;
374 break;
375 case '/':
376 p = skipspace(p, 1) - 1;
377 break;
378 default:
382 /* Erase any embedded prototype parameters. */
383 if ( lp )
384 writeblanks(lp + 1, rp);
385 p--; /* back up over terminator */
386 /* Find the name being declared. */
387 /* This is complicated because of procedure and */
388 /* array modifiers. */
389 for ( ; ; )
390 { p = skipspace(p - 1, -1);
391 switch ( *p )
393 case ']': /* skip array dimension(s) */
394 case ')': /* skip procedure args OR name */
395 { int level = 1;
396 while ( level )
397 switch ( *--p )
399 case ']': case ')': level++; break;
400 case '[': case '(': level--; break;
401 case '/': p = skipspace(p, -1) + 1; break;
402 default: ;
405 if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
406 { /* We found the name being declared */
407 while ( !isidfirstchar(*p) )
408 p = skipspace(p, 1) + 1;
409 goto found;
411 break;
412 default: goto found;
415 found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
416 { if ( convert_varargs )
417 { *bp++ = "va_alist";
418 vararg = p-2;
420 else
421 { p++;
422 if ( bp == breaks + 1 ) /* sole argument */
423 writeblanks(breaks[0], p);
424 else
425 writeblanks(bp[-1] - 1, p);
426 bp--;
429 else
430 { while ( isidchar(*p) ) p--;
431 *bp++ = p+1;
433 p = end;
435 while ( *p++ == ',' );
436 *bp = p;
437 /* Make a special check for 'void' arglist */
438 if ( bp == breaks+2 )
439 { p = skipspace(breaks[0], 1);
440 if ( !strncmp(p, "void", 4) )
441 { p = skipspace(p+4, 1);
442 if ( p == breaks[2] - 1 )
443 { bp = breaks; /* yup, pretend arglist is empty */
444 writeblanks(breaks[0], p + 1);
448 /* Put out the function name and left parenthesis. */
449 p = buf;
450 while ( p != endfn ) putc(*p, out), p++;
451 /* Put out the declaration. */
452 if ( header )
453 { fputs(");", out);
454 for ( p = breaks[0]; *p; p++ )
455 if ( *p == '\r' || *p == '\n' )
456 putc(*p, out);
458 else
459 { for ( ap = breaks+1; ap < bp; ap += 2 )
460 { p = *ap;
461 while ( isidchar(*p) )
462 putc(*p, out), p++;
463 if ( ap < bp - 1 )
464 fputs(", ", out);
466 fputs(") ", out);
467 /* Put out the argument declarations */
468 for ( ap = breaks+2; ap <= bp; ap += 2 )
469 (*ap)[-1] = ';';
470 if ( vararg != 0 )
471 { *vararg = 0;
472 fputs(breaks[0], out); /* any prior args */
473 fputs("va_dcl", out); /* the final arg */
474 fputs(bp[0], out);
476 else
477 fputs(breaks[0], out);
479 free((char *)breaks);
480 return 0;