2 /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
5 ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
6 WARRANTY. No author or distributor accepts responsibility to anyone for the
7 consequences of using it or for whether it serves any particular purpose or
8 works at all, unless he says so in writing. Refer to the GNU General Public
9 License (the "GPL") for full details.
11 Everyone is granted permission to copy, modify and redistribute ansi2knr,
12 but only under the conditions described in the GPL. A copy of this license
13 is supposed to have been given to you along with ansi2knr so you can know
14 your rights and responsibilities. It should be in a file named COPYLEFT.
15 Among other things, the copyright notice and this notice must be preserved
18 We explicitly state here what we believe is already implied by the GPL: if
19 the ansi2knr program is distributed as a separate set of sources and a
20 separate executable file which are aggregated on a storage medium together
21 with another program, this in itself does not bring the other program under
22 the GPL, nor does the mere fact that such a program or the procedures for
23 constructing it invoke the ansi2knr executable bring any other part of the
24 program under the GPL.
29 ansi2knr input_file [output_file]
30 * If no output_file is supplied, output goes to stdout.
31 * There are no error messages.
33 * ansi2knr recognizes function definitions by seeing a non-keyword
34 * identifier at the left margin, followed by a left parenthesis,
35 * with a right parenthesis as the last character on the line,
36 * and with a left brace as the first token on the following line
37 * (ignoring possible intervening comments).
38 * It will recognize a multi-line header provided that no intervening
39 * line ends with a left or right brace or a semicolon.
40 * These algorithms ignore whitespace and comments, except that
41 * the function name must be the first thing on the line.
42 * The following constructs will confuse it:
43 * - Any other construct that starts at the left margin and
44 * follows the above syntax (such as a macro or function call).
45 * - Some macros that tinker with the syntax of the function header.
49 * The original and principal author of ansi2knr is L. Peter Deutsch
50 * <ghost@aladdin.com>. Other authors are noted in the change history
51 * that follows (in reverse chronological order):
52 lpd 96-01-21 added code to cope with not HAVE_CONFIG_H and with
53 compilers that don't understand void, as suggested by
55 lpd 96-01-15 changed to require that the first non-comment token
56 on the line following a function header be a left brace,
57 to reduce sensitivity to macros, as suggested by Tom Lane
59 lpd 95-06-22 removed #ifndefs whose sole purpose was to define
60 undefined preprocessor symbols as 0; changed all #ifdefs
61 for configuration symbols to #ifs
62 lpd 95-04-05 changed copyright notice to make it clear that
63 including ansi2knr in a program does not bring the entire
65 lpd 94-12-18 added conditionals for systems where ctype macros
66 don't handle 8-bit characters properly, suggested by
67 Francois Pinard <pinard@iro.umontreal.ca>;
68 removed --varargs switch (this is now the default)
69 lpd 94-10-10 removed CONFIG_BROKETS conditional
70 lpd 94-07-16 added some conditionals to help GNU `configure',
71 suggested by Francois Pinard <pinard@iro.umontreal.ca>;
72 properly erase prototype args in function parameters,
73 contributed by Jim Avera <jima@netcom.com>;
74 correct error in writeblanks (it shouldn't erase EOLs)
75 lpd 89-xx-xx original version
78 /* Most of the conditionals here are to make ansi2knr work with */
79 /* or without the GNU configure machinery. */
91 For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
92 This will define HAVE_CONFIG_H and so, activate the following lines.
95 # if STDC_HEADERS || HAVE_STRING_H
101 #else /* not HAVE_CONFIG_H */
103 /* Otherwise do it the hard way */
106 # include <strings.h>
109 extern int strlen(), strncmp();
115 #endif /* not HAVE_CONFIG_H */
121 malloc and free should be declared in stdlib.h,
122 but if you've got a K&R compiler, they probably aren't.
128 extern char *malloc();
131 extern char *malloc();
139 * The ctype macros don't always handle 8-bit characters correctly.
140 * Compensate for this here.
143 # undef HAVE_ISASCII /* just in case */
144 # define HAVE_ISASCII 1
147 #if STDC_HEADERS || !HAVE_ISASCII
148 # define is_ascii(c) 1
150 # define is_ascii(c) isascii(c)
153 #define is_space(c) (is_ascii(c) && isspace(c))
154 #define is_alpha(c) (is_ascii(c) && isalpha(c))
155 #define is_alnum(c) (is_ascii(c) && isalnum(c))
157 /* Scanning macros */
158 #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
159 #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
161 /* Forward references */
167 /* The main program */
173 #define bufsize 5000 /* arbitrary size */
178 * In previous versions, ansi2knr recognized a --varargs switch.
179 * If this switch was supplied, ansi2knr would attempt to convert
180 * a ... argument to va_alist and va_dcl; if this switch was not
181 * supplied, ansi2knr would simply drop any such arguments.
182 * Now, ansi2knr always does this conversion, and we only
183 * check for this switch for backward compatibility.
185 int convert_varargs
= 1;
187 if ( argc
> 1 && argv
[1][0] == '-' )
188 { if ( !strcmp(argv
[1], "--varargs") )
189 { convert_varargs
= 1;
194 { fprintf(stderr
, "Unrecognized switch: %s\n", argv
[1]);
198 if (argc
< 2 || argc
> 3)
200 printf("Usage: ansi2knr input_file [output_file]\n");
203 in
= fopen(argv
[1], "r");
206 fprintf(stderr
, "Cannot open input file %s\n", argv
[1]);
211 out
= fopen(argv
[2], "w");
214 fprintf(stderr
, "Cannot open output file %s\n", argv
[2]);
222 fprintf(out
, "#line 1 \"%s\"\n", argv
[1]);
223 buf
= malloc(bufsize
);
225 while ( fgets(line
, (unsigned)(buf
+ bufsize
- line
), in
) != NULL
)
227 test
: line
+= strlen(line
);
228 switch ( test1(buf
) )
230 case 2: /* a function header */
231 convert1(buf
, out
, 1, convert_varargs
);
233 case 1: /* a function */
234 /* Check for a { at the start of the next line. */
236 f
: if ( line
>= buf
+ (bufsize
- 1) ) /* overflow check */
238 if ( fgets(line
, (unsigned)(buf
+ bufsize
- line
), in
) == NULL
)
240 switch ( *skipspace(more
, 1) )
243 /* Definitely a function header. */
244 convert1(buf
, out
, 0, convert_varargs
);
248 /* The next line was blank or a comment: */
249 /* keep scanning for a non-comment. */
250 line
+= strlen(line
);
253 /* buf isn't a function header, but */
261 case -1: /* maybe the start of a function */
262 if ( line
!= buf
+ (bufsize
- 1) ) /* overflow check */
265 default: /* not a function */
279 /* Skip over space and comments, in either direction. */
283 register int dir
; /* 1 for forward, -1 for backward */
285 { while ( is_space(*p
) )
287 if ( !(*p
== '/' && p
[dir
] == '*') )
290 while ( !(*p
== '*' && p
[dir
] == '/') )
292 return p
; /* multi-line comment?? */
301 * Write blanks over part of a string.
302 * Don't overwrite end-of-line characters.
305 writeblanks(start
, end
)
309 for ( p
= start
; p
< end
; p
++ )
310 if ( *p
!= '\r' && *p
!= '\n' )
316 * Test whether the string in buf is a function definition.
317 * The string may contain and/or end with a newline.
319 * 0 - definitely not a function definition;
320 * 1 - definitely a function definition;
321 * 2 - definitely a function prototype (NOT USED);
322 * -1 - may be the beginning of a function definition,
323 * append another line and look again.
324 * The reason we don't attempt to convert function prototypes is that
325 * Ghostscript's declaration-generating macros look too much like
326 * prototypes, and confuse the algorithms.
331 { register char *p
= buf
;
336 if ( !isidfirstchar(*p
) )
337 return 0; /* no name at left margin */
338 bend
= skipspace(buf
+ strlen(buf
) - 1, -1);
341 case ';': contin
= 0 /*2*/; break;
342 case ')': contin
= 1; break;
343 case '{': return 0; /* not a function */
344 case '}': return 0; /* not a function */
345 default: contin
= -1;
347 while ( isidchar(*p
) )
352 return 0; /* not a function */
355 return 0; /* no parameters */
356 /* Check that the apparent function name isn't a keyword. */
357 /* We only need to check for keywords that could be followed */
358 /* by a left parenthesis (which, unfortunately, is most of them). */
359 { static char *words
[] =
360 { "asm", "auto", "case", "char", "const", "double",
361 "extern", "float", "for", "if", "int", "long",
362 "register", "return", "short", "signed", "sizeof",
363 "static", "switch", "typedef", "unsigned",
364 "void", "volatile", "while", 0
368 int len
= endfn
- buf
;
370 while ( (kp
= *key
) != 0 )
371 { if ( strlen(kp
) == len
&& !strncmp(kp
, buf
, len
) )
372 return 0; /* name is a keyword */
379 /* Convert a recognized function definition or header to K&R syntax. */
381 convert1(buf
, out
, header
, convert_varargs
)
384 int header
; /* Boolean */
385 int convert_varargs
; /* Boolean */
389 * The breaks table contains pointers to the beginning and end
393 unsigned num_breaks
= 2; /* for testing */
399 /* Pre-ANSI implementations don't agree on whether strchr */
400 /* is called strchr or index, so we open-code it here. */
401 for ( endfn
= buf
; *(endfn
++) != '('; )
404 breaks
= (char **)malloc(sizeof(char *) * num_breaks
* 2);
406 { /* Couldn't allocate break table, give up */
407 fprintf(stderr
, "Unable to allocate break table!\n");
411 btop
= breaks
+ num_breaks
* 2 - 2;
413 /* Parse the argument list */
421 { /* Filled up break table. */
422 /* Allocate a bigger one and start over. */
423 free((char *)breaks
);
428 /* Find the end of the argument */
429 for ( ; end
== NULL
; p
++ )
433 if ( !level
) end
= p
;
436 if ( !level
) lp
= p
;
440 if ( --level
< 0 ) end
= p
;
444 p
= skipspace(p
, 1) - 1;
450 /* Erase any embedded prototype parameters. */
452 writeblanks(lp
+ 1, rp
);
453 p
--; /* back up over terminator */
454 /* Find the name being declared. */
455 /* This is complicated because of procedure and */
456 /* array modifiers. */
458 { p
= skipspace(p
- 1, -1);
461 case ']': /* skip array dimension(s) */
462 case ')': /* skip procedure args OR name */
467 case ']': case ')': level
++; break;
468 case '[': case '(': level
--; break;
469 case '/': p
= skipspace(p
, -1) + 1; break;
473 if ( *p
== '(' && *skipspace(p
+ 1, 1) == '*' )
474 { /* We found the name being declared */
475 while ( !isidfirstchar(*p
) )
476 p
= skipspace(p
, 1) + 1;
484 found
: if ( *p
== '.' && p
[-1] == '.' && p
[-2] == '.' )
485 { if ( convert_varargs
)
486 { *bp
++ = "va_alist";
491 if ( bp
== breaks
+ 1 ) /* sole argument */
492 writeblanks(breaks
[0], p
);
494 writeblanks(bp
[-1] - 1, p
);
499 { while ( isidchar(*p
) ) p
--;
504 while ( *p
++ == ',' );
506 /* Make a special check for 'void' arglist */
507 if ( bp
== breaks
+2 )
508 { p
= skipspace(breaks
[0], 1);
509 if ( !strncmp(p
, "void", 4) )
510 { p
= skipspace(p
+4, 1);
511 if ( p
== breaks
[2] - 1 )
512 { bp
= breaks
; /* yup, pretend arglist is empty */
513 writeblanks(breaks
[0], p
+ 1);
517 /* Put out the function name and left parenthesis. */
519 while ( p
!= endfn
) putc(*p
, out
), p
++;
520 /* Put out the declaration. */
523 for ( p
= breaks
[0]; *p
; p
++ )
524 if ( *p
== '\r' || *p
== '\n' )
528 { for ( ap
= breaks
+1; ap
< bp
; ap
+= 2 )
530 while ( isidchar(*p
) )
536 /* Put out the argument declarations */
537 for ( ap
= breaks
+2; ap
<= bp
; ap
+= 2 )
541 fputs(breaks
[0], out
); /* any prior args */
542 fputs("va_dcl", out
); /* the final arg */
546 fputs(breaks
[0], out
);
548 free((char *)breaks
);