Commit | Line | Data |
---|---|---|
528b75e1 WJ |
1 | /* Copyright (C) 1989, 1991 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* ansi2knr.c */ | |
21 | /* Convert ANSI function declarations to K&R syntax */ | |
22 | #include <stdio.h> | |
23 | #include <ctype.h> | |
24 | #include "string_.h" | |
25 | #include "malloc_.h" | |
26 | ||
27 | /* Usage: | |
28 | ansi2knr input_file output_file | |
29 | * If no output_file is supplied, output goes to stdout. | |
30 | * There are no error messages. | |
31 | * | |
32 | * ansi2knr recognizes functions by seeing a non-keyword identifier | |
33 | * at the left margin, followed by a left parenthesis, | |
34 | * with a right parenthesis as the last character on the line. | |
35 | * It will recognize a multi-line header if the last character | |
36 | * on each line but the last is a left parenthesis or comma. | |
37 | * These algorithms ignore whitespace and comments, except that | |
38 | * the function name must be the first thing on the line. | |
39 | * The following constructs will confuse it: | |
40 | - Any other construct that starts at the left margin and | |
41 | follows the above syntax (such as a macro or function call). | |
42 | - Macros that tinker with the syntax of the function header. | |
43 | */ | |
44 | ||
45 | /* Scanning macros */ | |
46 | #define isidchar(ch) (isalnum(ch) || (ch) == '_') | |
47 | #define isidfirstchar(ch) (isalpha(ch) || (ch) == '_') | |
48 | ||
49 | main(argc, argv) | |
50 | int argc; | |
51 | char *argv[]; | |
52 | { FILE *in, *out; | |
53 | #define bufsize 500 /* arbitrary size */ | |
54 | char buf[bufsize]; | |
55 | char *line; | |
56 | switch ( argc ) | |
57 | { | |
58 | default: | |
59 | printf("Usage: ansi2knr input_file [output_file]\n"); | |
60 | exit(0); | |
61 | case 2: | |
62 | out = stdout; break; | |
63 | case 3: | |
64 | out = fopen(argv[2], "w"); | |
65 | if ( out == NULL ) | |
66 | { fprintf(stderr, "Cannot open %s\n", argv[2]); | |
67 | exit(1); | |
68 | } | |
69 | } | |
70 | in = fopen(argv[1], "r"); | |
71 | if ( in == NULL ) | |
72 | { fprintf(stderr, "Cannot open %s\n", argv[1]); | |
73 | exit(1); | |
74 | } | |
75 | fprintf(out, "#line 1 \"%s\"\n", argv[1]); | |
76 | line = buf; | |
77 | while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL ) | |
78 | { switch ( test1(buf) ) | |
79 | { | |
80 | case 1: /* a function */ | |
81 | convert1(buf, out); | |
82 | break; | |
83 | case -1: /* maybe the start of a function */ | |
84 | line = buf + strlen(buf); | |
85 | continue; | |
86 | default: /* not a function */ | |
87 | fputs(buf, out); | |
88 | break; | |
89 | } | |
90 | line = buf; | |
91 | } | |
92 | if ( line != buf ) fputs(buf, out); | |
93 | fclose(out); | |
94 | fclose(in); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | /* Skip over space and comments, in either direction. */ | |
99 | char * | |
100 | skipspace(p, dir) | |
101 | register char *p; | |
102 | register int dir; /* 1 for forward, -1 for backward */ | |
103 | { for ( ; ; ) | |
104 | { while ( isspace(*p) ) p += dir; | |
105 | if ( !(*p == '/' && p[dir] == '*') ) break; | |
106 | p += dir; p += dir; | |
107 | while ( !(*p == '*' && p[dir] == '/') ) | |
108 | { if ( *p == 0 ) return p; /* multi-line comment?? */ | |
109 | p += dir; | |
110 | } | |
111 | p += dir; p += dir; | |
112 | } | |
113 | return p; | |
114 | } | |
115 | ||
116 | /* | |
117 | * Write blanks over part of a string. | |
118 | */ | |
119 | void | |
120 | writeblanks(start, end) | |
121 | char *start; | |
122 | char *end; | |
123 | { char *p; | |
124 | for ( p = start; p < end; p++ ) *p = ' '; | |
125 | } | |
126 | ||
127 | /* | |
128 | * Test whether the string in buf is a function definition. | |
129 | * The string may contain and/or end with a newline. | |
130 | * Return as follows: | |
131 | * 0 - definitely not a function definition; | |
132 | * 1 - definitely a function definition; | |
133 | * -1 - may be the beginning of a function definition, | |
134 | * append another line and look again. | |
135 | */ | |
136 | test1(buf) | |
137 | char *buf; | |
138 | { register char *p = buf; | |
139 | char *bend; | |
140 | char *endfn; | |
141 | int contin; | |
142 | if ( !isidfirstchar(*p) ) | |
143 | return 0; /* no name at left margin */ | |
144 | bend = skipspace(buf + strlen(buf) - 1, -1); | |
145 | switch ( *bend ) | |
146 | { | |
147 | case ')': contin = 1; break; | |
148 | case '(': | |
149 | case ',': contin = -1; break; | |
150 | default: return 0; /* not a function */ | |
151 | } | |
152 | while ( isidchar(*p) ) p++; | |
153 | endfn = p; | |
154 | p = skipspace(p, 1); | |
155 | if ( *p++ != '(' ) | |
156 | return 0; /* not a function */ | |
157 | p = skipspace(p, 1); | |
158 | if ( *p == ')' ) | |
159 | return 0; /* no parameters */ | |
160 | /* Check that the apparent function name isn't a keyword. */ | |
161 | /* We only need to check for keywords that could be followed */ | |
162 | /* by a left parenthesis (which, unfortunately, is most of them). */ | |
163 | { static char *words[] = | |
164 | { "asm", "auto", "case", "char", "const", "double", | |
165 | "extern", "float", "for", "if", "int", "long", | |
166 | "register", "return", "short", "signed", "sizeof", | |
167 | "static", "switch", "typedef", "unsigned", | |
168 | "void", "volatile", "while", 0 | |
169 | }; | |
170 | char **key = words; | |
171 | char *kp; | |
172 | int len = endfn - buf; | |
173 | while ( (kp = *key) != 0 ) | |
174 | { if ( strlen(kp) == len && !strncmp(kp, buf, len) ) | |
175 | return 0; /* name is a keyword */ | |
176 | key++; | |
177 | } | |
178 | } | |
179 | return contin; | |
180 | } | |
181 | ||
182 | convert1(buf, out) | |
183 | char *buf; | |
184 | FILE *out; | |
185 | { char *endfn = strchr(buf, '(') + 1; | |
186 | register char *p; | |
187 | char **breaks; | |
188 | unsigned num_breaks = 2; /* for testing */ | |
189 | char **btop; | |
190 | char **bp; | |
191 | char **ap; | |
192 | top: p = endfn; | |
193 | breaks = (char **)malloc(sizeof(char *) * num_breaks * 2); | |
194 | if ( breaks == 0 ) | |
195 | { /* Couldn't allocate break table, give up */ | |
196 | fprintf(stderr, "Unable to allocate break table!\n"); | |
197 | fputs(buf, out); | |
198 | return -1; | |
199 | } | |
200 | btop = breaks + num_breaks * 2 - 2; | |
201 | bp = breaks; | |
202 | /* Parse the argument list */ | |
203 | do | |
204 | { int level = 0; | |
205 | char *end = NULL; | |
206 | if ( bp >= btop ) | |
207 | { /* Filled up break table. */ | |
208 | /* Allocate a bigger one and start over. */ | |
209 | free((char *)breaks); | |
210 | num_breaks <<= 1; | |
211 | goto top; | |
212 | } | |
213 | *bp++ = p; | |
214 | /* Find the end of the argument */ | |
215 | for ( ; end == NULL; p++ ) | |
216 | { switch(*p) | |
217 | { | |
218 | case ',': if ( !level ) end = p; break; | |
219 | case '(': level++; break; | |
220 | case ')': if ( --level < 0 ) end = p; break; | |
221 | case '/': p = skipspace(p, 1) - 1; break; | |
222 | default: ; | |
223 | } | |
224 | } | |
225 | p--; /* back up over terminator */ | |
226 | /* Find the name being declared. */ | |
227 | /* This is complicated because of procedure and */ | |
228 | /* array modifiers. */ | |
229 | for ( ; ; ) | |
230 | { p = skipspace(p - 1, -1); | |
231 | switch ( *p ) | |
232 | { | |
233 | case ']': /* skip array dimension(s) */ | |
234 | case ')': /* skip procedure args OR name */ | |
235 | { int level = 1; | |
236 | while ( level ) | |
237 | switch ( *--p ) | |
238 | { | |
239 | case ']': case ')': level++; break; | |
240 | case '[': case '(': level--; break; | |
241 | case '/': p = skipspace(p, -1) + 1; break; | |
242 | default: ; | |
243 | } | |
244 | } | |
245 | if ( *p == '(' && *skipspace(p + 1, 1) == '*' ) | |
246 | { /* We found the name being declared */ | |
247 | while ( !isidfirstchar(*p) ) | |
248 | p = skipspace(p, 1) + 1; | |
249 | goto found; | |
250 | } | |
251 | break; | |
252 | default: goto found; | |
253 | } | |
254 | } | |
255 | found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' ) | |
256 | { p++; | |
257 | if ( bp == breaks + 1 ) /* sole argument */ | |
258 | writeblanks(breaks[0], p); | |
259 | else | |
260 | writeblanks(bp[-1] - 1, p); | |
261 | bp--; | |
262 | } | |
263 | else | |
264 | { while ( isidchar(*p) ) p--; | |
265 | *bp++ = p+1; | |
266 | } | |
267 | p = end; | |
268 | } | |
269 | while ( *p++ == ',' ); | |
270 | *bp = p; | |
271 | /* Make a special check for 'void' arglist */ | |
272 | if ( bp == breaks+2 ) | |
273 | { p = skipspace(breaks[0], 1); | |
274 | if ( !strncmp(p, "void", 4) ) | |
275 | { p = skipspace(p+4, 1); | |
276 | if ( p == breaks[2] - 1 ) | |
277 | { bp = breaks; /* yup, pretend arglist is empty */ | |
278 | writeblanks(breaks[0], p + 1); | |
279 | } | |
280 | } | |
281 | } | |
282 | /* Put out the function name */ | |
283 | p = buf; | |
284 | while ( p != endfn ) putc(*p, out), p++; | |
285 | /* Put out the declaration */ | |
286 | for ( ap = breaks+1; ap < bp; ap += 2 ) | |
287 | { p = *ap; | |
288 | while ( isidchar(*p) ) putc(*p, out), p++; | |
289 | if ( ap < bp - 1 ) fputs(", ", out); | |
290 | } | |
291 | fputs(") ", out); | |
292 | /* Put out the argument declarations */ | |
293 | for ( ap = breaks+2; ap <= bp; ap += 2 ) (*ap)[-1] = ';'; | |
294 | fputs(breaks[0], out); | |
295 | free((char *)breaks); | |
296 | return 0; | |
297 | } |