1*69606e3fSchristos /* Copyright (C) 1989, 2000 Aladdin Enterprises. All rights reserved. */
2*69606e3fSchristos
3*69606e3fSchristos /*$Id: ansi2knr.c,v 1.1.1.1 2014/08/18 06:46:57 christos Exp $*/
4*69606e3fSchristos /* Convert ANSI C function definitions to K&R ("traditional C") syntax */
5*69606e3fSchristos
6*69606e3fSchristos /*
7*69606e3fSchristos ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
8*69606e3fSchristos WARRANTY. No author or distributor accepts responsibility to anyone for the
9*69606e3fSchristos consequences of using it or for whether it serves any particular purpose or
10*69606e3fSchristos works at all, unless he says so in writing. Refer to the GNU General Public
11*69606e3fSchristos License (the "GPL") for full details.
12*69606e3fSchristos
13*69606e3fSchristos Everyone is granted permission to copy, modify and redistribute ansi2knr,
14*69606e3fSchristos but only under the conditions described in the GPL. A copy of this license
15*69606e3fSchristos is supposed to have been given to you along with ansi2knr so you can know
16*69606e3fSchristos your rights and responsibilities. It should be in a file named COPYLEFT,
17*69606e3fSchristos or, if there is no file named COPYLEFT, a file named COPYING. Among other
18*69606e3fSchristos things, the copyright notice and this notice must be preserved on all
19*69606e3fSchristos copies.
20*69606e3fSchristos
21*69606e3fSchristos We explicitly state here what we believe is already implied by the GPL: if
22*69606e3fSchristos the ansi2knr program is distributed as a separate set of sources and a
23*69606e3fSchristos separate executable file which are aggregated on a storage medium together
24*69606e3fSchristos with another program, this in itself does not bring the other program under
25*69606e3fSchristos the GPL, nor does the mere fact that such a program or the procedures for
26*69606e3fSchristos constructing it invoke the ansi2knr executable bring any other part of the
27*69606e3fSchristos program under the GPL.
28*69606e3fSchristos */
29*69606e3fSchristos
30*69606e3fSchristos /*
31*69606e3fSchristos * Usage:
32*69606e3fSchristos ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]
33*69606e3fSchristos * --filename provides the file name for the #line directive in the output,
34*69606e3fSchristos * overriding input_file (if present).
35*69606e3fSchristos * If no input_file is supplied, input is read from stdin.
36*69606e3fSchristos * If no output_file is supplied, output goes to stdout.
37*69606e3fSchristos * There are no error messages.
38*69606e3fSchristos *
39*69606e3fSchristos * ansi2knr recognizes function definitions by seeing a non-keyword
40*69606e3fSchristos * identifier at the left margin, followed by a left parenthesis, with a
41*69606e3fSchristos * right parenthesis as the last character on the line, and with a left
42*69606e3fSchristos * brace as the first token on the following line (ignoring possible
43*69606e3fSchristos * intervening comments and/or preprocessor directives), except that a line
44*69606e3fSchristos * consisting of only
45*69606e3fSchristos * identifier1(identifier2)
46*69606e3fSchristos * will not be considered a function definition unless identifier2 is
47*69606e3fSchristos * the word "void", and a line consisting of
48*69606e3fSchristos * identifier1(identifier2, <<arbitrary>>)
49*69606e3fSchristos * will not be considered a function definition.
50*69606e3fSchristos * ansi2knr will recognize a multi-line header provided that no intervening
51*69606e3fSchristos * line ends with a left or right brace or a semicolon. These algorithms
52*69606e3fSchristos * ignore whitespace, comments, and preprocessor directives, except that
53*69606e3fSchristos * the function name must be the first thing on the line. The following
54*69606e3fSchristos * constructs will confuse it:
55*69606e3fSchristos * - Any other construct that starts at the left margin and
56*69606e3fSchristos * follows the above syntax (such as a macro or function call).
57*69606e3fSchristos * - Some macros that tinker with the syntax of function headers.
58*69606e3fSchristos */
59*69606e3fSchristos
60*69606e3fSchristos /*
61*69606e3fSchristos * The original and principal author of ansi2knr is L. Peter Deutsch
62*69606e3fSchristos * <ghost@aladdin.com>. Other authors are noted in the change history
63*69606e3fSchristos * that follows (in reverse chronological order):
64*69606e3fSchristos
65*69606e3fSchristos lpd 2000-04-12 backs out Eggert's changes because of bugs:
66*69606e3fSchristos - concatlits didn't declare the type of its bufend argument;
67*69606e3fSchristos - concatlits didn't recognize when it was inside a comment;
68*69606e3fSchristos - scanstring could scan backward past the beginning of the string; when
69*69606e3fSchristos - the check for \ + newline in scanstring was unnecessary.
70*69606e3fSchristos
71*69606e3fSchristos 2000-03-05 Paul Eggert <eggert@twinsun.com>
72*69606e3fSchristos
73*69606e3fSchristos Add support for concatenated string literals.
74*69606e3fSchristos * ansi2knr.c (concatlits): New decl.
75*69606e3fSchristos (main): Invoke concatlits to concatenate string literals.
76*69606e3fSchristos (scanstring): Handle backslash-newline correctly. Work with
77*69606e3fSchristos character constants. Fix bug when scanning backwards through
78*69606e3fSchristos backslash-quote. Check for unterminated strings.
79*69606e3fSchristos (convert1): Parse character constants, too.
80*69606e3fSchristos (appendline, concatlits): New functions.
81*69606e3fSchristos * ansi2knr.1: Document this.
82*69606e3fSchristos
83*69606e3fSchristos lpd 1999-08-17 added code to allow preprocessor directives
84*69606e3fSchristos wherever comments are allowed
85*69606e3fSchristos lpd 1999-04-12 added minor fixes from Pavel Roskin
86*69606e3fSchristos <pavel_roskin@geocities.com> for clean compilation with
87*69606e3fSchristos gcc -W -Wall
88*69606e3fSchristos lpd 1999-03-22 added hack to recognize lines consisting of
89*69606e3fSchristos identifier1(identifier2, xxx) as *not* being procedures
90*69606e3fSchristos lpd 1999-02-03 made indentation of preprocessor commands consistent
91*69606e3fSchristos lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
92*69606e3fSchristos endless loop; quoted strings within an argument list
93*69606e3fSchristos confused the parser
94*69606e3fSchristos lpd 1999-01-24 added a check for write errors on the output,
95*69606e3fSchristos suggested by Jim Meyering <meyering@ascend.com>
96*69606e3fSchristos lpd 1998-11-09 added further hack to recognize identifier(void)
97*69606e3fSchristos as being a procedure
98*69606e3fSchristos lpd 1998-10-23 added hack to recognize lines consisting of
99*69606e3fSchristos identifier1(identifier2) as *not* being procedures
100*69606e3fSchristos lpd 1997-12-08 made input_file optional; only closes input and/or
101*69606e3fSchristos output file if not stdin or stdout respectively; prints
102*69606e3fSchristos usage message on stderr rather than stdout; adds
103*69606e3fSchristos --filename switch (changes suggested by
104*69606e3fSchristos <ceder@lysator.liu.se>)
105*69606e3fSchristos lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
106*69606e3fSchristos compilers that don't understand void, as suggested by
107*69606e3fSchristos Tom Lane
108*69606e3fSchristos lpd 1996-01-15 changed to require that the first non-comment token
109*69606e3fSchristos on the line following a function header be a left brace,
110*69606e3fSchristos to reduce sensitivity to macros, as suggested by Tom Lane
111*69606e3fSchristos <tgl@sss.pgh.pa.us>
112*69606e3fSchristos lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
113*69606e3fSchristos undefined preprocessor symbols as 0; changed all #ifdefs
114*69606e3fSchristos for configuration symbols to #ifs
115*69606e3fSchristos lpd 1995-04-05 changed copyright notice to make it clear that
116*69606e3fSchristos including ansi2knr in a program does not bring the entire
117*69606e3fSchristos program under the GPL
118*69606e3fSchristos lpd 1994-12-18 added conditionals for systems where ctype macros
119*69606e3fSchristos don't handle 8-bit characters properly, suggested by
120*69606e3fSchristos Francois Pinard <pinard@iro.umontreal.ca>;
121*69606e3fSchristos removed --varargs switch (this is now the default)
122*69606e3fSchristos lpd 1994-10-10 removed CONFIG_BROKETS conditional
123*69606e3fSchristos lpd 1994-07-16 added some conditionals to help GNU `configure',
124*69606e3fSchristos suggested by Francois Pinard <pinard@iro.umontreal.ca>;
125*69606e3fSchristos properly erase prototype args in function parameters,
126*69606e3fSchristos contributed by Jim Avera <jima@netcom.com>;
127*69606e3fSchristos correct error in writeblanks (it shouldn't erase EOLs)
128*69606e3fSchristos lpd 1989-xx-xx original version
129*69606e3fSchristos */
130*69606e3fSchristos
131*69606e3fSchristos /* Most of the conditionals here are to make ansi2knr work with */
132*69606e3fSchristos /* or without the GNU configure machinery. */
133*69606e3fSchristos
134*69606e3fSchristos #if HAVE_CONFIG_H
135*69606e3fSchristos # include <config.h>
136*69606e3fSchristos #endif
137*69606e3fSchristos
138*69606e3fSchristos #include <stdio.h>
139*69606e3fSchristos #include <ctype.h>
140*69606e3fSchristos
141*69606e3fSchristos #if HAVE_CONFIG_H
142*69606e3fSchristos
143*69606e3fSchristos /*
144*69606e3fSchristos For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
145*69606e3fSchristos This will define HAVE_CONFIG_H and so, activate the following lines.
146*69606e3fSchristos */
147*69606e3fSchristos
148*69606e3fSchristos # if STDC_HEADERS || HAVE_STRING_H
149*69606e3fSchristos # include <string.h>
150*69606e3fSchristos # else
151*69606e3fSchristos # include <strings.h>
152*69606e3fSchristos # endif
153*69606e3fSchristos
154*69606e3fSchristos #else /* not HAVE_CONFIG_H */
155*69606e3fSchristos
156*69606e3fSchristos /* Otherwise do it the hard way */
157*69606e3fSchristos
158*69606e3fSchristos # ifdef BSD
159*69606e3fSchristos # include <strings.h>
160*69606e3fSchristos # else
161*69606e3fSchristos # ifdef VMS
162*69606e3fSchristos extern int strlen(), strncmp();
163*69606e3fSchristos # else
164*69606e3fSchristos # include <string.h>
165*69606e3fSchristos # endif
166*69606e3fSchristos # endif
167*69606e3fSchristos
168*69606e3fSchristos #endif /* not HAVE_CONFIG_H */
169*69606e3fSchristos
170*69606e3fSchristos #if STDC_HEADERS
171*69606e3fSchristos # include <stdlib.h>
172*69606e3fSchristos #else
173*69606e3fSchristos /*
174*69606e3fSchristos malloc and free should be declared in stdlib.h,
175*69606e3fSchristos but if you've got a K&R compiler, they probably aren't.
176*69606e3fSchristos */
177*69606e3fSchristos # ifdef MSDOS
178*69606e3fSchristos # include <malloc.h>
179*69606e3fSchristos # else
180*69606e3fSchristos # ifdef VMS
181*69606e3fSchristos extern char *malloc();
182*69606e3fSchristos extern void free();
183*69606e3fSchristos # else
184*69606e3fSchristos extern char *malloc();
185*69606e3fSchristos extern int free();
186*69606e3fSchristos # endif
187*69606e3fSchristos # endif
188*69606e3fSchristos
189*69606e3fSchristos #endif
190*69606e3fSchristos
191*69606e3fSchristos /* Define NULL (for *very* old compilers). */
192*69606e3fSchristos #ifndef NULL
193*69606e3fSchristos # define NULL (0)
194*69606e3fSchristos #endif
195*69606e3fSchristos
196*69606e3fSchristos /*
197*69606e3fSchristos * The ctype macros don't always handle 8-bit characters correctly.
198*69606e3fSchristos * Compensate for this here.
199*69606e3fSchristos */
200*69606e3fSchristos #ifdef isascii
201*69606e3fSchristos # undef HAVE_ISASCII /* just in case */
202*69606e3fSchristos # define HAVE_ISASCII 1
203*69606e3fSchristos #else
204*69606e3fSchristos #endif
205*69606e3fSchristos #if STDC_HEADERS || !HAVE_ISASCII
206*69606e3fSchristos # define is_ascii(c) 1
207*69606e3fSchristos #else
208*69606e3fSchristos # define is_ascii(c) isascii(c)
209*69606e3fSchristos #endif
210*69606e3fSchristos
211*69606e3fSchristos #define is_space(c) (is_ascii(c) && isspace(c))
212*69606e3fSchristos #define is_alpha(c) (is_ascii(c) && isalpha(c))
213*69606e3fSchristos #define is_alnum(c) (is_ascii(c) && isalnum(c))
214*69606e3fSchristos
215*69606e3fSchristos /* Scanning macros */
216*69606e3fSchristos #define isidchar(ch) (is_alnum(ch) || (ch) == '_')
217*69606e3fSchristos #define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
218*69606e3fSchristos
219*69606e3fSchristos /* Forward references */
220*69606e3fSchristos char *ppdirforward();
221*69606e3fSchristos char *ppdirbackward();
222*69606e3fSchristos char *skipspace();
223*69606e3fSchristos char *scanstring();
224*69606e3fSchristos int writeblanks();
225*69606e3fSchristos int test1();
226*69606e3fSchristos int convert1();
227*69606e3fSchristos
228*69606e3fSchristos /* The main program */
229*69606e3fSchristos int
main(argc,argv)230*69606e3fSchristos main(argc, argv)
231*69606e3fSchristos int argc;
232*69606e3fSchristos char *argv[];
233*69606e3fSchristos { FILE *in = stdin;
234*69606e3fSchristos FILE *out = stdout;
235*69606e3fSchristos char *filename = 0;
236*69606e3fSchristos char *program_name = argv[0];
237*69606e3fSchristos char *output_name = 0;
238*69606e3fSchristos #define bufsize 5000 /* arbitrary size */
239*69606e3fSchristos char *buf;
240*69606e3fSchristos char *line;
241*69606e3fSchristos char *more;
242*69606e3fSchristos char *usage =
243*69606e3fSchristos "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
244*69606e3fSchristos /*
245*69606e3fSchristos * In previous versions, ansi2knr recognized a --varargs switch.
246*69606e3fSchristos * If this switch was supplied, ansi2knr would attempt to convert
247*69606e3fSchristos * a ... argument to va_alist and va_dcl; if this switch was not
248*69606e3fSchristos * supplied, ansi2knr would simply drop any such arguments.
249*69606e3fSchristos * Now, ansi2knr always does this conversion, and we only
250*69606e3fSchristos * check for this switch for backward compatibility.
251*69606e3fSchristos */
252*69606e3fSchristos int convert_varargs = 1;
253*69606e3fSchristos int output_error;
254*69606e3fSchristos
255*69606e3fSchristos while ( argc > 1 && argv[1][0] == '-' ) {
256*69606e3fSchristos if ( !strcmp(argv[1], "--varargs") ) {
257*69606e3fSchristos convert_varargs = 1;
258*69606e3fSchristos argc--;
259*69606e3fSchristos argv++;
260*69606e3fSchristos continue;
261*69606e3fSchristos }
262*69606e3fSchristos if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
263*69606e3fSchristos filename = argv[2];
264*69606e3fSchristos argc -= 2;
265*69606e3fSchristos argv += 2;
266*69606e3fSchristos continue;
267*69606e3fSchristos }
268*69606e3fSchristos fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
269*69606e3fSchristos argv[1]);
270*69606e3fSchristos fprintf(stderr, usage);
271*69606e3fSchristos exit(1);
272*69606e3fSchristos }
273*69606e3fSchristos switch ( argc )
274*69606e3fSchristos {
275*69606e3fSchristos default:
276*69606e3fSchristos fprintf(stderr, usage);
277*69606e3fSchristos exit(0);
278*69606e3fSchristos case 3:
279*69606e3fSchristos output_name = argv[2];
280*69606e3fSchristos out = fopen(output_name, "w");
281*69606e3fSchristos if ( out == NULL ) {
282*69606e3fSchristos fprintf(stderr, "%s: Cannot open output file %s\n",
283*69606e3fSchristos program_name, output_name);
284*69606e3fSchristos exit(1);
285*69606e3fSchristos }
286*69606e3fSchristos /* falls through */
287*69606e3fSchristos case 2:
288*69606e3fSchristos in = fopen(argv[1], "r");
289*69606e3fSchristos if ( in == NULL ) {
290*69606e3fSchristos fprintf(stderr, "%s: Cannot open input file %s\n",
291*69606e3fSchristos program_name, argv[1]);
292*69606e3fSchristos exit(1);
293*69606e3fSchristos }
294*69606e3fSchristos if ( filename == 0 )
295*69606e3fSchristos filename = argv[1];
296*69606e3fSchristos /* falls through */
297*69606e3fSchristos case 1:
298*69606e3fSchristos break;
299*69606e3fSchristos }
300*69606e3fSchristos if ( filename )
301*69606e3fSchristos fprintf(out, "#line 1 \"%s\"\n", filename);
302*69606e3fSchristos buf = malloc(bufsize);
303*69606e3fSchristos if ( buf == NULL )
304*69606e3fSchristos {
305*69606e3fSchristos fprintf(stderr, "Unable to allocate read buffer!\n");
306*69606e3fSchristos exit(1);
307*69606e3fSchristos }
308*69606e3fSchristos line = buf;
309*69606e3fSchristos while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
310*69606e3fSchristos {
311*69606e3fSchristos test: line += strlen(line);
312*69606e3fSchristos switch ( test1(buf) )
313*69606e3fSchristos {
314*69606e3fSchristos case 2: /* a function header */
315*69606e3fSchristos convert1(buf, out, 1, convert_varargs);
316*69606e3fSchristos break;
317*69606e3fSchristos case 1: /* a function */
318*69606e3fSchristos /* Check for a { at the start of the next line. */
319*69606e3fSchristos more = ++line;
320*69606e3fSchristos f: if ( line >= buf + (bufsize - 1) ) /* overflow check */
321*69606e3fSchristos goto wl;
322*69606e3fSchristos if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
323*69606e3fSchristos goto wl;
324*69606e3fSchristos switch ( *skipspace(ppdirforward(more), 1) )
325*69606e3fSchristos {
326*69606e3fSchristos case '{':
327*69606e3fSchristos /* Definitely a function header. */
328*69606e3fSchristos convert1(buf, out, 0, convert_varargs);
329*69606e3fSchristos fputs(more, out);
330*69606e3fSchristos break;
331*69606e3fSchristos case 0:
332*69606e3fSchristos /* The next line was blank or a comment: */
333*69606e3fSchristos /* keep scanning for a non-comment. */
334*69606e3fSchristos line += strlen(line);
335*69606e3fSchristos goto f;
336*69606e3fSchristos default:
337*69606e3fSchristos /* buf isn't a function header, but */
338*69606e3fSchristos /* more might be. */
339*69606e3fSchristos fputs(buf, out);
340*69606e3fSchristos strcpy(buf, more);
341*69606e3fSchristos line = buf;
342*69606e3fSchristos goto test;
343*69606e3fSchristos }
344*69606e3fSchristos break;
345*69606e3fSchristos case -1: /* maybe the start of a function */
346*69606e3fSchristos if ( line != buf + (bufsize - 1) ) /* overflow check */
347*69606e3fSchristos continue;
348*69606e3fSchristos /* falls through */
349*69606e3fSchristos default: /* not a function */
350*69606e3fSchristos wl: fputs(buf, out);
351*69606e3fSchristos break;
352*69606e3fSchristos }
353*69606e3fSchristos line = buf;
354*69606e3fSchristos }
355*69606e3fSchristos if ( line != buf )
356*69606e3fSchristos fputs(buf, out);
357*69606e3fSchristos free(buf);
358*69606e3fSchristos if ( output_name ) {
359*69606e3fSchristos output_error = ferror(out);
360*69606e3fSchristos output_error |= fclose(out);
361*69606e3fSchristos } else { /* out == stdout */
362*69606e3fSchristos fflush(out);
363*69606e3fSchristos output_error = ferror(out);
364*69606e3fSchristos }
365*69606e3fSchristos if ( output_error ) {
366*69606e3fSchristos fprintf(stderr, "%s: error writing to %s\n", program_name,
367*69606e3fSchristos (output_name ? output_name : "stdout"));
368*69606e3fSchristos exit(1);
369*69606e3fSchristos }
370*69606e3fSchristos if ( in != stdin )
371*69606e3fSchristos fclose(in);
372*69606e3fSchristos return 0;
373*69606e3fSchristos }
374*69606e3fSchristos
375*69606e3fSchristos /*
376*69606e3fSchristos * Skip forward or backward over one or more preprocessor directives.
377*69606e3fSchristos */
378*69606e3fSchristos char *
ppdirforward(p)379*69606e3fSchristos ppdirforward(p)
380*69606e3fSchristos char *p;
381*69606e3fSchristos {
382*69606e3fSchristos for (; *p == '#'; ++p) {
383*69606e3fSchristos for (; *p != '\r' && *p != '\n'; ++p)
384*69606e3fSchristos if (*p == 0)
385*69606e3fSchristos return p;
386*69606e3fSchristos if (*p == '\r' && p[1] == '\n')
387*69606e3fSchristos ++p;
388*69606e3fSchristos }
389*69606e3fSchristos return p;
390*69606e3fSchristos }
391*69606e3fSchristos char *
ppdirbackward(p,limit)392*69606e3fSchristos ppdirbackward(p, limit)
393*69606e3fSchristos char *p;
394*69606e3fSchristos char *limit;
395*69606e3fSchristos {
396*69606e3fSchristos char *np = p;
397*69606e3fSchristos
398*69606e3fSchristos for (;; p = --np) {
399*69606e3fSchristos if (*np == '\n' && np[-1] == '\r')
400*69606e3fSchristos --np;
401*69606e3fSchristos for (; np > limit && np[-1] != '\r' && np[-1] != '\n'; --np)
402*69606e3fSchristos if (np[-1] == 0)
403*69606e3fSchristos return np;
404*69606e3fSchristos if (*np != '#')
405*69606e3fSchristos return p;
406*69606e3fSchristos }
407*69606e3fSchristos }
408*69606e3fSchristos
409*69606e3fSchristos /*
410*69606e3fSchristos * Skip over whitespace, comments, and preprocessor directives,
411*69606e3fSchristos * in either direction.
412*69606e3fSchristos */
413*69606e3fSchristos char *
skipspace(p,dir)414*69606e3fSchristos skipspace(p, dir)
415*69606e3fSchristos char *p;
416*69606e3fSchristos int dir; /* 1 for forward, -1 for backward */
417*69606e3fSchristos {
418*69606e3fSchristos for ( ; ; ) {
419*69606e3fSchristos while ( is_space(*p) )
420*69606e3fSchristos p += dir;
421*69606e3fSchristos if ( !(*p == '/' && p[dir] == '*') )
422*69606e3fSchristos break;
423*69606e3fSchristos p += dir; p += dir;
424*69606e3fSchristos while ( !(*p == '*' && p[dir] == '/') ) {
425*69606e3fSchristos if ( *p == 0 )
426*69606e3fSchristos return p; /* multi-line comment?? */
427*69606e3fSchristos p += dir;
428*69606e3fSchristos }
429*69606e3fSchristos p += dir; p += dir;
430*69606e3fSchristos }
431*69606e3fSchristos return p;
432*69606e3fSchristos }
433*69606e3fSchristos
434*69606e3fSchristos /* Scan over a quoted string, in either direction. */
435*69606e3fSchristos char *
scanstring(p,dir)436*69606e3fSchristos scanstring(p, dir)
437*69606e3fSchristos char *p;
438*69606e3fSchristos int dir;
439*69606e3fSchristos {
440*69606e3fSchristos for (p += dir; ; p += dir)
441*69606e3fSchristos if (*p == '"' && p[-dir] != '\\')
442*69606e3fSchristos return p + dir;
443*69606e3fSchristos }
444*69606e3fSchristos
445*69606e3fSchristos /*
446*69606e3fSchristos * Write blanks over part of a string.
447*69606e3fSchristos * Don't overwrite end-of-line characters.
448*69606e3fSchristos */
449*69606e3fSchristos int
writeblanks(start,end)450*69606e3fSchristos writeblanks(start, end)
451*69606e3fSchristos char *start;
452*69606e3fSchristos char *end;
453*69606e3fSchristos { char *p;
454*69606e3fSchristos for ( p = start; p < end; p++ )
455*69606e3fSchristos if ( *p != '\r' && *p != '\n' )
456*69606e3fSchristos *p = ' ';
457*69606e3fSchristos return 0;
458*69606e3fSchristos }
459*69606e3fSchristos
460*69606e3fSchristos /*
461*69606e3fSchristos * Test whether the string in buf is a function definition.
462*69606e3fSchristos * The string may contain and/or end with a newline.
463*69606e3fSchristos * Return as follows:
464*69606e3fSchristos * 0 - definitely not a function definition;
465*69606e3fSchristos * 1 - definitely a function definition;
466*69606e3fSchristos * 2 - definitely a function prototype (NOT USED);
467*69606e3fSchristos * -1 - may be the beginning of a function definition,
468*69606e3fSchristos * append another line and look again.
469*69606e3fSchristos * The reason we don't attempt to convert function prototypes is that
470*69606e3fSchristos * Ghostscript's declaration-generating macros look too much like
471*69606e3fSchristos * prototypes, and confuse the algorithms.
472*69606e3fSchristos */
473*69606e3fSchristos int
test1(buf)474*69606e3fSchristos test1(buf)
475*69606e3fSchristos char *buf;
476*69606e3fSchristos { char *p = buf;
477*69606e3fSchristos char *bend;
478*69606e3fSchristos char *endfn;
479*69606e3fSchristos int contin;
480*69606e3fSchristos
481*69606e3fSchristos if ( !isidfirstchar(*p) )
482*69606e3fSchristos return 0; /* no name at left margin */
483*69606e3fSchristos bend = skipspace(ppdirbackward(buf + strlen(buf) - 1, buf), -1);
484*69606e3fSchristos switch ( *bend )
485*69606e3fSchristos {
486*69606e3fSchristos case ';': contin = 0 /*2*/; break;
487*69606e3fSchristos case ')': contin = 1; break;
488*69606e3fSchristos case '{': return 0; /* not a function */
489*69606e3fSchristos case '}': return 0; /* not a function */
490*69606e3fSchristos default: contin = -1;
491*69606e3fSchristos }
492*69606e3fSchristos while ( isidchar(*p) )
493*69606e3fSchristos p++;
494*69606e3fSchristos endfn = p;
495*69606e3fSchristos p = skipspace(p, 1);
496*69606e3fSchristos if ( *p++ != '(' )
497*69606e3fSchristos return 0; /* not a function */
498*69606e3fSchristos p = skipspace(p, 1);
499*69606e3fSchristos if ( *p == ')' )
500*69606e3fSchristos return 0; /* no parameters */
501*69606e3fSchristos /* Check that the apparent function name isn't a keyword. */
502*69606e3fSchristos /* We only need to check for keywords that could be followed */
503*69606e3fSchristos /* by a left parenthesis (which, unfortunately, is most of them). */
504*69606e3fSchristos { static char *words[] =
505*69606e3fSchristos { "asm", "auto", "case", "char", "const", "double",
506*69606e3fSchristos "extern", "float", "for", "if", "int", "long",
507*69606e3fSchristos "register", "return", "short", "signed", "sizeof",
508*69606e3fSchristos "static", "switch", "typedef", "unsigned",
509*69606e3fSchristos "void", "volatile", "while", 0
510*69606e3fSchristos };
511*69606e3fSchristos char **key = words;
512*69606e3fSchristos char *kp;
513*69606e3fSchristos unsigned len = endfn - buf;
514*69606e3fSchristos
515*69606e3fSchristos while ( (kp = *key) != 0 )
516*69606e3fSchristos { if ( strlen(kp) == len && !strncmp(kp, buf, len) )
517*69606e3fSchristos return 0; /* name is a keyword */
518*69606e3fSchristos key++;
519*69606e3fSchristos }
520*69606e3fSchristos }
521*69606e3fSchristos {
522*69606e3fSchristos char *id = p;
523*69606e3fSchristos int len;
524*69606e3fSchristos /*
525*69606e3fSchristos * Check for identifier1(identifier2) and not
526*69606e3fSchristos * identifier1(void), or identifier1(identifier2, xxxx).
527*69606e3fSchristos */
528*69606e3fSchristos
529*69606e3fSchristos while ( isidchar(*p) )
530*69606e3fSchristos p++;
531*69606e3fSchristos len = p - id;
532*69606e3fSchristos p = skipspace(p, 1);
533*69606e3fSchristos if (*p == ',' ||
534*69606e3fSchristos (*p == ')' && (len != 4 || strncmp(id, "void", 4)))
535*69606e3fSchristos )
536*69606e3fSchristos return 0; /* not a function */
537*69606e3fSchristos }
538*69606e3fSchristos /*
539*69606e3fSchristos * If the last significant character was a ), we need to count
540*69606e3fSchristos * parentheses, because it might be part of a formal parameter
541*69606e3fSchristos * that is a procedure.
542*69606e3fSchristos */
543*69606e3fSchristos if (contin > 0) {
544*69606e3fSchristos int level = 0;
545*69606e3fSchristos
546*69606e3fSchristos for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
547*69606e3fSchristos level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
548*69606e3fSchristos if (level > 0)
549*69606e3fSchristos contin = -1;
550*69606e3fSchristos }
551*69606e3fSchristos return contin;
552*69606e3fSchristos }
553*69606e3fSchristos
554*69606e3fSchristos /* Convert a recognized function definition or header to K&R syntax. */
555*69606e3fSchristos int
convert1(buf,out,header,convert_varargs)556*69606e3fSchristos convert1(buf, out, header, convert_varargs)
557*69606e3fSchristos char *buf;
558*69606e3fSchristos FILE *out;
559*69606e3fSchristos int header; /* Boolean */
560*69606e3fSchristos int convert_varargs; /* Boolean */
561*69606e3fSchristos { char *endfn;
562*69606e3fSchristos char *p;
563*69606e3fSchristos /*
564*69606e3fSchristos * The breaks table contains pointers to the beginning and end
565*69606e3fSchristos * of each argument.
566*69606e3fSchristos */
567*69606e3fSchristos char **breaks;
568*69606e3fSchristos unsigned num_breaks = 2; /* for testing */
569*69606e3fSchristos char **btop;
570*69606e3fSchristos char **bp;
571*69606e3fSchristos char **ap;
572*69606e3fSchristos char *vararg = 0;
573*69606e3fSchristos
574*69606e3fSchristos /* Pre-ANSI implementations don't agree on whether strchr */
575*69606e3fSchristos /* is called strchr or index, so we open-code it here. */
576*69606e3fSchristos for ( endfn = buf; *(endfn++) != '('; )
577*69606e3fSchristos ;
578*69606e3fSchristos top: p = endfn;
579*69606e3fSchristos breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
580*69606e3fSchristos if ( breaks == NULL )
581*69606e3fSchristos { /* Couldn't allocate break table, give up */
582*69606e3fSchristos fprintf(stderr, "Unable to allocate break table!\n");
583*69606e3fSchristos fputs(buf, out);
584*69606e3fSchristos return -1;
585*69606e3fSchristos }
586*69606e3fSchristos btop = breaks + num_breaks * 2 - 2;
587*69606e3fSchristos bp = breaks;
588*69606e3fSchristos /* Parse the argument list */
589*69606e3fSchristos do
590*69606e3fSchristos { int level = 0;
591*69606e3fSchristos char *lp = NULL;
592*69606e3fSchristos char *rp = NULL;
593*69606e3fSchristos char *end = NULL;
594*69606e3fSchristos
595*69606e3fSchristos if ( bp >= btop )
596*69606e3fSchristos { /* Filled up break table. */
597*69606e3fSchristos /* Allocate a bigger one and start over. */
598*69606e3fSchristos free((char *)breaks);
599*69606e3fSchristos num_breaks <<= 1;
600*69606e3fSchristos goto top;
601*69606e3fSchristos }
602*69606e3fSchristos *bp++ = p;
603*69606e3fSchristos /* Find the end of the argument */
604*69606e3fSchristos for ( ; end == NULL; p++ )
605*69606e3fSchristos { switch(*p)
606*69606e3fSchristos {
607*69606e3fSchristos case ',':
608*69606e3fSchristos if ( !level ) end = p;
609*69606e3fSchristos break;
610*69606e3fSchristos case '(':
611*69606e3fSchristos if ( !level ) lp = p;
612*69606e3fSchristos level++;
613*69606e3fSchristos break;
614*69606e3fSchristos case ')':
615*69606e3fSchristos if ( --level < 0 ) end = p;
616*69606e3fSchristos else rp = p;
617*69606e3fSchristos break;
618*69606e3fSchristos case '/':
619*69606e3fSchristos if (p[1] == '*')
620*69606e3fSchristos p = skipspace(p, 1) - 1;
621*69606e3fSchristos break;
622*69606e3fSchristos case '"':
623*69606e3fSchristos p = scanstring(p, 1) - 1;
624*69606e3fSchristos break;
625*69606e3fSchristos default:
626*69606e3fSchristos ;
627*69606e3fSchristos }
628*69606e3fSchristos }
629*69606e3fSchristos /* Erase any embedded prototype parameters. */
630*69606e3fSchristos if ( lp && rp )
631*69606e3fSchristos writeblanks(lp + 1, rp);
632*69606e3fSchristos p--; /* back up over terminator */
633*69606e3fSchristos /* Find the name being declared. */
634*69606e3fSchristos /* This is complicated because of procedure and */
635*69606e3fSchristos /* array modifiers. */
636*69606e3fSchristos for ( ; ; )
637*69606e3fSchristos { p = skipspace(p - 1, -1);
638*69606e3fSchristos switch ( *p )
639*69606e3fSchristos {
640*69606e3fSchristos case ']': /* skip array dimension(s) */
641*69606e3fSchristos case ')': /* skip procedure args OR name */
642*69606e3fSchristos { int level = 1;
643*69606e3fSchristos while ( level )
644*69606e3fSchristos switch ( *--p )
645*69606e3fSchristos {
646*69606e3fSchristos case ']': case ')':
647*69606e3fSchristos level++;
648*69606e3fSchristos break;
649*69606e3fSchristos case '[': case '(':
650*69606e3fSchristos level--;
651*69606e3fSchristos break;
652*69606e3fSchristos case '/':
653*69606e3fSchristos if (p > buf && p[-1] == '*')
654*69606e3fSchristos p = skipspace(p, -1) + 1;
655*69606e3fSchristos break;
656*69606e3fSchristos case '"':
657*69606e3fSchristos p = scanstring(p, -1) + 1;
658*69606e3fSchristos break;
659*69606e3fSchristos default: ;
660*69606e3fSchristos }
661*69606e3fSchristos }
662*69606e3fSchristos if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
663*69606e3fSchristos { /* We found the name being declared */
664*69606e3fSchristos while ( !isidfirstchar(*p) )
665*69606e3fSchristos p = skipspace(p, 1) + 1;
666*69606e3fSchristos goto found;
667*69606e3fSchristos }
668*69606e3fSchristos break;
669*69606e3fSchristos default:
670*69606e3fSchristos goto found;
671*69606e3fSchristos }
672*69606e3fSchristos }
673*69606e3fSchristos found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
674*69606e3fSchristos { if ( convert_varargs )
675*69606e3fSchristos { *bp++ = "va_alist";
676*69606e3fSchristos vararg = p-2;
677*69606e3fSchristos }
678*69606e3fSchristos else
679*69606e3fSchristos { p++;
680*69606e3fSchristos if ( bp == breaks + 1 ) /* sole argument */
681*69606e3fSchristos writeblanks(breaks[0], p);
682*69606e3fSchristos else
683*69606e3fSchristos writeblanks(bp[-1] - 1, p);
684*69606e3fSchristos bp--;
685*69606e3fSchristos }
686*69606e3fSchristos }
687*69606e3fSchristos else
688*69606e3fSchristos { while ( isidchar(*p) ) p--;
689*69606e3fSchristos *bp++ = p+1;
690*69606e3fSchristos }
691*69606e3fSchristos p = end;
692*69606e3fSchristos }
693*69606e3fSchristos while ( *p++ == ',' );
694*69606e3fSchristos *bp = p;
695*69606e3fSchristos /* Make a special check for 'void' arglist */
696*69606e3fSchristos if ( bp == breaks+2 )
697*69606e3fSchristos { p = skipspace(breaks[0], 1);
698*69606e3fSchristos if ( !strncmp(p, "void", 4) )
699*69606e3fSchristos { p = skipspace(p+4, 1);
700*69606e3fSchristos if ( p == breaks[2] - 1 )
701*69606e3fSchristos { bp = breaks; /* yup, pretend arglist is empty */
702*69606e3fSchristos writeblanks(breaks[0], p + 1);
703*69606e3fSchristos }
704*69606e3fSchristos }
705*69606e3fSchristos }
706*69606e3fSchristos /* Put out the function name and left parenthesis. */
707*69606e3fSchristos p = buf;
708*69606e3fSchristos while ( p != endfn ) putc(*p, out), p++;
709*69606e3fSchristos /* Put out the declaration. */
710*69606e3fSchristos if ( header )
711*69606e3fSchristos { fputs(");", out);
712*69606e3fSchristos for ( p = breaks[0]; *p; p++ )
713*69606e3fSchristos if ( *p == '\r' || *p == '\n' )
714*69606e3fSchristos putc(*p, out);
715*69606e3fSchristos }
716*69606e3fSchristos else
717*69606e3fSchristos { for ( ap = breaks+1; ap < bp; ap += 2 )
718*69606e3fSchristos { p = *ap;
719*69606e3fSchristos while ( isidchar(*p) )
720*69606e3fSchristos putc(*p, out), p++;
721*69606e3fSchristos if ( ap < bp - 1 )
722*69606e3fSchristos fputs(", ", out);
723*69606e3fSchristos }
724*69606e3fSchristos fputs(") ", out);
725*69606e3fSchristos /* Put out the argument declarations */
726*69606e3fSchristos for ( ap = breaks+2; ap <= bp; ap += 2 )
727*69606e3fSchristos (*ap)[-1] = ';';
728*69606e3fSchristos if ( vararg != 0 )
729*69606e3fSchristos { *vararg = 0;
730*69606e3fSchristos fputs(breaks[0], out); /* any prior args */
731*69606e3fSchristos fputs("va_dcl", out); /* the final arg */
732*69606e3fSchristos fputs(bp[0], out);
733*69606e3fSchristos }
734*69606e3fSchristos else
735*69606e3fSchristos fputs(breaks[0], out);
736*69606e3fSchristos }
737*69606e3fSchristos free((char *)breaks);
738*69606e3fSchristos return 0;
739*69606e3fSchristos }
740