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