1 |
/* Copyright (C) 1989, 1997, 1998 Aladdin Enterprises. All rights reserved. */ |
/* Copyright (C) 1989, 2000 Aladdin Enterprises. All rights reserved. */ |
2 |
|
|
3 |
/*$Id$*/ |
/*$Id$*/ |
4 |
/* Convert ANSI C function definitions to K&R ("traditional C") syntax */ |
/* Convert ANSI C function definitions to K&R ("traditional C") syntax */ |
37 |
* There are no error messages. |
* There are no error messages. |
38 |
* |
* |
39 |
* ansi2knr recognizes function definitions by seeing a non-keyword |
* ansi2knr recognizes function definitions by seeing a non-keyword |
40 |
* identifier at the left margin, followed by a left parenthesis, |
* identifier at the left margin, followed by a left parenthesis, with a |
41 |
* with a right parenthesis as the last character on the line, |
* right parenthesis as the last character on the line, and with a left |
42 |
* and with a left brace as the first token on the following line |
* brace as the first token on the following line (ignoring possible |
43 |
* (ignoring possible intervening comments), except that a line |
* intervening comments and/or preprocessor directives), except that a line |
44 |
* consisting of only |
* consisting of only |
45 |
* identifier1(identifier2) |
* identifier1(identifier2) |
46 |
* will not be considered a function definition unless identifier2 is |
* will not be considered a function definition unless identifier2 is |
47 |
* the word "void". ansi2knr will recognize a multi-line header provided |
* the word "void", and a line consisting of |
48 |
* that no intervening line ends with a left or right brace or a semicolon. |
* identifier1(identifier2, <<arbitrary>>) |
49 |
* These algorithms ignore whitespace and comments, except that |
* will not be considered a function definition. |
50 |
* the function name must be the first thing on the line. |
* ansi2knr will recognize a multi-line header provided that no intervening |
51 |
* The following constructs will confuse it: |
* line ends with a left or right brace or a semicolon. These algorithms |
52 |
|
* ignore whitespace, comments, and preprocessor directives, except that |
53 |
|
* the function name must be the first thing on the line. The following |
54 |
|
* constructs will confuse it: |
55 |
* - Any other construct that starts at the left margin and |
* - Any other construct that starts at the left margin and |
56 |
* follows the above syntax (such as a macro or function call). |
* follows the above syntax (such as a macro or function call). |
57 |
* - Some macros that tinker with the syntax of the function header. |
* - Some macros that tinker with the syntax of function headers. |
58 |
*/ |
*/ |
59 |
|
|
60 |
/* |
/* |
61 |
* The original and principal author of ansi2knr is L. Peter Deutsch |
* The original and principal author of ansi2knr is L. Peter Deutsch |
62 |
* <ghost@aladdin.com>. Other authors are noted in the change history |
* <ghost@aladdin.com>. Other authors are noted in the change history |
63 |
* that follows (in reverse chronological order): |
* that follows (in reverse chronological order): |
64 |
|
|
65 |
|
lpd 2000-04-12 backs out Eggert's changes because of bugs: |
66 |
|
- concatlits didn't declare the type of its bufend argument; |
67 |
|
- concatlits didn't't recognize when it was inside a comment; |
68 |
|
- scanstring could scan backward past the beginning of the string; when |
69 |
|
- the check for \ + newline in scanstring was unnecessary. |
70 |
|
|
71 |
|
2000-03-05 Paul Eggert <eggert@twinsun.com> |
72 |
|
|
73 |
|
Add support for concatenated string literals. |
74 |
|
* ansi2knr.c (concatlits): New decl. |
75 |
|
(main): Invoke concatlits to concatenate string literals. |
76 |
|
(scanstring): Handle backslash-newline correctly. Work with |
77 |
|
character constants. Fix bug when scanning backwards through |
78 |
|
backslash-quote. Check for unterminated strings. |
79 |
|
(convert1): Parse character constants, too. |
80 |
|
(appendline, concatlits): New functions. |
81 |
|
* ansi2knr.1: Document this. |
82 |
|
|
83 |
|
lpd 1999-08-17 added code to allow preprocessor directives |
84 |
|
wherever comments are allowed |
85 |
|
lpd 1999-04-12 added minor fixes from Pavel Roskin |
86 |
|
<pavel_roskin@geocities.com> for clean compilation with |
87 |
|
gcc -W -Wall |
88 |
|
lpd 1999-03-22 added hack to recognize lines consisting of |
89 |
|
identifier1(identifier2, xxx) as *not* being procedures |
90 |
|
lpd 1999-02-03 made indentation of preprocessor commands consistent |
91 |
|
lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an |
92 |
|
endless loop; quoted strings within an argument list |
93 |
|
confused the parser |
94 |
|
lpd 1999-01-24 added a check for write errors on the output, |
95 |
|
suggested by Jim Meyering <meyering@ascend.com> |
96 |
lpd 1998-11-09 added further hack to recognize identifier(void) |
lpd 1998-11-09 added further hack to recognize identifier(void) |
97 |
as being a procedure |
as being a procedure |
98 |
lpd 1998-10-23 added hack to recognize lines consisting of |
lpd 1998-10-23 added hack to recognize lines consisting of |
188 |
|
|
189 |
#endif |
#endif |
190 |
|
|
191 |
|
/* Define NULL (for *very* old compilers). */ |
192 |
|
#ifndef NULL |
193 |
|
# define NULL (0) |
194 |
|
#endif |
195 |
|
|
196 |
/* |
/* |
197 |
* The ctype macros don't always handle 8-bit characters correctly. |
* The ctype macros don't always handle 8-bit characters correctly. |
198 |
* Compensate for this here. |
* Compensate for this here. |
199 |
*/ |
*/ |
200 |
#ifdef isascii |
#ifdef isascii |
201 |
# undef HAVE_ISASCII /* just in case */ |
# undef HAVE_ISASCII /* just in case */ |
202 |
# define HAVE_ISASCII 1 |
# define HAVE_ISASCII 1 |
203 |
#else |
#else |
204 |
#endif |
#endif |
205 |
#if STDC_HEADERS || !HAVE_ISASCII |
#if STDC_HEADERS || !HAVE_ISASCII |
206 |
# define is_ascii(c) 1 |
# define is_ascii(c) 1 |
207 |
#else |
#else |
208 |
# define is_ascii(c) isascii(c) |
# define is_ascii(c) isascii(c) |
209 |
#endif |
#endif |
210 |
|
|
211 |
#define is_space(c) (is_ascii(c) && isspace(c)) |
#define is_space(c) (is_ascii(c) && isspace(c)) |
217 |
#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_') |
#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_') |
218 |
|
|
219 |
/* Forward references */ |
/* Forward references */ |
220 |
|
char *ppdirforward(); |
221 |
|
char *ppdirbackward(); |
222 |
char *skipspace(); |
char *skipspace(); |
223 |
|
char *scanstring(); |
224 |
int writeblanks(); |
int writeblanks(); |
225 |
int test1(); |
int test1(); |
226 |
int convert1(); |
int convert1(); |
233 |
{ FILE *in = stdin; |
{ FILE *in = stdin; |
234 |
FILE *out = stdout; |
FILE *out = stdout; |
235 |
char *filename = 0; |
char *filename = 0; |
236 |
|
char *program_name = argv[0]; |
237 |
|
char *output_name = 0; |
238 |
#define bufsize 5000 /* arbitrary size */ |
#define bufsize 5000 /* arbitrary size */ |
239 |
char *buf; |
char *buf; |
240 |
char *line; |
char *line; |
250 |
* check for this switch for backward compatibility. |
* check for this switch for backward compatibility. |
251 |
*/ |
*/ |
252 |
int convert_varargs = 1; |
int convert_varargs = 1; |
253 |
|
int output_error; |
254 |
|
|
255 |
while ( argc > 1 && argv[1][0] == '-' ) { |
while ( argc > 1 && argv[1][0] == '-' ) { |
256 |
if ( !strcmp(argv[1], "--varargs") ) { |
if ( !strcmp(argv[1], "--varargs") ) { |
265 |
argv += 2; |
argv += 2; |
266 |
continue; |
continue; |
267 |
} |
} |
268 |
fprintf(stderr, "Unrecognized switch: %s\n", argv[1]); |
fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name, |
269 |
|
argv[1]); |
270 |
fprintf(stderr, usage); |
fprintf(stderr, usage); |
271 |
exit(1); |
exit(1); |
272 |
} |
} |
276 |
fprintf(stderr, usage); |
fprintf(stderr, usage); |
277 |
exit(0); |
exit(0); |
278 |
case 3: |
case 3: |
279 |
out = fopen(argv[2], "w"); |
output_name = argv[2]; |
280 |
|
out = fopen(output_name, "w"); |
281 |
if ( out == NULL ) { |
if ( out == NULL ) { |
282 |
fprintf(stderr, "Cannot open output file %s\n", argv[2]); |
fprintf(stderr, "%s: Cannot open output file %s\n", |
283 |
|
program_name, output_name); |
284 |
exit(1); |
exit(1); |
285 |
} |
} |
286 |
/* falls through */ |
/* falls through */ |
287 |
case 2: |
case 2: |
288 |
in = fopen(argv[1], "r"); |
in = fopen(argv[1], "r"); |
289 |
if ( in == NULL ) { |
if ( in == NULL ) { |
290 |
fprintf(stderr, "Cannot open input file %s\n", argv[1]); |
fprintf(stderr, "%s: Cannot open input file %s\n", |
291 |
|
program_name, argv[1]); |
292 |
exit(1); |
exit(1); |
293 |
} |
} |
294 |
if ( filename == 0 ) |
if ( filename == 0 ) |
300 |
if ( filename ) |
if ( filename ) |
301 |
fprintf(out, "#line 1 \"%s\"\n", filename); |
fprintf(out, "#line 1 \"%s\"\n", filename); |
302 |
buf = malloc(bufsize); |
buf = malloc(bufsize); |
303 |
|
if ( buf == NULL ) |
304 |
|
{ |
305 |
|
fprintf(stderr, "Unable to allocate read buffer!\n"); |
306 |
|
exit(1); |
307 |
|
} |
308 |
line = buf; |
line = buf; |
309 |
while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL ) |
while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL ) |
310 |
{ |
{ |
321 |
goto wl; |
goto wl; |
322 |
if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL ) |
if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL ) |
323 |
goto wl; |
goto wl; |
324 |
switch ( *skipspace(more, 1) ) |
switch ( *skipspace(ppdirforward(more), 1) ) |
325 |
{ |
{ |
326 |
case '{': |
case '{': |
327 |
/* Definitely a function header. */ |
/* Definitely a function header. */ |
355 |
if ( line != buf ) |
if ( line != buf ) |
356 |
fputs(buf, out); |
fputs(buf, out); |
357 |
free(buf); |
free(buf); |
358 |
if ( out != stdout ) |
if ( output_name ) { |
359 |
fclose(out); |
output_error = ferror(out); |
360 |
|
output_error |= fclose(out); |
361 |
|
} else { /* out == stdout */ |
362 |
|
fflush(out); |
363 |
|
output_error = ferror(out); |
364 |
|
} |
365 |
|
if ( output_error ) { |
366 |
|
fprintf(stderr, "%s: error writing to %s\n", program_name, |
367 |
|
(output_name ? output_name : "stdout")); |
368 |
|
exit(1); |
369 |
|
} |
370 |
if ( in != stdin ) |
if ( in != stdin ) |
371 |
fclose(in); |
fclose(in); |
372 |
return 0; |
return 0; |
373 |
} |
} |
374 |
|
|
375 |
/* Skip over space and comments, in either direction. */ |
/* |
376 |
|
* Skip forward or backward over one or more preprocessor directives. |
377 |
|
*/ |
378 |
|
char * |
379 |
|
ppdirforward(p) |
380 |
|
char *p; |
381 |
|
{ |
382 |
|
for (; *p == '#'; ++p) { |
383 |
|
for (; *p != '\r' && *p != '\n'; ++p) |
384 |
|
if (*p == 0) |
385 |
|
return p; |
386 |
|
if (*p == '\r' && p[1] == '\n') |
387 |
|
++p; |
388 |
|
} |
389 |
|
return p; |
390 |
|
} |
391 |
|
char * |
392 |
|
ppdirbackward(p, limit) |
393 |
|
char *p; |
394 |
|
char *limit; |
395 |
|
{ |
396 |
|
char *np = p; |
397 |
|
|
398 |
|
for (;; p = --np) { |
399 |
|
if (*np == '\n' && np[-1] == '\r') |
400 |
|
--np; |
401 |
|
for (; np > limit && np[-1] != '\r' && np[-1] != '\n'; --np) |
402 |
|
if (np[-1] == 0) |
403 |
|
return np; |
404 |
|
if (*np != '#') |
405 |
|
return p; |
406 |
|
} |
407 |
|
} |
408 |
|
|
409 |
|
/* |
410 |
|
* Skip over whitespace, comments, and preprocessor directives, |
411 |
|
* in either direction. |
412 |
|
*/ |
413 |
char * |
char * |
414 |
skipspace(p, dir) |
skipspace(p, dir) |
415 |
register char *p; |
char *p; |
416 |
register int dir; /* 1 for forward, -1 for backward */ |
int dir; /* 1 for forward, -1 for backward */ |
417 |
{ for ( ; ; ) |
{ |
418 |
{ while ( is_space(*p) ) |
for ( ; ; ) { |
419 |
p += dir; |
while ( is_space(*p) ) |
420 |
if ( !(*p == '/' && p[dir] == '*') ) |
p += dir; |
421 |
break; |
if ( !(*p == '/' && p[dir] == '*') ) |
422 |
p += dir; p += dir; |
break; |
423 |
while ( !(*p == '*' && p[dir] == '/') ) |
p += dir; p += dir; |
424 |
{ if ( *p == 0 ) |
while ( !(*p == '*' && p[dir] == '/') ) { |
425 |
return p; /* multi-line comment?? */ |
if ( *p == 0 ) |
426 |
p += dir; |
return p; /* multi-line comment?? */ |
427 |
} |
p += dir; |
428 |
p += dir; p += dir; |
} |
429 |
} |
p += dir; p += dir; |
430 |
return p; |
} |
431 |
|
return p; |
432 |
|
} |
433 |
|
|
434 |
|
/* Scan over a quoted string, in either direction. */ |
435 |
|
char * |
436 |
|
scanstring(p, dir) |
437 |
|
char *p; |
438 |
|
int dir; |
439 |
|
{ |
440 |
|
for (p += dir; ; p += dir) |
441 |
|
if (*p == '"' && p[-dir] != '\\') |
442 |
|
return p + dir; |
443 |
} |
} |
444 |
|
|
445 |
/* |
/* |
473 |
int |
int |
474 |
test1(buf) |
test1(buf) |
475 |
char *buf; |
char *buf; |
476 |
{ register char *p = buf; |
{ char *p = buf; |
477 |
char *bend; |
char *bend; |
478 |
char *endfn; |
char *endfn; |
479 |
int contin; |
int contin; |
480 |
|
|
481 |
if ( !isidfirstchar(*p) ) |
if ( !isidfirstchar(*p) ) |
482 |
return 0; /* no name at left margin */ |
return 0; /* no name at left margin */ |
483 |
bend = skipspace(buf + strlen(buf) - 1, -1); |
bend = skipspace(ppdirbackward(buf + strlen(buf) - 1, buf), -1); |
484 |
switch ( *bend ) |
switch ( *bend ) |
485 |
{ |
{ |
486 |
case ';': contin = 0 /*2*/; break; |
case ';': contin = 0 /*2*/; break; |
510 |
}; |
}; |
511 |
char **key = words; |
char **key = words; |
512 |
char *kp; |
char *kp; |
513 |
int len = endfn - buf; |
unsigned len = endfn - buf; |
514 |
|
|
515 |
while ( (kp = *key) != 0 ) |
while ( (kp = *key) != 0 ) |
516 |
{ if ( strlen(kp) == len && !strncmp(kp, buf, len) ) |
{ if ( strlen(kp) == len && !strncmp(kp, buf, len) ) |
523 |
int len; |
int len; |
524 |
/* |
/* |
525 |
* Check for identifier1(identifier2) and not |
* Check for identifier1(identifier2) and not |
526 |
* identifier1(void). |
* identifier1(void), or identifier1(identifier2, xxxx). |
527 |
*/ |
*/ |
528 |
|
|
529 |
while ( isidchar(*p) ) |
while ( isidchar(*p) ) |
530 |
p++; |
p++; |
531 |
len = p - id; |
len = p - id; |
532 |
p = skipspace(p, 1); |
p = skipspace(p, 1); |
533 |
if ( *p == ')' && (len != 4 || strncmp(id, "void", 4)) ) |
if (*p == ',' || |
534 |
|
(*p == ')' && (len != 4 || strncmp(id, "void", 4))) |
535 |
|
) |
536 |
return 0; /* not a function */ |
return 0; /* not a function */ |
537 |
} |
} |
538 |
/* |
/* |
559 |
int header; /* Boolean */ |
int header; /* Boolean */ |
560 |
int convert_varargs; /* Boolean */ |
int convert_varargs; /* Boolean */ |
561 |
{ char *endfn; |
{ char *endfn; |
562 |
register char *p; |
char *p; |
563 |
/* |
/* |
564 |
* The breaks table contains pointers to the beginning and end |
* The breaks table contains pointers to the beginning and end |
565 |
* of each argument. |
* of each argument. |
577 |
; |
; |
578 |
top: p = endfn; |
top: p = endfn; |
579 |
breaks = (char **)malloc(sizeof(char *) * num_breaks * 2); |
breaks = (char **)malloc(sizeof(char *) * num_breaks * 2); |
580 |
if ( breaks == 0 ) |
if ( breaks == NULL ) |
581 |
{ /* Couldn't allocate break table, give up */ |
{ /* Couldn't allocate break table, give up */ |
582 |
fprintf(stderr, "Unable to allocate break table!\n"); |
fprintf(stderr, "Unable to allocate break table!\n"); |
583 |
fputs(buf, out); |
fputs(buf, out); |
589 |
do |
do |
590 |
{ int level = 0; |
{ int level = 0; |
591 |
char *lp = NULL; |
char *lp = NULL; |
592 |
char *rp; |
char *rp = NULL; |
593 |
char *end = NULL; |
char *end = NULL; |
594 |
|
|
595 |
if ( bp >= btop ) |
if ( bp >= btop ) |
616 |
else rp = p; |
else rp = p; |
617 |
break; |
break; |
618 |
case '/': |
case '/': |
619 |
p = skipspace(p, 1) - 1; |
if (p[1] == '*') |
620 |
|
p = skipspace(p, 1) - 1; |
621 |
break; |
break; |
622 |
|
case '"': |
623 |
|
p = scanstring(p, 1) - 1; |
624 |
|
break; |
625 |
default: |
default: |
626 |
; |
; |
627 |
} |
} |
628 |
} |
} |
629 |
/* Erase any embedded prototype parameters. */ |
/* Erase any embedded prototype parameters. */ |
630 |
if ( lp ) |
if ( lp && rp ) |
631 |
writeblanks(lp + 1, rp); |
writeblanks(lp + 1, rp); |
632 |
p--; /* back up over terminator */ |
p--; /* back up over terminator */ |
633 |
/* Find the name being declared. */ |
/* Find the name being declared. */ |
643 |
while ( level ) |
while ( level ) |
644 |
switch ( *--p ) |
switch ( *--p ) |
645 |
{ |
{ |
646 |
case ']': case ')': level++; break; |
case ']': case ')': |
647 |
case '[': case '(': level--; break; |
level++; |
648 |
case '/': p = skipspace(p, -1) + 1; break; |
break; |
649 |
|
case '[': case '(': |
650 |
|
level--; |
651 |
|
break; |
652 |
|
case '/': |
653 |
|
if (p > buf && p[-1] == '*') |
654 |
|
p = skipspace(p, -1) + 1; |
655 |
|
break; |
656 |
|
case '"': |
657 |
|
p = scanstring(p, -1) + 1; |
658 |
|
break; |
659 |
default: ; |
default: ; |
660 |
} |
} |
661 |
} |
} |