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