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