1106424Sroberto/* Copyright (C) 1989, 2000 Aladdin Enterprises.  All rights reserved. */
254359Sroberto
3106424Sroberto/*$Id: ansi2knr.c,v 1.3 2000/04/13 03:41:48 lpd Exp $*/
454359Sroberto/* Convert ANSI C function definitions to K&R ("traditional C") syntax */
554359Sroberto
654359Sroberto/*
754359Srobertoansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY
854359SrobertoWARRANTY.  No author or distributor accepts responsibility to anyone for the
954359Srobertoconsequences of using it or for whether it serves any particular purpose or
1054359Srobertoworks at all, unless he says so in writing.  Refer to the GNU General Public
1154359SrobertoLicense (the "GPL") for full details.
1254359Sroberto
1354359SrobertoEveryone is granted permission to copy, modify and redistribute ansi2knr,
1454359Srobertobut only under the conditions described in the GPL.  A copy of this license
1554359Srobertois supposed to have been given to you along with ansi2knr so you can know
1654359Srobertoyour rights and responsibilities.  It should be in a file named COPYLEFT,
1754359Srobertoor, if there is no file named COPYLEFT, a file named COPYING.  Among other
1854359Srobertothings, the copyright notice and this notice must be preserved on all
1954359Srobertocopies.
2054359Sroberto
2154359SrobertoWe explicitly state here what we believe is already implied by the GPL: if
2254359Srobertothe ansi2knr program is distributed as a separate set of sources and a
2354359Srobertoseparate executable file which are aggregated on a storage medium together
2454359Srobertowith another program, this in itself does not bring the other program under
2554359Srobertothe GPL, nor does the mere fact that such a program or the procedures for
2654359Srobertoconstructing it invoke the ansi2knr executable bring any other part of the
2754359Srobertoprogram under the GPL.
2854359Sroberto*/
2954359Sroberto
3054359Sroberto/*
3154359Sroberto * Usage:
3254359Sroberto	ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]
3354359Sroberto * --filename provides the file name for the #line directive in the output,
3454359Sroberto * overriding input_file (if present).
3554359Sroberto * If no input_file is supplied, input is read from stdin.
3654359Sroberto * If no output_file is supplied, output goes to stdout.
3754359Sroberto * There are no error messages.
3854359Sroberto *
3954359Sroberto * ansi2knr recognizes function definitions by seeing a non-keyword
4054359Sroberto * identifier at the left margin, followed by a left parenthesis, with a
4154359Sroberto * right parenthesis as the last character on the line, and with a left
4254359Sroberto * brace as the first token on the following line (ignoring possible
4354359Sroberto * intervening comments and/or preprocessor directives), except that a line
4454359Sroberto * consisting of only
4554359Sroberto *	identifier1(identifier2)
4654359Sroberto * will not be considered a function definition unless identifier2 is
4754359Sroberto * the word "void", and a line consisting of
4854359Sroberto *	identifier1(identifier2, <<arbitrary>>)
4954359Sroberto * will not be considered a function definition.
5054359Sroberto * ansi2knr will recognize a multi-line header provided that no intervening
5154359Sroberto * line ends with a left or right brace or a semicolon.  These algorithms
5254359Sroberto * ignore whitespace, comments, and preprocessor directives, except that
5354359Sroberto * the function name must be the first thing on the line.  The following
5454359Sroberto * constructs will confuse it:
5554359Sroberto *	- Any other construct that starts at the left margin and
5654359Sroberto *	    follows the above syntax (such as a macro or function call).
5754359Sroberto *	- Some macros that tinker with the syntax of function headers.
5854359Sroberto */
5954359Sroberto
6054359Sroberto/*
6154359Sroberto * The original and principal author of ansi2knr is L. Peter Deutsch
6254359Sroberto * <ghost@aladdin.com>.  Other authors are noted in the change history
6354359Sroberto * that follows (in reverse chronological order):
64106424Sroberto
65106424Sroberto	lpd 2000-04-12 backs out Eggert's changes because of bugs:
66106424Sroberto	- concatlits didn't declare the type of its bufend argument;
67182007Sroberto	- concatlits didn't recognize when it was inside a comment;
68106424Sroberto	- scanstring could scan backward past the beginning of the string; when
69106424Sroberto	- the check for \ + newline in scanstring was unnecessary.
70106424Sroberto
71106424Sroberto	2000-03-05  Paul Eggert  <eggert@twinsun.com>
72106424Sroberto
73106424Sroberto	Add support for concatenated string literals.
74106424Sroberto	* ansi2knr.c (concatlits): New decl.
75106424Sroberto	(main): Invoke concatlits to concatenate string literals.
76106424Sroberto	(scanstring): Handle backslash-newline correctly.  Work with
77106424Sroberto	character constants.  Fix bug when scanning backwards through
78106424Sroberto	backslash-quote.  Check for unterminated strings.
79106424Sroberto	(convert1): Parse character constants, too.
80106424Sroberto	(appendline, concatlits): New functions.
81106424Sroberto	* ansi2knr.1: Document this.
82106424Sroberto
8354359Sroberto	lpd 1999-08-17 added code to allow preprocessor directives
8454359Sroberto		wherever comments are allowed
8554359Sroberto	lpd 1999-04-12 added minor fixes from Pavel Roskin
8654359Sroberto		<pavel_roskin@geocities.com> for clean compilation with
8754359Sroberto		gcc -W -Wall
8854359Sroberto	lpd 1999-03-22 added hack to recognize lines consisting of
8954359Sroberto		identifier1(identifier2, xxx) as *not* being procedures
9054359Sroberto	lpd 1999-02-03 made indentation of preprocessor commands consistent
9154359Sroberto	lpd 1999-01-28 fixed two bugs: a '/' in an argument list caused an
9254359Sroberto		endless loop; quoted strings within an argument list
9354359Sroberto		confused the parser
9454359Sroberto	lpd 1999-01-24 added a check for write errors on the output,
9554359Sroberto		suggested by Jim Meyering <meyering@ascend.com>
9654359Sroberto	lpd 1998-11-09 added further hack to recognize identifier(void)
9754359Sroberto		as being a procedure
9854359Sroberto	lpd 1998-10-23 added hack to recognize lines consisting of
9954359Sroberto		identifier1(identifier2) as *not* being procedures
10054359Sroberto	lpd 1997-12-08 made input_file optional; only closes input and/or
10154359Sroberto		output file if not stdin or stdout respectively; prints
10254359Sroberto		usage message on stderr rather than stdout; adds
10354359Sroberto		--filename switch (changes suggested by
10454359Sroberto		<ceder@lysator.liu.se>)
10554359Sroberto	lpd 1996-01-21 added code to cope with not HAVE_CONFIG_H and with
10654359Sroberto		compilers that don't understand void, as suggested by
10754359Sroberto		Tom Lane
10854359Sroberto	lpd 1996-01-15 changed to require that the first non-comment token
10954359Sroberto		on the line following a function header be a left brace,
11054359Sroberto		to reduce sensitivity to macros, as suggested by Tom Lane
11154359Sroberto		<tgl@sss.pgh.pa.us>
11254359Sroberto	lpd 1995-06-22 removed #ifndefs whose sole purpose was to define
11354359Sroberto		undefined preprocessor symbols as 0; changed all #ifdefs
11454359Sroberto		for configuration symbols to #ifs
11554359Sroberto	lpd 1995-04-05 changed copyright notice to make it clear that
11654359Sroberto		including ansi2knr in a program does not bring the entire
11754359Sroberto		program under the GPL
11854359Sroberto	lpd 1994-12-18 added conditionals for systems where ctype macros
11954359Sroberto		don't handle 8-bit characters properly, suggested by
12054359Sroberto		Francois Pinard <pinard@iro.umontreal.ca>;
12154359Sroberto		removed --varargs switch (this is now the default)
12254359Sroberto	lpd 1994-10-10 removed CONFIG_BROKETS conditional
12354359Sroberto	lpd 1994-07-16 added some conditionals to help GNU `configure',
12454359Sroberto		suggested by Francois Pinard <pinard@iro.umontreal.ca>;
12554359Sroberto		properly erase prototype args in function parameters,
12654359Sroberto		contributed by Jim Avera <jima@netcom.com>;
12754359Sroberto		correct error in writeblanks (it shouldn't erase EOLs)
12854359Sroberto	lpd 1989-xx-xx original version
12954359Sroberto */
13054359Sroberto
13154359Sroberto/* Most of the conditionals here are to make ansi2knr work with */
13254359Sroberto/* or without the GNU configure machinery. */
13354359Sroberto
13454359Sroberto#if HAVE_CONFIG_H
13554359Sroberto# include <config.h>
13654359Sroberto#endif
13754359Sroberto
13854359Sroberto#include <stdio.h>
13954359Sroberto#include <ctype.h>
14054359Sroberto
14154359Sroberto#if HAVE_CONFIG_H
14254359Sroberto
14354359Sroberto/*
14454359Sroberto   For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h).
14554359Sroberto   This will define HAVE_CONFIG_H and so, activate the following lines.
14654359Sroberto */
14754359Sroberto
14854359Sroberto# if STDC_HEADERS || HAVE_STRING_H
14954359Sroberto#  include <string.h>
15054359Sroberto# else
15154359Sroberto#  include <strings.h>
15254359Sroberto# endif
15354359Sroberto
15454359Sroberto#else /* not HAVE_CONFIG_H */
15554359Sroberto
15654359Sroberto/* Otherwise do it the hard way */
15754359Sroberto
15854359Sroberto# ifdef BSD
15954359Sroberto#  include <strings.h>
16054359Sroberto# else
16154359Sroberto#  ifdef VMS
16254359Sroberto    extern int strlen(), strncmp();
16354359Sroberto#  else
16454359Sroberto#   include <string.h>
16554359Sroberto#  endif
16654359Sroberto# endif
16754359Sroberto
16854359Sroberto#endif /* not HAVE_CONFIG_H */
16954359Sroberto
17054359Sroberto#if STDC_HEADERS
17154359Sroberto# include <stdlib.h>
17254359Sroberto#else
17354359Sroberto/*
17454359Sroberto   malloc and free should be declared in stdlib.h,
17554359Sroberto   but if you've got a K&R compiler, they probably aren't.
17654359Sroberto */
17754359Sroberto# ifdef MSDOS
17854359Sroberto#  include <malloc.h>
17954359Sroberto# else
18054359Sroberto#  ifdef VMS
18154359Sroberto     extern char *malloc();
18254359Sroberto     extern void free();
18354359Sroberto#  else
18454359Sroberto     extern char *malloc();
18554359Sroberto     extern int free();
18654359Sroberto#  endif
18754359Sroberto# endif
18854359Sroberto
18954359Sroberto#endif
19054359Sroberto
19154359Sroberto/* Define NULL (for *very* old compilers). */
19254359Sroberto#ifndef NULL
19354359Sroberto# define NULL (0)
19454359Sroberto#endif
19554359Sroberto
19654359Sroberto/*
19754359Sroberto * The ctype macros don't always handle 8-bit characters correctly.
19854359Sroberto * Compensate for this here.
19954359Sroberto */
20054359Sroberto#ifdef isascii
20154359Sroberto# undef HAVE_ISASCII		/* just in case */
20254359Sroberto# define HAVE_ISASCII 1
20354359Sroberto#else
20454359Sroberto#endif
20554359Sroberto#if STDC_HEADERS || !HAVE_ISASCII
20654359Sroberto# define is_ascii(c) 1
20754359Sroberto#else
20854359Sroberto# define is_ascii(c) isascii(c)
20954359Sroberto#endif
21054359Sroberto
21154359Sroberto#define is_space(c) (is_ascii(c) && isspace(c))
21254359Sroberto#define is_alpha(c) (is_ascii(c) && isalpha(c))
21354359Sroberto#define is_alnum(c) (is_ascii(c) && isalnum(c))
21454359Sroberto
21554359Sroberto/* Scanning macros */
21654359Sroberto#define isidchar(ch) (is_alnum(ch) || (ch) == '_')
21754359Sroberto#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_')
21854359Sroberto
21954359Sroberto/* Forward references */
22054359Srobertochar *ppdirforward();
22154359Srobertochar *ppdirbackward();
22254359Srobertochar *skipspace();
22354359Srobertochar *scanstring();
22454359Srobertoint writeblanks();
22554359Srobertoint test1();
22654359Srobertoint convert1();
22754359Sroberto
22854359Sroberto/* The main program */
22954359Srobertoint
23054359Srobertomain(argc, argv)
23154359Sroberto    int argc;
23254359Sroberto    char *argv[];
23354359Sroberto{	FILE *in = stdin;
23454359Sroberto	FILE *out = stdout;
23554359Sroberto	char *filename = 0;
23654359Sroberto	char *program_name = argv[0];
23754359Sroberto	char *output_name = 0;
23854359Sroberto#define bufsize 5000			/* arbitrary size */
23954359Sroberto	char *buf;
24054359Sroberto	char *line;
24154359Sroberto	char *more;
24254359Sroberto	char *usage =
24354359Sroberto	  "Usage: ansi2knr [--filename FILENAME] [INPUT_FILE [OUTPUT_FILE]]\n";
24454359Sroberto	/*
24554359Sroberto	 * In previous versions, ansi2knr recognized a --varargs switch.
24654359Sroberto	 * If this switch was supplied, ansi2knr would attempt to convert
24754359Sroberto	 * a ... argument to va_alist and va_dcl; if this switch was not
24854359Sroberto	 * supplied, ansi2knr would simply drop any such arguments.
24954359Sroberto	 * Now, ansi2knr always does this conversion, and we only
25054359Sroberto	 * check for this switch for backward compatibility.
25154359Sroberto	 */
25254359Sroberto	int convert_varargs = 1;
25354359Sroberto	int output_error;
25454359Sroberto
25554359Sroberto	while ( argc > 1 && argv[1][0] == '-' ) {
25654359Sroberto	  if ( !strcmp(argv[1], "--varargs") ) {
25754359Sroberto	    convert_varargs = 1;
25854359Sroberto	    argc--;
25954359Sroberto	    argv++;
26054359Sroberto	    continue;
26154359Sroberto	  }
26254359Sroberto	  if ( !strcmp(argv[1], "--filename") && argc > 2 ) {
26354359Sroberto	    filename = argv[2];
26454359Sroberto	    argc -= 2;
26554359Sroberto	    argv += 2;
26654359Sroberto	    continue;
26754359Sroberto	  }
26854359Sroberto	  fprintf(stderr, "%s: Unrecognized switch: %s\n", program_name,
26954359Sroberto		  argv[1]);
27054359Sroberto	  fprintf(stderr, usage);
27154359Sroberto	  exit(1);
27254359Sroberto	}
27354359Sroberto	switch ( argc )
27454359Sroberto	   {
27554359Sroberto	default:
27654359Sroberto		fprintf(stderr, usage);
27754359Sroberto		exit(0);
27854359Sroberto	case 3:
27954359Sroberto		output_name = argv[2];
28054359Sroberto		out = fopen(output_name, "w");
28154359Sroberto		if ( out == NULL ) {
28254359Sroberto		  fprintf(stderr, "%s: Cannot open output file %s\n",
28354359Sroberto			  program_name, output_name);
28454359Sroberto		  exit(1);
28554359Sroberto		}
28654359Sroberto		/* falls through */
28754359Sroberto	case 2:
28854359Sroberto		in = fopen(argv[1], "r");
28954359Sroberto		if ( in == NULL ) {
29054359Sroberto		  fprintf(stderr, "%s: Cannot open input file %s\n",
29154359Sroberto			  program_name, argv[1]);
29254359Sroberto		  exit(1);
29354359Sroberto		}
29454359Sroberto		if ( filename == 0 )
29554359Sroberto		  filename = argv[1];
29654359Sroberto		/* falls through */
29754359Sroberto	case 1:
29854359Sroberto		break;
29954359Sroberto	   }
30054359Sroberto	if ( filename )
30154359Sroberto	  fprintf(out, "#line 1 \"%s\"\n", filename);
30254359Sroberto	buf = malloc(bufsize);
30354359Sroberto	if ( buf == NULL )
30454359Sroberto	   {
30554359Sroberto		fprintf(stderr, "Unable to allocate read buffer!\n");
30654359Sroberto		exit(1);
30754359Sroberto	   }
30854359Sroberto	line = buf;
30954359Sroberto	while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL )
31054359Sroberto	   {
31154359Srobertotest:		line += strlen(line);
31254359Sroberto		switch ( test1(buf) )
31354359Sroberto		   {
31454359Sroberto		case 2:			/* a function header */
31554359Sroberto			convert1(buf, out, 1, convert_varargs);
31654359Sroberto			break;
31754359Sroberto		case 1:			/* a function */
31854359Sroberto			/* Check for a { at the start of the next line. */
31954359Sroberto			more = ++line;
32054359Srobertof:			if ( line >= buf + (bufsize - 1) ) /* overflow check */
32154359Sroberto			  goto wl;
32254359Sroberto			if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL )
32354359Sroberto			  goto wl;
32454359Sroberto			switch ( *skipspace(ppdirforward(more), 1) )
32554359Sroberto			  {
32654359Sroberto			  case '{':
32754359Sroberto			    /* Definitely a function header. */
32854359Sroberto			    convert1(buf, out, 0, convert_varargs);
32954359Sroberto			    fputs(more, out);
33054359Sroberto			    break;
33154359Sroberto			  case 0:
33254359Sroberto			    /* The next line was blank or a comment: */
33354359Sroberto			    /* keep scanning for a non-comment. */
33454359Sroberto			    line += strlen(line);
33554359Sroberto			    goto f;
33654359Sroberto			  default:
33754359Sroberto			    /* buf isn't a function header, but */
33854359Sroberto			    /* more might be. */
33954359Sroberto			    fputs(buf, out);
34054359Sroberto			    strcpy(buf, more);
34154359Sroberto			    line = buf;
34254359Sroberto			    goto test;
34354359Sroberto			  }
34454359Sroberto			break;
34554359Sroberto		case -1:		/* maybe the start of a function */
34654359Sroberto			if ( line != buf + (bufsize - 1) ) /* overflow check */
34754359Sroberto			  continue;
34854359Sroberto			/* falls through */
34954359Sroberto		default:		/* not a function */
35054359Srobertowl:			fputs(buf, out);
35154359Sroberto			break;
35254359Sroberto		   }
35354359Sroberto		line = buf;
35454359Sroberto	   }
35554359Sroberto	if ( line != buf )
35654359Sroberto	  fputs(buf, out);
35754359Sroberto	free(buf);
35854359Sroberto	if ( output_name ) {
35954359Sroberto	  output_error = ferror(out);
36054359Sroberto	  output_error |= fclose(out);
36154359Sroberto	} else {		/* out == stdout */
36254359Sroberto	  fflush(out);
36354359Sroberto	  output_error = ferror(out);
36454359Sroberto	}
36554359Sroberto	if ( output_error ) {
36654359Sroberto	  fprintf(stderr, "%s: error writing to %s\n", program_name,
36754359Sroberto		  (output_name ? output_name : "stdout"));
36854359Sroberto	  exit(1);
36954359Sroberto	}
37054359Sroberto	if ( in != stdin )
37154359Sroberto	  fclose(in);
37254359Sroberto	return 0;
37354359Sroberto}
37454359Sroberto
37554359Sroberto/*
37654359Sroberto * Skip forward or backward over one or more preprocessor directives.
37754359Sroberto */
37854359Srobertochar *
37954359Srobertoppdirforward(p)
38054359Sroberto    char *p;
38154359Sroberto{
38254359Sroberto    for (; *p == '#'; ++p) {
38354359Sroberto	for (; *p != '\r' && *p != '\n'; ++p)
38454359Sroberto	    if (*p == 0)
38554359Sroberto		return p;
38654359Sroberto	if (*p == '\r' && p[1] == '\n')
38754359Sroberto	    ++p;
38854359Sroberto    }
38954359Sroberto    return p;
39054359Sroberto}
39154359Srobertochar *
39254359Srobertoppdirbackward(p, limit)
39354359Sroberto    char *p;
39454359Sroberto    char *limit;
39554359Sroberto{
39654359Sroberto    char *np = p;
39754359Sroberto
39854359Sroberto    for (;; p = --np) {
39954359Sroberto	if (*np == '\n' && np[-1] == '\r')
40054359Sroberto	    --np;
40154359Sroberto	for (; np > limit && np[-1] != '\r' && np[-1] != '\n'; --np)
40254359Sroberto	    if (np[-1] == 0)
40354359Sroberto		return np;
40454359Sroberto	if (*np != '#')
40554359Sroberto	    return p;
40654359Sroberto    }
40754359Sroberto}
40854359Sroberto
40954359Sroberto/*
41054359Sroberto * Skip over whitespace, comments, and preprocessor directives,
41154359Sroberto * in either direction.
41254359Sroberto */
41354359Srobertochar *
41454359Srobertoskipspace(p, dir)
41554359Sroberto    char *p;
41654359Sroberto    int dir;			/* 1 for forward, -1 for backward */
41754359Sroberto{
41854359Sroberto    for ( ; ; ) {
41954359Sroberto	while ( is_space(*p) )
42054359Sroberto	    p += dir;
42154359Sroberto	if ( !(*p == '/' && p[dir] == '*') )
42254359Sroberto	    break;
42354359Sroberto	p += dir;  p += dir;
42454359Sroberto	while ( !(*p == '*' && p[dir] == '/') ) {
42554359Sroberto	    if ( *p == 0 )
42654359Sroberto		return p;	/* multi-line comment?? */
42754359Sroberto	    p += dir;
42854359Sroberto	}
42954359Sroberto	p += dir;  p += dir;
43054359Sroberto    }
43154359Sroberto    return p;
43254359Sroberto}
43354359Sroberto
43454359Sroberto/* Scan over a quoted string, in either direction. */
43554359Srobertochar *
43654359Srobertoscanstring(p, dir)
43754359Sroberto    char *p;
43854359Sroberto    int dir;
43954359Sroberto{
44054359Sroberto    for (p += dir; ; p += dir)
44154359Sroberto	if (*p == '"' && p[-dir] != '\\')
44254359Sroberto	    return p + dir;
44354359Sroberto}
44454359Sroberto
44554359Sroberto/*
44654359Sroberto * Write blanks over part of a string.
44754359Sroberto * Don't overwrite end-of-line characters.
44854359Sroberto */
44954359Srobertoint
45054359Srobertowriteblanks(start, end)
45154359Sroberto    char *start;
45254359Sroberto    char *end;
45354359Sroberto{	char *p;
45454359Sroberto	for ( p = start; p < end; p++ )
45554359Sroberto	  if ( *p != '\r' && *p != '\n' )
45654359Sroberto	    *p = ' ';
45754359Sroberto	return 0;
45854359Sroberto}
45954359Sroberto
46054359Sroberto/*
46154359Sroberto * Test whether the string in buf is a function definition.
46254359Sroberto * The string may contain and/or end with a newline.
46354359Sroberto * Return as follows:
46454359Sroberto *	0 - definitely not a function definition;
46554359Sroberto *	1 - definitely a function definition;
46654359Sroberto *	2 - definitely a function prototype (NOT USED);
46754359Sroberto *	-1 - may be the beginning of a function definition,
46854359Sroberto *		append another line and look again.
46954359Sroberto * The reason we don't attempt to convert function prototypes is that
47054359Sroberto * Ghostscript's declaration-generating macros look too much like
47154359Sroberto * prototypes, and confuse the algorithms.
47254359Sroberto */
47354359Srobertoint
47454359Srobertotest1(buf)
47554359Sroberto    char *buf;
47654359Sroberto{	char *p = buf;
47754359Sroberto	char *bend;
47854359Sroberto	char *endfn;
47954359Sroberto	int contin;
48054359Sroberto
48154359Sroberto	if ( !isidfirstchar(*p) )
48254359Sroberto	  return 0;		/* no name at left margin */
48354359Sroberto	bend = skipspace(ppdirbackward(buf + strlen(buf) - 1, buf), -1);
48454359Sroberto	switch ( *bend )
48554359Sroberto	   {
48654359Sroberto	   case ';': contin = 0 /*2*/; break;
48754359Sroberto	   case ')': contin = 1; break;
48854359Sroberto	   case '{': return 0;		/* not a function */
48954359Sroberto	   case '}': return 0;		/* not a function */
49054359Sroberto	   default: contin = -1;
49154359Sroberto	   }
49254359Sroberto	while ( isidchar(*p) )
49354359Sroberto	  p++;
49454359Sroberto	endfn = p;
49554359Sroberto	p = skipspace(p, 1);
49654359Sroberto	if ( *p++ != '(' )
49754359Sroberto	  return 0;		/* not a function */
49854359Sroberto	p = skipspace(p, 1);
49954359Sroberto	if ( *p == ')' )
50054359Sroberto	  return 0;		/* no parameters */
50154359Sroberto	/* Check that the apparent function name isn't a keyword. */
50254359Sroberto	/* We only need to check for keywords that could be followed */
50354359Sroberto	/* by a left parenthesis (which, unfortunately, is most of them). */
50454359Sroberto	   {	static char *words[] =
50554359Sroberto		   {	"asm", "auto", "case", "char", "const", "double",
50654359Sroberto			"extern", "float", "for", "if", "int", "long",
50754359Sroberto			"register", "return", "short", "signed", "sizeof",
50854359Sroberto			"static", "switch", "typedef", "unsigned",
50954359Sroberto			"void", "volatile", "while", 0
51054359Sroberto		   };
51154359Sroberto		char **key = words;
51254359Sroberto		char *kp;
51354359Sroberto		unsigned len = endfn - buf;
51454359Sroberto
51554359Sroberto		while ( (kp = *key) != 0 )
51654359Sroberto		   {	if ( strlen(kp) == len && !strncmp(kp, buf, len) )
51754359Sroberto			  return 0;	/* name is a keyword */
51854359Sroberto			key++;
51954359Sroberto		   }
52054359Sroberto	   }
52154359Sroberto	   {
52254359Sroberto	       char *id = p;
52354359Sroberto	       int len;
52454359Sroberto	       /*
52554359Sroberto		* Check for identifier1(identifier2) and not
52654359Sroberto		* identifier1(void), or identifier1(identifier2, xxxx).
52754359Sroberto		*/
52854359Sroberto
52954359Sroberto	       while ( isidchar(*p) )
53054359Sroberto		   p++;
53154359Sroberto	       len = p - id;
53254359Sroberto	       p = skipspace(p, 1);
53354359Sroberto	       if (*p == ',' ||
53454359Sroberto		   (*p == ')' && (len != 4 || strncmp(id, "void", 4)))
53554359Sroberto		   )
53654359Sroberto		   return 0;	/* not a function */
53754359Sroberto	   }
53854359Sroberto	/*
53954359Sroberto	 * If the last significant character was a ), we need to count
54054359Sroberto	 * parentheses, because it might be part of a formal parameter
54154359Sroberto	 * that is a procedure.
54254359Sroberto	 */
54354359Sroberto	if (contin > 0) {
54454359Sroberto	    int level = 0;
54554359Sroberto
54654359Sroberto	    for (p = skipspace(buf, 1); *p; p = skipspace(p + 1, 1))
54754359Sroberto		level += (*p == '(' ? 1 : *p == ')' ? -1 : 0);
54854359Sroberto	    if (level > 0)
54954359Sroberto		contin = -1;
55054359Sroberto	}
55154359Sroberto	return contin;
55254359Sroberto}
55354359Sroberto
55454359Sroberto/* Convert a recognized function definition or header to K&R syntax. */
55554359Srobertoint
55654359Srobertoconvert1(buf, out, header, convert_varargs)
55754359Sroberto    char *buf;
55854359Sroberto    FILE *out;
55954359Sroberto    int header;			/* Boolean */
56054359Sroberto    int convert_varargs;	/* Boolean */
56154359Sroberto{	char *endfn;
56254359Sroberto	char *p;
56354359Sroberto	/*
56454359Sroberto	 * The breaks table contains pointers to the beginning and end
56554359Sroberto	 * of each argument.
56654359Sroberto	 */
56754359Sroberto	char **breaks;
56854359Sroberto	unsigned num_breaks = 2;	/* for testing */
56954359Sroberto	char **btop;
57054359Sroberto	char **bp;
57154359Sroberto	char **ap;
57254359Sroberto	char *vararg = 0;
57354359Sroberto
57454359Sroberto	/* Pre-ANSI implementations don't agree on whether strchr */
57554359Sroberto	/* is called strchr or index, so we open-code it here. */
57654359Sroberto	for ( endfn = buf; *(endfn++) != '('; )
57754359Sroberto	  ;
57854359Srobertotop:	p = endfn;
57954359Sroberto	breaks = (char **)malloc(sizeof(char *) * num_breaks * 2);
58054359Sroberto	if ( breaks == NULL )
58154359Sroberto	   {	/* Couldn't allocate break table, give up */
58254359Sroberto		fprintf(stderr, "Unable to allocate break table!\n");
58354359Sroberto		fputs(buf, out);
58454359Sroberto		return -1;
58554359Sroberto	   }
58654359Sroberto	btop = breaks + num_breaks * 2 - 2;
58754359Sroberto	bp = breaks;
58854359Sroberto	/* Parse the argument list */
58954359Sroberto	do
59054359Sroberto	   {	int level = 0;
59154359Sroberto		char *lp = NULL;
59254359Sroberto		char *rp = NULL;
59354359Sroberto		char *end = NULL;
59454359Sroberto
59554359Sroberto		if ( bp >= btop )
59654359Sroberto		   {	/* Filled up break table. */
59754359Sroberto			/* Allocate a bigger one and start over. */
59854359Sroberto			free((char *)breaks);
59954359Sroberto			num_breaks <<= 1;
60054359Sroberto			goto top;
60154359Sroberto		   }
60254359Sroberto		*bp++ = p;
60354359Sroberto		/* Find the end of the argument */
60454359Sroberto		for ( ; end == NULL; p++ )
60554359Sroberto		   {	switch(*p)
60654359Sroberto			   {
60754359Sroberto			   case ',':
60854359Sroberto				if ( !level ) end = p;
60954359Sroberto				break;
61054359Sroberto			   case '(':
61154359Sroberto				if ( !level ) lp = p;
61254359Sroberto				level++;
61354359Sroberto				break;
61454359Sroberto			   case ')':
61554359Sroberto				if ( --level < 0 ) end = p;
61654359Sroberto				else rp = p;
61754359Sroberto				break;
61854359Sroberto			   case '/':
61954359Sroberto				if (p[1] == '*')
62054359Sroberto				    p = skipspace(p, 1) - 1;
62154359Sroberto				break;
62254359Sroberto			   case '"':
62354359Sroberto			       p = scanstring(p, 1) - 1;
62454359Sroberto			       break;
62554359Sroberto			   default:
62654359Sroberto				;
62754359Sroberto			   }
62854359Sroberto		   }
62954359Sroberto		/* Erase any embedded prototype parameters. */
63054359Sroberto		if ( lp && rp )
63154359Sroberto		  writeblanks(lp + 1, rp);
63254359Sroberto		p--;			/* back up over terminator */
63354359Sroberto		/* Find the name being declared. */
63454359Sroberto		/* This is complicated because of procedure and */
63554359Sroberto		/* array modifiers. */
63654359Sroberto		for ( ; ; )
63754359Sroberto		   {	p = skipspace(p - 1, -1);
63854359Sroberto			switch ( *p )
63954359Sroberto			   {
64054359Sroberto			   case ']':	/* skip array dimension(s) */
64154359Sroberto			   case ')':	/* skip procedure args OR name */
64254359Sroberto			   {	int level = 1;
64354359Sroberto				while ( level )
64454359Sroberto				 switch ( *--p )
64554359Sroberto				   {
64654359Sroberto				   case ']': case ')':
64754359Sroberto				       level++;
64854359Sroberto				       break;
64954359Sroberto				   case '[': case '(':
65054359Sroberto				       level--;
65154359Sroberto				       break;
65254359Sroberto				   case '/':
65354359Sroberto				       if (p > buf && p[-1] == '*')
65454359Sroberto					   p = skipspace(p, -1) + 1;
65554359Sroberto				       break;
65654359Sroberto				   case '"':
65754359Sroberto				       p = scanstring(p, -1) + 1;
65854359Sroberto				       break;
65954359Sroberto				   default: ;
66054359Sroberto				   }
66154359Sroberto			   }
66254359Sroberto				if ( *p == '(' && *skipspace(p + 1, 1) == '*' )
66354359Sroberto				   {	/* We found the name being declared */
66454359Sroberto					while ( !isidfirstchar(*p) )
66554359Sroberto					  p = skipspace(p, 1) + 1;
66654359Sroberto					goto found;
66754359Sroberto				   }
66854359Sroberto				break;
66954359Sroberto			   default:
67054359Sroberto				goto found;
67154359Sroberto			   }
67254359Sroberto		   }
67354359Srobertofound:		if ( *p == '.' && p[-1] == '.' && p[-2] == '.' )
67454359Sroberto		  {	if ( convert_varargs )
67554359Sroberto			  {	*bp++ = "va_alist";
67654359Sroberto				vararg = p-2;
67754359Sroberto			  }
67854359Sroberto			else
67954359Sroberto			  {	p++;
68054359Sroberto				if ( bp == breaks + 1 )	/* sole argument */
68154359Sroberto				  writeblanks(breaks[0], p);
68254359Sroberto				else
68354359Sroberto				  writeblanks(bp[-1] - 1, p);
68454359Sroberto				bp--;
68554359Sroberto			  }
68654359Sroberto		   }
68754359Sroberto		else
68854359Sroberto		   {	while ( isidchar(*p) ) p--;
68954359Sroberto			*bp++ = p+1;
69054359Sroberto		   }
69154359Sroberto		p = end;
69254359Sroberto	   }
69354359Sroberto	while ( *p++ == ',' );
69454359Sroberto	*bp = p;
69554359Sroberto	/* Make a special check for 'void' arglist */
69654359Sroberto	if ( bp == breaks+2 )
69754359Sroberto	   {	p = skipspace(breaks[0], 1);
69854359Sroberto		if ( !strncmp(p, "void", 4) )
69954359Sroberto		   {	p = skipspace(p+4, 1);
70054359Sroberto			if ( p == breaks[2] - 1 )
70154359Sroberto			   {	bp = breaks;	/* yup, pretend arglist is empty */
70254359Sroberto				writeblanks(breaks[0], p + 1);
70354359Sroberto			   }
70454359Sroberto		   }
70554359Sroberto	   }
70654359Sroberto	/* Put out the function name and left parenthesis. */
70754359Sroberto	p = buf;
70854359Sroberto	while ( p != endfn ) putc(*p, out), p++;
70954359Sroberto	/* Put out the declaration. */
71054359Sroberto	if ( header )
71154359Sroberto	  {	fputs(");", out);
71254359Sroberto		for ( p = breaks[0]; *p; p++ )
71354359Sroberto		  if ( *p == '\r' || *p == '\n' )
71454359Sroberto		    putc(*p, out);
71554359Sroberto	  }
71654359Sroberto	else
71754359Sroberto	  {	for ( ap = breaks+1; ap < bp; ap += 2 )
71854359Sroberto		  {	p = *ap;
71954359Sroberto			while ( isidchar(*p) )
72054359Sroberto			  putc(*p, out), p++;
72154359Sroberto			if ( ap < bp - 1 )
72254359Sroberto			  fputs(", ", out);
72354359Sroberto		  }
72454359Sroberto		fputs(")  ", out);
72554359Sroberto		/* Put out the argument declarations */
72654359Sroberto		for ( ap = breaks+2; ap <= bp; ap += 2 )
72754359Sroberto		  (*ap)[-1] = ';';
72854359Sroberto		if ( vararg != 0 )
72954359Sroberto		  {	*vararg = 0;
73054359Sroberto			fputs(breaks[0], out);		/* any prior args */
73154359Sroberto			fputs("va_dcl", out);		/* the final arg */
73254359Sroberto			fputs(bp[0], out);
73354359Sroberto		  }
73454359Sroberto		else
73554359Sroberto		  fputs(breaks[0], out);
73654359Sroberto	  }
73754359Sroberto	free((char *)breaks);
73854359Sroberto	return 0;
73954359Sroberto}
740