xref: /netbsd-src/external/gpl2/gmake/dist/ansi2knr.c (revision 69606e3f5c9388e52aed8c120ad63c049ca45d8f)
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