diff options
Diffstat (limited to 'unproto')
-rw-r--r-- | unproto/Makefile | 97 | ||||
-rw-r--r-- | unproto/README | 130 | ||||
-rw-r--r-- | unproto/Shar.Headers | 50 | ||||
-rw-r--r-- | unproto/cpp.sh | 9 | ||||
-rw-r--r-- | unproto/error.c | 78 | ||||
-rw-r--r-- | unproto/error.h | 4 | ||||
-rw-r--r-- | unproto/example.c | 140 | ||||
-rw-r--r-- | unproto/example.out | 173 | ||||
-rw-r--r-- | unproto/stdarg.h | 38 | ||||
-rw-r--r-- | unproto/symbol.c | 161 | ||||
-rw-r--r-- | unproto/symbol.h | 11 | ||||
-rw-r--r-- | unproto/tok_class.c | 180 | ||||
-rw-r--r-- | unproto/tok_io.c | 464 | ||||
-rw-r--r-- | unproto/tok_pool.c | 104 | ||||
-rw-r--r-- | unproto/token.h | 47 | ||||
-rw-r--r-- | unproto/unproto.1 | 79 | ||||
-rw-r--r-- | unproto/unproto.c | 749 | ||||
-rw-r--r-- | unproto/varargs.c | 32 | ||||
-rw-r--r-- | unproto/vstring.c | 91 | ||||
-rw-r--r-- | unproto/vstring.h | 14 |
20 files changed, 2651 insertions, 0 deletions
diff --git a/unproto/Makefile b/unproto/Makefile new file mode 100644 index 0000000..638be25 --- /dev/null +++ b/unproto/Makefile @@ -0,0 +1,97 @@ +# @(#) Makefile 1.3 91/12/01 12:37:57 + +## BEGIN CONFIGURATION STUFF + +# For maximal flexibility, the "/lib/cpp | unproto" pipeline can be +# packaged as an executable shell script (see the provided "cpp.sh" script) +# that should be installed as "/whatever/cpp". This script should then be +# specified to the C compiler as a non-default preprocessor. +# +PROG = unproto +PIPE = + +# For maximal performance, the overhead of shell script inpretation can +# be eliminated by having the unprototyper program itself open the pipe +# to the preprocessor. In that case, define the PIPE_THROUGH_CPP macro +# as the path name of the default C preprocessor (usually "/lib/cpp"), +# install the unprototyper as "/whatever/cpp" and specify that to the C +# compiler as a non-default preprocessor. +# +# PROG = cpp +# PIPE = -DPIPE_THROUGH_CPP=\"/lib/cpp\" + +# Some compilers complain about some #directives. The following is only a +# partial solution, because the directives are still seen by /lib/cpp. +# Be careful with filtering out #pragma, because some non-ANSI compilers +# (SunOS) rely on its use. +# +# SKIP = -DIGNORE_DIRECTIVES=\"pragma\",\"foo\",\"bar\" +# +SKIP = + +# If you need support for functions that implement ANSI-style variable +# length argument lists, edit the stdarg.h file provided with this +# package so that it contains the proper definitions for your machine. + +## END CONFIGURATION STUFF + +SHELL = /bin/sh + +CFILES = tok_io.c tok_class.c tok_pool.c unproto.c vstring.c symbol.c error.c +HFILES = error.h token.h vstring.h symbol.h +SCRIPTS = cpp.sh +SAMPLES = stdarg.h varargs.c example.c example.out +SOURCES = README Makefile $(CFILES) $(HFILES) $(SCRIPTS) $(SAMPLES) +FILES = $(SOURCES) unproto.1 +OBJECTS = tok_io.o tok_class.o tok_pool.o unproto.o vstring.o symbol.o error.o + +ifneq ($(TOPDIR),) +include $(TOPDIR)/Make.defs +CFLAGS=$(CCFLAGS) $(PIPE) $(SKIP) +else +CC=bcc +CFLAGS=-O $(PIPE) $(SKIP) +LDFLAGS=-s +endif + +#CFLAGS = -O -pg -Dstatic= $(PIPE) $(SKIP) +#CFLAGS = -g $(PIPE) $(SKIP) -DDEBUG + +$(PROG): $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(MALLOC) + +install: $(PROG) + install -d $(LIBDIR) + install -m 755 $(PROG) $(LIBDIR)/unproto + +# For linting, enable all bells and whistles. + +lint: + lint -DPIPE_THROUGH_CPP=\"foo\" -DIGNORE_DIRECTIVES=\"foo\",\"bar\" \ + $(CFILES) + +# Testing requires that the program is compiled with -DDEBUG + +test: $(PROG) example.c example.out + ./cpp example.c >example.tmp + @echo the following diff command should produce no output + diff -b example.out example.tmp + rm -f example.tmp + +shar: $(FILES) + @shar $(FILES) + +archive: + $(ARCHIVE) $(SOURCES) + +clean: + rm -f *.o core cpp unproto mon.out varargs.o varargs example.tmp + +error.o : error.c token.h error.h Makefile +symbol.o : symbol.c token.h symbol.h Makefile +tok_class.o : tok_class.c error.h vstring.h token.h symbol.h Makefile +tok_io.o : tok_io.c token.h vstring.h error.h Makefile +tok_pool.o : tok_pool.c token.h vstring.h Makefile +unproto.o : unproto.c vstring.h stdarg.h token.h error.h symbol.h Makefile +varargs.o : varargs.c stdarg.h Makefile +vstring.o : vstring.c vstring.h Makefile diff --git a/unproto/README b/unproto/README new file mode 100644 index 0000000..6856d3a --- /dev/null +++ b/unproto/README @@ -0,0 +1,130 @@ +@(#) README 1.3 91/12/01 12:37:55 + +unproto - ANSI C to old C converter + +Purpose: + +This is a filter that sits in between the C preprocessor and the next C +compiler stage, and that on the fly rewrites ANSI C to old C. Its +primary application is to compile ANSI C software in UNIX environments +that do not (yet) have an ANSI C compiler and that cannot run GCC +because of technical or political problems. + +The filter leaves old-style C alone, and de-ANSI-fies function +headings, function pointer type declarations (and casts), function type +declarations, and combinations thereof. Many freely-distributable +unprotoizers have problems with the latter because they are based on a +non-recursive algorithm or even make assumptions about code layout. + +The unprototyper has support for systems that require special tricks +for variadic functions (fortunately, many don't). A sample `stdarg.h' +file is provided with support for sparc, mc68k, 80x86, vax and others. + +The program has been tested on a Sun SLC running SunOS 4.1.1 and on a +80286 PC clone running Microport's version of System V Release 2. It +runs at about the same speed as /lib/cpp, so it should have negigible +impact on compilation times. + +Problems solved with this release: + +Minor: the program was originally intended for the compilation of +already tested ANSI C source, so that diagnostics from the native C +compiler would be sufficient. The present release produces better +diagnostics, so that it can also be used for program development. + +Major: the template Makefile suggested that all #pragma directives be +filtered out. This turns out to be a bad idea because some non-ANSI +compilers (SunOS) rely on #pragmas to correctly handle the unusual flow +control caused by vfork(2), setjmp(3) etcetera. A warning to this +effect has been added to the Makefile. + +No changes were made to the actual filter logic; output should be +identical to that of the previous release. + +Restrictions: + +Other ANSI-isms are just passed on without modification, such as +trigraphs, token pasting (##), #pragmas, stringizing (#text) and +string concatenation ("string1" "string2"). + +The unprototyper does not understand declarations of (object). The +result (the object disappears) will be a syntax error so this should +not go by unnoticed. + +Some C programmers rely on ANSI-style prototypes for the automatic type +conversion of function arguments. The unprototyper, however, does not +generate casts. The lack of automatic conversion between integral +and/or pointer argument types should not be a problem in environments +where sizeof(int) == sizeof(long) == sizeof(pointer). A more serious +problem is the lack of automatic type conversions beteen integral and +floating-point function argument types. Let lint(1) be your friend. + +Operation: + +This package implements an non-default C preprocessor (the output from +the default C preprocessor being piped through the unprototyper). How +one tells the C compiler to use an non-default preprocessor program is +somewhat compiler-dependent: + + SunOS 4.x: cc -Qpath directory_with_non-default_cpp ... + + SysV Rel2: cc -Bdirectory_with_non-default_cpp/ -tp ... + +Your C compiler manual should provide the necessary information. + +On some systems the lint(1) command is just a shell script, and writing +a version that uses the unprototyper should not be too hard. With SunOS +4.x, /usr/bin/lint is not a shell script, but it does accept the same +syntax as the cc(1) command for the specification of a non-default +compiler pass. + +You may have to do some research on the lint command provided with your +own machine. + +Configuration: + +Check the contents of the `stdarg.h' file provided with this package. +This file serves a dual purpose. It should be included by C source file +that implements ANSI-style variadic functions. It is also used to +configure the `unproto' program so that it emits the proper magic for +the `...' construct. + +The `stdarg.h' file contains definitions for the sparc architecture and +for architectures that pass arguments via the stack (usually OK for +80*86, mc68k and vax C compilers). Risc processors often need special +tricks. These are usually found in the file `/usr/include/varargs.h'. + +The file `varargs.c' provided with this package can be used to verify +that the `stdarg.h' file has been set up correctly. + +For maximal flexibility, you can use the `cpp' shell script provided +with this package to set up the pipe between the default C preprocessor +and the unprototyper command. The script assumes that the unprototyper +binary is called `unproto'. See the cpp.sh script and the Makefile for +details. + +The overhead of shell-script interpretation can be avoided by having +the unprototyper itself open the pipe to the C preprocessor. In this +case, the `unproto.c' source file should be compiled with the +`PIPE_THROUGH_CPP' macro defined as the pathname of the C preprocessor +(usually `/lib/cpp'), and the unprototyper binary should be called +`cpp'. See the Makefile for details. + +Installation: + +Install the `stdarg.h' include file and the `unproto.1' manual page in +suitable places. + +If you use the `cpp' shell script to pipe the preprocessor output +through the unprototyper program, install the `unproto' binary in a +place where the `cpp' shell script can find it, and install the `cpp' +shell script in a suitable place. + +If the unprototyper itself opens the pipe to the C preprocessor (i.e. +the unprototyper was built with the `PIPE_THROUGH_CPP' macro defined), +install the `cpp' unprototyper binary in a suitable place. + + Wietse Venema + wietse@wzv.win.tue.nl + Eindhoven University of Technology + The Netherlands diff --git a/unproto/Shar.Headers b/unproto/Shar.Headers new file mode 100644 index 0000000..d707a4f --- /dev/null +++ b/unproto/Shar.Headers @@ -0,0 +1,50 @@ +Newsgroups: comp.sources.misc +From: wietse@wzv.win.tue.nl (Wietse Venema) +Subject: v26i094: unproto - compile ANSI C with old C compiler, Part01/02 +Message-ID: <csm-v26i094=unproto.083344@sparky.IMD.Sterling.COM> +X-Md4-Signature: 69685c0673793ce6c539657a3d1de2ab +Date: Sun, 1 Dec 1991 14:35:08 GMT +Approved: kent@sparky.imd.sterling.com + +Submitted-by: wietse@wzv.win.tue.nl (Wietse Venema) +Posting-number: Volume 26, Issue 94 +Archive-name: unproto/part01 +Environment: SYSV2, SunOS +Supersedes: unproto: Volume 23, Issue 12-13 + +This is a filter that sits in between the C preprocessor and the next C +compiler stage, and that on the fly rewrites ANSI C to old C. Its +primary application is to compile ANSI C software in UNIX environments +that do not (yet) have an ANSI C compiler and that cannot run GCC +because of technical or political problems. + +Problems solved with this release: + +Minor: the program was originally intended for the compilation of +already tested ANSI C source, so that diagnostics from the native C +compiler would be sufficient. The present release produces better +diagnostics, so that it can also be used for program development. + +Major: the template Makefile suggested that all #pragma directives be +filtered out. This turns out to be a bad idea because some non-ANSI +compilers (SunOS) rely on #pragmas to correctly handle the unusual flow +control caused by vfork(2), setjmp(3) etcetera. A warning to this +effect has been added to the Makefile. + +No changes were made to the actual filter logic; output should be +identical to that of the previous release. + +Newsgroups: comp.sources.misc +From: wietse@wzv.win.tue.nl (Wietse Venema) +Subject: v26i095: unproto - compile ANSI C with old C compiler, Part02/02 +Message-ID: <1991Dec1.143532.21763@sparky.imd.sterling.com> +X-Md4-Signature: e41270a11de3ed19fd262a273924fc94 +Date: Sun, 1 Dec 1991 14:35:32 GMT +Approved: kent@sparky.imd.sterling.com + +Submitted-by: wietse@wzv.win.tue.nl (Wietse Venema) +Posting-number: Volume 26, Issue 95 +Archive-name: unproto/part02 +Environment: SYSV2, SunOS +Supersedes: unproto: Volume 23, Issue 12-13 + diff --git a/unproto/cpp.sh b/unproto/cpp.sh new file mode 100644 index 0000000..8122b68 --- /dev/null +++ b/unproto/cpp.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# @(#) cpp.sh 1.2 91/09/22 21:21:43 + +# Unprototypeing preprocessor for non-ANSI C compilers. Define __STDC__ +# if you have enough courage. You will have to modify this script if +# your cc(1) command specifies output file names to the preprocessor. + +exec /lib/cpp "$@" -Dconst= -Dvolatile= | unproto diff --git a/unproto/error.c b/unproto/error.c new file mode 100644 index 0000000..bc9702b --- /dev/null +++ b/unproto/error.c @@ -0,0 +1,78 @@ +/*++ +/* NAME +/* error 3 +/* SUMMARY +/* diagnostics +/* PACKAGE +/* unproto +/* SYNOPSIS +/* #include "error.h" +/* +/* void error(quit, text) +/* int quit; +/* char *text; +/* +/* void error_where(quit, path, line, text) +/* int quit; +/* char *path; +/* int line; +/* char *text; +/* DESCRIPTION +/* The routines in this file print a diagnostic (text) and optionally +/* terminate the program (quit != 0) with exit status "quit". +/* +/* error() provides a default context, i.e. the source-file +/* coordinate of the last read token. +/* +/* error_where() allows the caller to explicitly specify context: path +/* is a source-file name, and line is a line number. +/* +/* context is ignored if the line number is zero or if the path +/* is an empty string. +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/11/30 21:10:35 +/* VERSION/RELEASE +/* 1.1 +/*--*/ + +static char error_sccsid[] = "@(#) error.c 1.1 91/11/30 21:10:35"; + +/* C library */ + +#include <stdio.h> + +void exit(); + +/* Application-specific stuff */ + +#include "token.h" +#include "error.h" + +/* error - report problem (implicit context) and optionally quit */ + +void error(quit, text) +int quit; +char *text; +{ + error_where(quit, curr_path, curr_line, text); +} + +/* error_where - report problem (explicit context) and optionally quit */ + +void error_where(quit, path, line, text) +int quit; +char *path; +int line; +char *text; +{ + if (line && path[0]) + fprintf(stderr, "%s, line %d: ", path, line); + fprintf(stderr, "%s\n", text); + if (quit) + exit(quit); +} diff --git a/unproto/error.h b/unproto/error.h new file mode 100644 index 0000000..579906d --- /dev/null +++ b/unproto/error.h @@ -0,0 +1,4 @@ +/* @(#) error.h 1.1 91/11/30 21:10:36 */ + +extern void error(); /* default context */ +extern void error_where(); /* user-specified context */ diff --git a/unproto/example.c b/unproto/example.c new file mode 100644 index 0000000..003df4a --- /dev/null +++ b/unproto/example.c @@ -0,0 +1,140 @@ + /* + * @(#) example.c 1.2 91/09/22 21:21:45 + * + * Examples of things that can be done with the unproto package + */ + + /* + * New-style argument list with structured argument, one field being pointer + * to function returning pointer to function with function-pointer argument + */ + +x(struct { + struct { + int (*(*foo) (int (*arg1) (double))) (float arg2); + } foo; +} baz) { + return (0); +} + + /* + * Old-style argument list with new-style argument type, declaration + * embedded within block. Plus a couple assignments with function calls that + * look like casts. + */ + +foo(bar) +int (*(*bar) (float)) (int); +{ + int (*baz) (int) = (int (*) (int)) 0, + y = (y * (*baz) (y)), + *(*z) (int) = (int *(*) (int)) 0; + + struct { int (*foo)(int); } *(*s)(int) = + (struct { int (*foo)(int); } *(*)(int)) 0; + + { + y = (y * (*baz) (y)); + } + { + z = (int *(*) (int)) 0; + } + { + s = (struct { int (*foo)(int); } *(*)(int)) 0; + } + + return (0); +} + +/* Multiple declarations in one statement */ + +test1() +{ + int foo2,*(*(*bar)(int))(float),*baz(double); +} + +/* Discriminate declarations from executable statements */ + +test2(char *y) +{ + int foo = 5,atoi(char *); + + foo = 5,atoi(y); +} + +/* Declarations without explicit type */ + +test3,test4(int); + +test5(int y) +{ + { + test3; + } + { + test4(y); + } +} + +test6[1],test7(int); + +test7(int x) +{ + { + test6[1]; + } + { + test7(x); + } +} + +/* Checking a complicated cast */ + +struct { + struct { + int (*f)(int), o; + } bar; +} (*baz2)(int) = (struct { struct { int (*f)(int), o; } bar; } (*)(int)) 0; + +/* Distinguish things with the same shape but with different meaning */ + +test8(x) +{ + { + struct { + int foo; + } bar(int); + } + { + do { + int foo; + } while (x); + } +} + +/* Do not think foo(*bar) is a function pointer declaration */ + +test9(char *bar) +{ + foo(*bar); +} + +/* another couple of special-cased words. */ + +test10(int x) +{ + { + int test10(int); + do test10(x); + while (x); + } + { + return test10(x); + } +} + +test11(int *x) +{ + while (*x) + (putchar(*x++)); +} diff --git a/unproto/example.out b/unproto/example.out new file mode 100644 index 0000000..6dc75f3 --- /dev/null +++ b/unproto/example.out @@ -0,0 +1,173 @@ +# 1 "example.c" + + + + + + + + + + + +x + + + +(baz) +# 16 "example.c" +struct { struct { int (*(*foo)())(); } foo;} baz; +# 16 "example.c" +{/*1*/ + /* end dcls */return (0); +}/*1*/ + + + + + + + +foo +(bar)int (*(*bar)())(); +{/*1*/ + int (*baz)() = (int (*)()) 0, + y = (y * (*baz)(y)), + *(*z)() = (int *(*)()) 0; + + struct {/*2*/ int (*foo)(); }/*2*/ *(*s)() = + (struct { int (*foo)(); } *(*)()) 0; + + /* end dcls */{/*2*/ + y /* end dcls */= (y * (*baz)(y)); + }/*2*/ + {/*2*/ + z /* end dcls */= (int *(*)()) 0; + }/*2*/ + {/*2*/ + s /* end dcls */= (struct { int (*foo)(); } *(*)()) 0; + }/*2*/ + + return (0); +}/*1*/ + + + +test1 +() +# 52 "example.c" +{/*1*/ + int foo2,*(*(*bar)())(),*baz(); +}/*1*/ + + + +test2 +(y) +# 59 "example.c" +char *y; +# 59 "example.c" +{/*1*/ + int foo = 5,atoi(); + + foo /* end dcls */= 5,atoi(y); +}/*1*/ + + + +test3,test4(); + +test5 +(y) +# 70 "example.c" +int y; +# 70 "example.c" +{/*1*/ + /* end dcls */{/*2*/ + test3/* end dcls */; + }/*2*/ + {/*2*/ + test4/* end dcls */(y); + }/*2*/ +}/*1*/ + +test6[1],test7(); + +test7 +(x) +# 82 "example.c" +int x; +# 82 "example.c" +{/*1*/ + /* end dcls */{/*2*/ + test6/* end dcls */[1]; + }/*2*/ + {/*2*/ + test7/* end dcls */(x); + }/*2*/ +}/*1*/ + + + +struct {/*1*/ + struct {/*2*/ + int (*f)(), o; + }/*2*/ bar; +}/*1*/ (*baz2)() = (struct { struct { int (*f)(), o; } bar; } (*)()) 0; + + + +test8 +(x) +# 102 "example.c" +{/*1*/ + /* end dcls */{/*2*/ + struct {/*3*/ + int foo; + }/*3*/ bar(); + }/*2*/ + {/*2*/ + /* end dcls */do {/*3*/ + int foo; + }/*3*/ while (x); + }/*2*/ +}/*1*/ + + + +test9 +(bar) +# 118 "example.c" +char *bar; +# 118 "example.c" +{/*1*/ + foo/* end dcls */(*bar); +}/*1*/ + + + +test10 +(x) +# 125 "example.c" +int x; +# 125 "example.c" +{/*1*/ + /* end dcls */{/*2*/ + int test10(); + /* end dcls */do test10(x); + while (x); + }/*2*/ + {/*2*/ + /* end dcls */return test10(x); + }/*2*/ +}/*1*/ + +test11 +(x) +# 137 "example.c" +int *x; +# 137 "example.c" +{/*1*/ + /* end dcls */while (*x) + (putchar(*x++)); +}/*1*/ diff --git a/unproto/stdarg.h b/unproto/stdarg.h new file mode 100644 index 0000000..978529b --- /dev/null +++ b/unproto/stdarg.h @@ -0,0 +1,38 @@ + /* + * @(#) stdarg.h 1.2 91/11/30 21:10:39 + * + * Sample stdarg.h file for use with the unproto filter. + * + * This file serves two purposes. + * + * 1 - As an include file for use with ANSI-style C source that implements + * variadic functions. + * + * 2 - To configure the unproto filter itself. If the _VA_ALIST_ macro is + * defined, its value will appear in the place of the "..." in argument + * lists of variadic function *definitions* (not declarations). + * + * Compilers that pass arguments via the stack can use the default code at the + * end of this file (this usually applies for the VAX, MC68k and 80*86 + * architectures). + * + * RISC-based systems often need special tricks. An example of the latter is + * given for the SPARC architecture. Read your /usr/include/varargs.h for + * more information. + * + * You can use the varargs.c program provided with the unproto package to + * verify that the stdarg.h file has been set up correctly. + */ + +#ifdef sparc +# define _VA_ALIST_ "__builtin_va_alist" + typedef char *va_list; +# define va_start(ap, p) (ap = (char *) &__builtin_va_alist) +# define va_arg(ap, type) ((type *) __builtin_va_arg_incr((type *) ap))[0] +# define va_end(ap) +#else /* vax, mc68k, 80*86 */ + typedef char *va_list; +# define va_start(ap, p) (ap = (char *) (&(p)+1)) +# define va_arg(ap, type) ((type *) (ap += sizeof(type)))[-1] +# define va_end(ap) +#endif diff --git a/unproto/symbol.c b/unproto/symbol.c new file mode 100644 index 0000000..0871f63 --- /dev/null +++ b/unproto/symbol.c @@ -0,0 +1,161 @@ +/*++ +/* NAME +/* symbol 3 +/* SUMMARY +/* rudimentary symbol table package +/* SYNOPSIS +/* #include "symbol.h" +/* +/* void sym_init() +/* +/* void sym_enter(name, type) +/* char *name; +/* int type; +/* +/* struct symbol *sym_find(name) +/* char *name; +/* DESCRIPTION +/* This is a rudimentary symbol-table package, just enough to +/* keep track of a couple of C keywords. +/* +/* sym_init() primes the table with C keywords. At present, most of +/* the keywords that have to do with types are left out. +/* We need a different strategy to detect type definitions because +/* we do not keep track of typedef names. +/* +/* sym_enter() adds an entry to the symbol table. +/* +/* sym_find() locates a symbol table entry (it returns 0 if +/* it is not found). +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/11/30 21:10:33 +/* VERSION/RELEASE +/* 1.2 +/*--*/ + +static char symbol_sccsid[] = "@(#) symbol.c 1.2 91/11/30 21:10:33"; + +/* C library */ + +extern char *strcpy(); +extern char *malloc(); + +/* Application-specific stuff */ + +#include "error.h" +#include "token.h" +#include "symbol.h" + +#define SYM_TABSIZE 20 + +static struct symbol *sym_tab[SYM_TABSIZE] = {0,}; + +/* More string stuff. Maybe it should go to an #include file. */ + +#define STREQ(x,y) (*(x) == *(y) && strcmp((x),(y)) == 0) + +/* hash - hash a string; original author: P. J. Weinberger at Bell Labs. */ + +static unsigned hash(s, size) +register char *s; +unsigned size; +{ + register unsigned long h = 0; + register unsigned long g; + + /* + * For a performance comparison with the hash function presented in K&R, + * first edition, see the "Dragon" book by Aho, Sethi and Ullman. + */ + + while (*s) { + h = (h << 4) + *s++; + if (g = (h & 0xf0000000)) { + h ^= (g >> 24); + h ^= g; + } + } + return (h % size); +} + +/* sym_enter - enter symbol into table */ + +void sym_enter(name, type) +char *name; +int type; +{ + struct symbol *s; + int where; + + if ((s = (struct symbol *) malloc(sizeof(*s))) == 0 + || (s->name = malloc(strlen(name) + 1)) == 0) + error(1, "out of memory"); + (void) strcpy(s->name, name); + s->type = type; + + where = hash(name, SYM_TABSIZE); + s->next = sym_tab[where]; + sym_tab[where] = s; +} + +/* sym_find - locate symbol definition */ + +struct symbol *sym_find(name) +register char *name; +{ + register struct symbol *s; + + /* + * This function is called for almost every "word" token, so it better be + * fast. + */ + + for (s = sym_tab[hash(name, SYM_TABSIZE)]; s; s = s->next) + if (STREQ(name, s->name)) + return (s); + return (0); +} + + /* + * Initialization data for symbol table. We do not enter keywords for types. + * We use a different strategy to detect type declarations because we do not + * keep track of typedef names. + */ + +struct sym { + char *name; + int tokno; +}; + +static struct sym syms[] = { + "if", TOK_CONTROL, + "else", TOK_CONTROL, + "while", TOK_CONTROL, + "do", TOK_CONTROL, + "switch", TOK_CONTROL, + "case", TOK_CONTROL, + "default", TOK_CONTROL, + "return", TOK_CONTROL, + "continue", TOK_CONTROL, + "break", TOK_CONTROL, + "goto", TOK_CONTROL, + "struct", TOK_COMPOSITE, + "union", TOK_COMPOSITE, + 0, +}; + +/* sym_init - enter known keywords into symbol table */ + +void sym_init() +{ + register struct sym *p; + + for (p = syms; p->name; p++) + sym_enter(p->name, p->tokno); +} + diff --git a/unproto/symbol.h b/unproto/symbol.h new file mode 100644 index 0000000..0711c1f --- /dev/null +++ b/unproto/symbol.h @@ -0,0 +1,11 @@ +/* @(#) symbol.h 1.1 91/09/22 21:21:42 */ + +struct symbol { + char *name; /* symbol name */ + int type; /* symbol type */ + struct symbol *next; +}; + +extern void sym_enter(); /* add symbol to table */ +extern struct symbol *sym_find(); /* locate symbol */ +extern void sym_init(); /* prime the table */ diff --git a/unproto/tok_class.c b/unproto/tok_class.c new file mode 100644 index 0000000..e25b695 --- /dev/null +++ b/unproto/tok_class.c @@ -0,0 +1,180 @@ +/*++ +/* NAME +/* tok_class 3 +/* SUMMARY +/* token classification +/* PACKAGE +/* unproto +/* SYNOPSIS +/* #include "token.h" +/* +/* struct token *tok_class(skip) +/* int skip; +/* DESCRIPTION +/* tok_class() collects a single and composite tokens, and +/* recognizes keywords. +/* At present, the only composite tokens are ()-delimited, +/* comma-separated lists. +/* +/* The skip argument has the same meaning as with the tok_get() +/* function. +/* DIAGNOSTICS +/* The code complains if input terminates in the middle of a list. +/* BUGS +/* Does not preserve white space at the beginning of a list element +/* or after the end of a list. +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/11/30 21:10:28 +/* VERSION/RELEASE +/* 1.3 +/*--*/ + +static char class_sccsid[] = "@(#) tok_class.c 1.3 91/11/30 21:10:28"; + +/* C library */ + +#include <stdio.h> + +extern char *strcpy(); + +/* Application-specific stuff */ + +#include "error.h" +#include "vstring.h" +#include "token.h" +#include "symbol.h" + +static struct token *tok_list(); +static void tok_list_struct(); +static void tok_list_append(); + +/* tok_space_append - append trailing space except after list */ + +#define tok_space_append(list,t) { \ + if (list == 0 /* leading space*/ \ + || list->tokno == TOK_LIST) \ + tok_free(t); \ + else \ + tok_list_append(list, t); \ + } + +/* tok_class - discriminate single tokens, keywords, and composite tokens */ + +struct token *tok_class(skip) +int skip; +{ + register struct token *t; + register struct symbol *s; + + if (t = tok_get(skip)) { + switch (t->tokno) { + case '(': /* beginning of list */ + t = tok_list(t); + break; + case TOK_WORD: /* look up keyword */ + if (s = sym_find(t->vstr->str)) + t->tokno = s->type; + break; + } + } + return (t); +} + +/* tok_list - collect ()-delimited, comma-separated list of tokens */ + +static struct token *tok_list(t) +struct token *t; +{ + register struct token *list = tok_alloc(); + char filename[BUFSIZ]; + int lineno; + + /* Save context of '(' for diagnostics. */ + + strcpy(filename, curr_path); + lineno = curr_line; + + list->tokno = TOK_LIST; + list->head = list->tail = t; +#ifdef DEBUG + strcpy(list->vstr->str, "LIST"); +#endif + + for (;;) { + if ((t = tok_get(DO_WSPACE)) == 0) { /* skip blanks */ + error_where(0, filename, lineno, "unmatched '('"); + return (list); /* do not waste any data */ + } + switch (t->tokno) { + case ')': /* end of list */ + tok_free(t); + return (list); + case '{': /* struct/union type */ + tok_list_struct(list->tail, t); + break; + case TOK_WSPACE: /* preserve trailing blanks */ + tok_space_append(list->tail->tail, t); /* except after list */ + break; + case '\n': /* preserve line count */ + tok_flush(t); + break; + case ',': /* list separator */ + tok_list_append(list, t); + break; + case '(': /* beginning of list */ + tok_list_append(list->tail, tok_list(t)); + break; + default: /* ordinary token */ + tok_list_append(list->tail, t); + break; + } + } +} + +/* tok_list_struct - collect structured type info within list */ + +static void tok_list_struct(list, t) +register struct token *list; +register struct token *t; +{ + tok_list_append(list, t); + + while (t = tok_class(DO_WSPACE)) { + switch (t->tokno) { + case '\n': /* preserve line count */ + tok_flush(t); + break; + case TOK_WSPACE: /* preserve trailing blanks */ + tok_space_append(list->tail, t); /* except after list */ + break; + case '{': /* recurse */ + tok_list_struct(list, t); + break; + case '}': /* done */ + tok_list_append(list, t); + return; + default: /* other */ + tok_list_append(list, t); + break; + } + } +} + +/* tok_list_append - append data to list */ + +static void tok_list_append(h, t) +struct token *h; +struct token *t; +{ + if (h->head == 0) { + h->head = h->tail = t; + } else { + h->tail->next = t; + h->tail = t; + } +} diff --git a/unproto/tok_io.c b/unproto/tok_io.c new file mode 100644 index 0000000..ab1129b --- /dev/null +++ b/unproto/tok_io.c @@ -0,0 +1,464 @@ +/*++ +/* NAME +/* tok_io 3 +/* SUMMARY +/* token I/O +/* PACKAGE +/* unproto +/* SYNOPSIS +/* #include "token.h" +/* +/* struct token *tok_get(skip_flag) +/* int skip_flag; +/* +/* void tok_unget(t) +/* struct token *t; +/* +/* void tok_flush(t) +/* struct token *t; +/* +/* void tok_show(t) +/* struct token *t; +/* +/* void put_str(s) +/* char *s; +/* +/* void put_ch(c) +/* int c; +/* +/* void show_line_control() +/* +/* char curr_path[]; +/* int curr_line; +/* DESCRIPTION +/* These functions read from stdin and write to stdout. The +/* output functions maintain some memory so that two successive +/* words will always be separated by white space. +/* +/* The input routines eliminate backslash-newline from the input. +/* +/* tok_get() reads the next token from standard input. It returns +/* a null pointer when the end of input is reached. If the skip_flag +/* argument is nonzero, white space (except newline) will be skipped. +/* +/* tok_unget() implements a limited amount of token push back. +/* +/* tok_show() displays the contents of a (possibly composite) token +/* on the standard output. +/* +/* tok_flush() displays the contents of a (possibly composite) token +/* on the standard output and makes it available for re-use. +/* +/* put_str() writes a null-terminated string to standard output. +/* +/* put_ch() writes one character to standard output. +/* +/* show_line_control() displays the line number of the next line +/* to be written to standard output, in a format suitable for the C +/* compiler parser phase. +/* +/* The curr_path[] and curr_line variables contain the input file name and +/* line number of the most recently read token. +/* BUGS +/* The tokenizer is just good enough for the unproto filter. +/* As a benefit, it is quite fast. +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/11/30 21:10:26 +/* VERSION/RELEASE +/* 1.2 +/*--*/ + +static char io_sccsid[] = "@(#) tok_io.c 1.2 91/11/30 21:10:26"; + +/* C library */ + +#include <stdio.h> +#include <ctype.h> + +extern char *strchr(); +extern char *malloc(); +extern char *realloc(); +extern char *strcpy(); + +/* Application-specific stuff */ + +#include "token.h" +#include "vstring.h" +#include "error.h" + +/* Stuff to keep track of original source file name and position */ + +char curr_path[BUFSIZ]; /* current file name */ +int curr_line = 0; /* # of last read line */ + +/* Forward declarations */ + +static void read_quoted(); +static void read_comment(); + +/* Buffered i/o stuff */ + +static struct vstring *buf = 0; /* read-ahead buffer */ +static char *bp = ""; /* buffer position */ + +#ifdef DEBUG +#define INITBUF 1 /* small initial buffer size */ +#else +#define INITBUF BUFSIZ /* reasonable initial buffer size */ +#endif + +#define input() (*bp ? *bp++ : next_line()) +#define unput(c) (*--bp = (c)) + +#define TOK_BUFSIZE 5 /* token push-back buffer size */ + +static struct token *tok_buf[TOK_BUFSIZE]; +static int tok_bufpos = 0; + +/* Type of last token sent to output, for pretty printing */ + +static int last_tok = 0; + +/* Directives that should be ignored. */ + +#ifdef IGNORE_DIRECTIVES + +static char *ignore_directives[] = { + IGNORE_DIRECTIVES, + 0, +}; + +#endif + +/* Modified string and ctype stuff. */ + +#define STREQUAL(x,y) (*(x) == *(y) && strcmp((x),(y)) == 0) + +#define ISALNUM(c) (isalnum(c) || (c) == '_') +#define ISALPHA(c) (isalpha(c) || (c) == '_') +#define ISSPACE(c) (isspace(c) && c != '\n') +#define ISDOT(c) (c == '.') + +/* Collect all characters that satisfy one condition */ + +#define COLLECT(v,c,cond) { \ + register struct vstring *vs = v; \ + register char *cp = vs->str; \ + *cp++ = c; \ + for (;;) { \ + if ((c = input()) == 0) { \ + break; \ + } else if (cond) { \ + if (VS_ADDCH(vs, cp, c) == 0) \ + error(1, "out of memory"); \ + } else { \ + unput(c); \ + break; \ + } \ + } \ + *cp = 0; \ + } + +/* do_control - parse control line, uses tok_get() */ + +static int do_control() +{ + struct token *t1; + struct token *t2; + int pass_thru = 1; /* 0 = ignore, 1 = output */ + + (void) input(); /* skip the hash */ + + if (t1 = tok_get(NO_WSPACE)) { + switch (t1->tokno) { + + /* + * In case of line number control, the remainder of the line has + * the format: linenumber "pathname". + */ + case TOK_NUMBER: + if (t2 = tok_get(NO_WSPACE)) { + if (t2->tokno == '"') { + curr_line = atoi(t1->vstr->str) - 1; + strcpy(curr_path, t2->vstr->str); + } + tok_free(t2); + } + break; + +#ifdef IGNORE_DIRECTIVES + case TOK_WORD: + /* Optionally ignore other #directives, such as #pragma. */ + { + char **cpp; + char *cp = t1->vstr->str; + + for (cpp = ignore_directives; *cpp; cpp++) { + if (STREQUAL(cp, *cpp)) { + pass_thru = 0; + break; + } + } + } + break; +#endif + } + tok_free(t1); + } + return (pass_thru); +} + +/* next_line - read one logical line, handle #control */ + +static int next_line() +{ + register int c; + register char *cp; + + /* Allocate buffer upon first entry */ + + if (buf == 0) + buf = vs_alloc(INITBUF); + + for (;;) { + cp = buf->str; + + /* Account for EOF and line continuations */ + + while ((c = getchar()) != EOF) { + if (VS_ADDCH(buf, cp, c) == 0) /* store character */ + error(1, "out of memory"); + if (c == '\n') { /* real end of line */ + curr_line++; + break; + } else if (c == '\\') { + if ((c = getchar()) == EOF) { /* XXX strip backslash-EOF */ + break; + } else if (c == '\n') { /* strip backslash-newline */ + curr_line++; + put_ch('\n'); /* preserve line count */ + cp--; /* un-store backslash */ + } else { + ungetc(c, stdin); /* keep backslash-other */ + } + } + } + *cp = 0; + bp = buf->str; + + /* Account for EOF and #control */ + + switch (bp[0]) { + case 0: /* EOF */ + return (0); + case '#': /* control */ + if (do_control()) + fputs(buf->str, stdout); /* pass through */ + else + putchar('\n'); /* filter out */ + break; + default: /* non-control */ + return (input()); + } + } +} + +/* tok_unget - push back one token */ + +void tok_unget(t) +register struct token *t; +{ + if (tok_bufpos >= TOK_BUFSIZE) + error(1, "too much pushback"); + tok_buf[tok_bufpos++] = t; +} + +/* tok_get - get next token */ + +struct token *tok_get(skip_flag) +int skip_flag; +{ + register struct token *t; + register int c; + int d; + + /* Use push-back token, if any. */ + + if (tok_bufpos) { + t = tok_buf[--tok_bufpos]; + return (t); + } + + /* + * Get one from the pool and fill it in. The loop is here in case we + * should skip white-space tokens, which happens in a minority of all + * cases. + */ + + t = tok_alloc(); + + for (;;) { + if ((c = input()) == 0) { + tok_free(t); + return (0); + } else if (!isascii(c)) { + t->vstr->str[0] = c; + t->vstr->str[1] = 0; + t->tokno = TOK_OTHER; + return (t); + } else if (c == '"' || c == '\'') { + read_quoted(t, c); + t->tokno = c; + return (t); + } else if (ISALPHA(c)) { + COLLECT(t->vstr, c, ISALNUM(c)); + t->tokno = TOK_WORD; + return (t); + } else if (isdigit(c)) { + COLLECT(t->vstr, c, isdigit(c)); + t->tokno = TOK_NUMBER; + return (t); + } else if (ISSPACE(c)) { + COLLECT(t->vstr, c, ISSPACE(c)); + if (skip_flag) + continue; + t->tokno = TOK_WSPACE; + return (t); + } else if (ISDOT(c)) { + COLLECT(t->vstr, c, ISDOT(c)); + t->tokno = TOK_OTHER; + return (t); + } else { + t->vstr->str[0] = c; + if (c == '/') { + if ((d = input()) == '*') { + t->vstr->str[1] = d; /* comment */ + read_comment(t->vstr); + if (skip_flag) + continue; + t->tokno = TOK_WSPACE; + return (t); + } else { + unput(d); + } + } + t->vstr->str[1] = 0; + t->tokno = c; + return (t); + } + } +} + +/* read_qouted - read string or character literal */ + +static void read_quoted(t, ch) +register struct token *t; +int ch; +{ + register char *cp = t->vstr->str; + register int c; + + *cp++ = ch; + + while (c = input()) { + if (c == '\n') { /* newline in string */ + unput(c); + break; + } + if (VS_ADDCH(t->vstr, cp, c) == 0) /* store character */ + error(1, "out of memory"); + if (c == ch) /* end of string */ + break; + if (c == '\\') /* eat next character */ + if ((c = input()) != 0 && VS_ADDCH(t->vstr, cp, c) == 0) + error(1, "out of memory"); + } + *cp = 0; + return; +} + +/* read_comment - stuff a whole comment into one huge token */ + +static void read_comment(vs) +register struct vstring *vs; +{ + register char *cp = vs->str + 2; /* skip slash star */ + register int c; + register int d; + + while (c = input()) { + if (VS_ADDCH(vs, cp, c) == 0) + error(1, "out of memory"); + if (c == '*') { + if ((d = input()) == '/') { + if (VS_ADDCH(vs, cp, d) == 0) + error(1, "out of memory"); + break; + } else { + unput(d); + } + } + } + *cp = 0; +} + +/* put_str - output a string */ + +void put_str(s) +char *s; +{ + fputs(s, stdout); + last_tok = s[0]; /* XXX */ +#ifdef DEBUG + fflush(stdout); +#endif +} + +/* put_ch - put character */ + +void put_ch(c) +int c; +{ + last_tok = putchar(c); +#ifdef DEBUG + fflush(stdout); +#endif +} + +/* tok_show - output (possibly composite) token */ + +void tok_show(t) +struct token *t; +{ + register struct token *p; + register struct token *s; + + switch (t->tokno) { + case TOK_LIST: + for (s = t->head; s; s = s->next) { + put_ch(s->tokno); /* opening paren or ',' */ + for (p = s->head; p; p = p->next) + tok_show(p); + } + put_ch(')'); /* closing paren */ + break; + case TOK_WORD: + if (ISALPHA(last_tok)) + putchar(' '); + /* FALLTRHOUGH */ + default: + fputs(t->vstr->str, stdout); /* token contents */ + last_tok = t->vstr->str[0]; +#ifdef DEBUG + fflush(stdout); +#endif + if (t->head) /* trailing blanks */ + for (p = t->head; p; p = p->next) + tok_show(p); + } +} diff --git a/unproto/tok_pool.c b/unproto/tok_pool.c new file mode 100644 index 0000000..c2a9665 --- /dev/null +++ b/unproto/tok_pool.c @@ -0,0 +1,104 @@ +/*++ +/* NAME +/* tok_pool 3 +/* SUMMARY +/* maintain pool of unused token structures +/* PACKAGE +/* unproto +/* SYNOPSIS +/* #include "token.h" +/* +/* struct token *tok_alloc() +/* +/* void tok_free(t) +/* struct token *t; +/* DESCRIPTION +/* tok_alloc() and tok_free() maintain a pool of unused token +/* structures. +/* +/* tok_alloc() takes the first free token structure from the pool +/* or allocates a new one if the pool is empty. +/* +/* tok_free() adds a (possibly composite) token structure to the pool. +/* BUGS +/* The pool never shrinks. +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/09/01 23:08:36 +/* VERSION/RELEASE +/* 1.1 +/*--*/ + +static char pool_sccsid[] = "@(#) tok_pool.c 1.1 91/09/01 23:08:36"; + +/* C library */ + +extern char *malloc(); + +/* Application-specific stuff */ + +#include "token.h" +#include "vstring.h" + +extern void error(); + +#define TOKLEN 5 /* initial string buffer length */ + +struct token *tok_pool = 0; /* free token pool */ + +/* tok_alloc - allocate token structure from pool or heap */ + +struct token *tok_alloc() +{ + register struct token *t; + + if (tok_pool) { /* re-use an old one */ + t = tok_pool; + tok_pool = t->next; + } else { /* create a new one */ + if ((t = (struct token *) malloc(sizeof(struct token))) == 0 + || (t->vstr = vs_alloc(TOKLEN)) == 0) + error(1, "out of memory"); + } + t->next = t->head = t->tail = 0; +#ifdef DEBUG + strcpy(t->vstr->str, "BUSY"); +#endif + return (t); +} + +/* tok_free - return (possibly composite) token to pool of free tokens */ + +void tok_free(t) +register struct token *t; +{ +#ifdef DEBUG + /* Check if we are freeing free token */ + + register struct token *p; + + for (p = tok_pool; p; p = p->next) + if (p == t) + error(1, "freeing free token"); +#endif + + /* Free neighbours and subordinates first */ + + if (t->next) + tok_free(t->next); + if (t->head) + tok_free(t->head); + + /* Free self */ + + t->next = tok_pool; + t->head = t->tail = 0; + tok_pool = t; +#ifdef DEBUG + strcpy(t->vstr->str, "FREE"); +#endif +} diff --git a/unproto/token.h b/unproto/token.h new file mode 100644 index 0000000..672039d --- /dev/null +++ b/unproto/token.h @@ -0,0 +1,47 @@ +/* @(#) token.h 1.3 91/11/30 21:10:37 */ + +struct token { + int tokno; /* token value, see below */ + int len; /* string or list length */ + struct vstring *vstr; /* token contents */ + struct token *next; + struct token *head; + struct token *tail; +}; + +/* Special token values */ + +#define TOK_LIST 256 /* () delimited list */ +#define TOK_WORD 257 /* keyword or identifier */ +#define TOK_NUMBER 258 /* number */ +#define TOK_WSPACE 259 /* white space except newline */ +#define TOK_OTHER 260 /* other multi-char token */ +#define TOK_CONTROL 261 /* flow control keyword */ +#define TOK_COMPOSITE 262 /* struct or union */ + +/* Input/output functions and macros */ + +extern struct token *tok_get(); /* read next single token */ +extern void tok_show(); /* display (composite) token */ +extern struct token *tok_class(); /* classify tokens */ +extern void put_ch(); /* write character */ +extern void put_str(); /* write string */ +extern void tok_unget(); /* stuff token back into input */ + +#define tok_flush(t) (tok_show(t), tok_free(t)) + +/* tok_get() and tok_class() options */ + +#define DO_WSPACE 0 /* retain space, tab */ +#define NO_WSPACE 1 /* skip space, tab */ + +/* Memory management */ + +struct token *tok_alloc(); /* allocate token storage */ +extern void tok_free(); /* re-cycle storage */ + +/* Context */ + +extern char curr_path[]; /* current path name */ +extern int curr_line; /* current line number */ +#define show_line_control() printf("# %d %s\n", curr_line, curr_path); diff --git a/unproto/unproto.1 b/unproto/unproto.1 new file mode 100644 index 0000000..671e917 --- /dev/null +++ b/unproto/unproto.1 @@ -0,0 +1,79 @@ +.TH UNPROTO 1 +.ad +.fi +.SH NAME +unproto +\- +ANSI C to old C converter +.SH PACKAGE +.na +.nf +unproto +.SH SYNOPSIS +.na +.nf +/lib/cpp ... | unproto + +/somewhere/cpp ... +.SH DESCRIPTION +.ad +.fi +This document describes a filter that sits between the +C preprocessor (usually \fI/lib/cpp\fP) and the next C compiler +pass. It rewrites ANSI-C style function headers, function type +declarations, function pointer types, and function pointer casts +to old style. Other ANSI-isms are passed on without modification +(token pasting, pragmas, etcetera). + +For maximal flexibility, the "cpp | unproto" pipeline can be +packaged as an executable shell script named "/somewhere/cpp". +This script should then be specified to the C compiler as a +non-default preprocessor. + +The overhead of shell script interpretation can be avoided by +having the unprototyper itself open the pipe to the preprocessor. +In that case, the source should be compiled with the PIPE_THROUGH_CPP +macro defined (usually as "/lib/cpp"), and the resulting binary +should be installed as "/somewhere/cpp". +.SH SEE ALSO +.na +.nf +.ad +.fi +cc(1), how to specify a non-default C preprocessor. + +Some versions of the lint command are implemented as a shell +script. It should require only minor modification for integration +with the unprotoizer. Other versions of the lint command accept the same +command syntax as the C compiler for the specification of a non-default +preprocessor. Some research may be needed. +.SH DIAGNOSTICS +.ad +.fi +The progam will complain if it unexpectedly +reaches the end of input. +.SH BUGS +.ad +.fi +Should be run on preprocessed source only, i.e. after macro expansion. + +Declarations of (whatever) are misunderstood and will result in +syntax errors. + +Does not generate explicit type casts for function argument +expressions. +.SH AUTHOR(S) +.na +.nf +Wietse Venema (wietse@wzv.win.tue.nl) +Eindhoven University of Technology +Department of Mathematics and Computer Science +Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +.SH LAST MODIFICATION +.na +.nf +91/09/22 21:21:35 +.SH VERSION/RELEASE +.na +.nf +1.2 diff --git a/unproto/unproto.c b/unproto/unproto.c new file mode 100644 index 0000000..9ca26ce --- /dev/null +++ b/unproto/unproto.c @@ -0,0 +1,749 @@ +/*++ +/* NAME +/* unproto 1 +/* SUMMARY +/* ANSI C to old C converter +/* PACKAGE +/* unproto +/* SYNOPSIS +/* /lib/cpp ... | unproto +/* +/* /somewhere/cpp ... +/* DESCRIPTION +/* This document describes a filter that sits between the +/* C preprocessor (usually \fI/lib/cpp\fP) and the next C compiler +/* pass. It rewrites ANSI-C style function headers, function type +/* declarations, function pointer types, and function pointer casts +/* to old style. Other ANSI-isms are passed on without modification +/* (token pasting, pragmas, etcetera). +/* +/* For maximal flexibility, the "cpp | unproto" pipeline can be +/* packaged as an executable shell script named "/somewhere/cpp". +/* This script should then be specified to the C compiler as a +/* non-default preprocessor. It will not work if your C compiler +/* specifies output file names to the preprocessor. +/* +/* The overhead of shell script interpretation can be avoided by +/* having the unprototyper itself open the pipe to the preprocessor. +/* In that case, the source should be compiled with the PIPE_THROUGH_CPP +/* macro defined (usually as "/lib/cpp"), and the resulting binary +/* should be installed as "/somewhere/cpp". +/* SEE ALSO +/* .ad +/* .fi +/* cc(1), how to specify a non-default C preprocessor. +/* +/* Some versions of the lint command are implemented as a shell +/* script. It should require only minor modification for integration +/* with the unprotoizer. Other versions of the lint command accept the same +/* command syntax as the C compiler for the specification of a non-default +/* preprocessor. Some research may be needed. +/* DIAGNOSTICS +/* The progam will complain if it unexpectedly +/* reaches the end of input. +/* BUGS +/* Should be run on preprocessed source only, i.e. after macro expansion. +/* +/* Declarations of (whatever) are misunderstood and will result in +/* syntax errors. +/* +/* Does not generate explicit type casts for function argument +/* expressions. +/* AUTHOR(S) +/* Wietse Venema (wietse@wzv.win.tue.nl) +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/09/22 21:21:35 +/* VERSION/RELEASE +/* 1.2 +/*--*/ + +static char unproto_sccsid[] = "@(#) unproto.c 1.3 91/11/30 21:10:30"; + +/* C library */ + +#include <stdio.h> +#include <errno.h> + +extern void exit(); +extern int optind; +extern char *optarg; +extern int getopt(); + +/* Application-specific stuff */ + +#include "vstring.h" +#include "stdarg.h" +#include "token.h" +#include "error.h" +#include "symbol.h" + +/* Forward declarations. */ + +static struct token *dcl_flush(); +static void block_flush(); +static void block_dcls(); +static struct token *show_func_ptr_type(); +static struct token *show_struct_type(); +static void show_arg_name(); +static void show_type(); +static void pair_flush(); +static void check_cast(); + +#define check_cast_flush(t) (check_cast(t), tok_free(t)) + +#ifdef PIPE_THROUGH_CPP +static int pipe_stdin_through_cpp(); +#endif + +/* Disable debugging printfs while preserving side effects. */ + +#ifdef DEBUG +#define DPRINTF printf +#else +#define DPRINTF (void) +#endif + +/* An attempt to make some complicated expressions a bit more readable. */ + +#define STREQ(x,y) (*(x) == *(y) && !strcmp((x),(y))) + +#define LAST_ARG_AND_EQUAL(s,c) ((s)->next == 0 && (s)->head \ + && ((s)->head == (s)->tail) \ + && (STREQ((s)->head->vstr->str, (c)))) + +#define LIST_BEGINS_WITH_STAR(s) (s->head->head && s->head->head->tokno == '*') + +#define IS_FUNC_PTR_TYPE(s) (s->tokno == TOK_LIST && s->next \ + && s->next->tokno == TOK_LIST \ + && LIST_BEGINS_WITH_STAR(s)) + +/* main - driver */ + +int main(argc, argv) +int argc; +char **argv; +{ + register struct token *t; +#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */ + int cpp_status; + int wait_pid; + int cpp_pid; + + cpp_pid = pipe_stdin_through_cpp(argv); +#else + if ( argc > 1 ) { + if( freopen(argv[1], "r", stdin) == 0 ) { + fprintf(stderr, "Cannot open '%s'\n", argv[1]); + exit(9); + } + } + if ( argc > 2 ) { + if( freopen(argv[2], "w", stdout) == 0 ) { + fprintf(stderr, "Cannot open '%s'\n", argv[2]); + exit(9); + } + } +#endif + + sym_init(); /* prime the symbol table */ + + while (t = tok_class(DO_WSPACE)) { + if (t = dcl_flush(t)) { /* try declaration */ + if (t->tokno == '{') { /* examine rejected token */ + block_flush(t); /* body */ + } else { + tok_flush(t); /* other, recover */ + } + } + } + +#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */ + while ((wait_pid = wait(&cpp_status)) != -1 && wait_pid != cpp_pid) + /* void */ ; + return (wait_pid != cpp_pid || cpp_status != 0); +#else + return (0); +#endif +} + +#ifdef PIPE_THROUGH_CPP /* pipe through /lib/cpp */ + +/* pipe_stdin_through_cpp - avoid shell script overhead */ + +static int pipe_stdin_through_cpp(argv) +char **argv; +{ + int pipefds[2]; + int pid; + char **cpptr = argv; + + /* + * With most UNIX implementations, the second non-option argument to + * /lib/cpp specifies the output file. If an output file other than + * stdout is specified, we must force /lib/cpp to write to stdout, and we + * must redirect our own standard output to the specified output file. + */ + +#define IS_OPTION(cp) ((cp)[0] == '-' && (cp)[1] != 0) + + /* Skip to first non-option argument, if any. */ + + while (*++cpptr && IS_OPTION(*cpptr)) + /* void */ ; + + /* + * Assume that the first non-option argument is the input file name. The + * next argument could be the output destination or an option (System V + * Release 2 /lib/cpp gets the options *after* the file arguments). + */ + + if (*cpptr && *++cpptr && **cpptr != '-') { + + /* + * The first non-option argument is followed by another argument that + * is not an option ("-stuff") or a hyphen ("-"). Redirect our own + * standard output before we clobber the file name. + */ + + if (freopen(*cpptr, "w", stdout) == 0) { + perror(*cpptr); + exit(1); + } + /* Clobber the file name argument so that /lib/cpp writes to stdout */ + + *cpptr = "-"; + } + /* Set up the pipe that connects /lib/cpp to our standard input. */ + + if (pipe(pipefds)) { + perror("pipe"); + exit(1); + } + switch (pid = fork()) { + case -1: /* error */ + perror("fork"); + exit(1); + case 0: /* child */ + close(pipefds[0]); /* close reading end */ + close(1); /* connect stdout to pipe */ + if (dup(pipefds[1]) != 1) + error(1, "dup() problem"); + close(pipefds[1]); /* close redundant fd */ + execv(PIPE_THROUGH_CPP, argv); + perror(PIPE_THROUGH_CPP); + exit(1); + default: /* parent */ + close(pipefds[1]); /* close writing end */ + close(0); /* connect stdin to pipe */ + if (dup(pipefds[0]) != 0) + error(1, "dup() problem"); + close(pipefds[0]); /* close redundant fd */ + return (pid); + } +} + +#endif + +/* header_flush - rewrite new-style function header to old style */ + +static void header_flush(t) +register struct token *t; +{ + register struct token *s; + + /* Do argument names, but suppress void and rewrite trailing ... */ + + if (LAST_ARG_AND_EQUAL(t->head, "void")) { + put_str("()\n"); /* no arguments */ + } else { + for (s = t->head; s; s = s->next) { /* foreach argument... */ + if (LAST_ARG_AND_EQUAL(s, "...")) { +#ifdef _VA_ALIST_ /* see ./stdarg.h */ + put_ch(s->tokno); /* ',' */ + put_str(_VA_ALIST_); /* varargs magic */ +#endif + } else { + put_ch(s->tokno); /* opening '(' or ',' */ + show_arg_name(s); /* extract argument name */ + } + } + put_str(")\n"); /* closing ')' */ + } + + /* Do argument types, but suppress void and trailing ... */ + + if (!LAST_ARG_AND_EQUAL(t->head, "void")) { + for (s = t->head; s; s = s->next) { /* foreach argument... */ + if (!LAST_ARG_AND_EQUAL(s, "...")) { + if (s->head != s->tail) { /* really new-style argument? */ + show_line_control(); /* fix line number */ + show_type(s); /* rewrite type info */ + put_str(";\n"); + } + } + } + } + tok_free(t); + show_line_control(); /* because '{' follows */ +} + +/* show_arg_name - extract argument name from argument type info */ + +static void show_arg_name(s) +register struct token *s; +{ + if (s->head) { + register struct token *p; + register struct token *t = 0; + + /* Find the last interesting item. */ + + for (p = s->head; p; p = p->next) { + if (p->tokno == TOK_WORD) { + t = p; /* remember last word */ + } else if (IS_FUNC_PTR_TYPE(p)) { + t = p; /* or function pointer */ + p = p->next; + } + } + + /* Extract argument name from last interesting item. */ + + if (t) { + if (t->tokno == TOK_LIST) + show_arg_name(t->head); /* function pointer, recurse */ + else + tok_show(t); /* print last word */ + } + } +} + +/* show_type - rewrite type to old-style syntax */ + +static void show_type(s) +register struct token *s; +{ + register struct token *p; + + for (p = s->head; p; p = p->next) { + if (IS_FUNC_PTR_TYPE(p)) { + p = show_func_ptr_type(p); /* function pointer type */ + } else { + tok_show(p); /* other */ + } + } +} + +/* show_func_ptr_type - display function_pointer type using old-style syntax */ + +static struct token *show_func_ptr_type(t) +struct token *t; +{ + register struct token *s; + + /* + * Rewrite (list1) (list2) to (list1) (). Only (list1) is given to us; + * the caller must have verified the presence of (list2). Account for the + * rare case that (list1) is a comma-separated list. That should be an + * error, but we do not want to waste any information. + */ + + for (s = t->head; s; s = s->next) { + put_ch(s->tokno); /* opening paren or ',' */ + show_type(s); /* recurse */ + } + put_str(")()"); /* closing paren */ + return (t->next); +} + +/* show_struct_type - display structured type, rewrite function-pointer types */ + +static struct token *show_struct_type(p) +register struct token *p; +{ + tok_show(p); /* opening brace */ + + while (p->next) { /* XXX cannot return 0 */ + p = p->next; + if (IS_FUNC_PTR_TYPE(p)) { + p = show_func_ptr_type(p); /* function-pointer member */ + } else if (p->tokno == '{') { + p = show_struct_type(p); /* recurse */ + } else { + tok_show(p); /* other */ + if (p->tokno == '}') { + return (p); /* done */ + } + } + } + DPRINTF("/* missing '}' */"); + return (p); +} + +/* is_func_ptr_cast - recognize function-pointer type cast */ + +static int is_func_ptr_cast(t) +register struct token *t; +{ + register struct token *p; + + /* + * Examine superficial structure. Require (list1) (list2). Require that + * list1 begins with a star. + */ + + if (!IS_FUNC_PTR_TYPE(t)) + return (0); + + /* + * Make sure that there is no name in (list1). Do not worry about + * unexpected tokens, because the compiler will complain anyway. + */ + + for (p = t->head->head; p; p = p->next) { + switch (p->tokno) { + case TOK_LIST: /* recurse */ + return (is_func_ptr_cast(p)); + case TOK_WORD: /* name in list */ + return (0); + } + } + return (1); /* no name found */ +} + +/* check_cast - display ()-delimited, comma-separated list */ + +static void check_cast(t) +struct token *t; +{ + register struct token *s; + register struct token *p; + + /* + * Rewrite function-pointer types and function-pointer casts. Do not + * blindly rewrite (*list1)(list2) to (*list1)(). Function argument lists + * are about the only thing we can discard without provoking diagnostics + * from the compiler. + */ + + for (s = t->head; s; s = s->next) { + put_ch(s->tokno); /* opening paren or ',' */ + for (p = s->head; p; p = p->next) { + switch (p->tokno) { + case TOK_LIST: + if (is_func_ptr_cast(p)) { /* not: IS_FUNC_PTR_TYPE(p) */ + p = show_func_ptr_type(p); /* or we might take away */ + } else { /* function-call arguments */ + check_cast(p); /* recurse */ + } + break; + case '{': + p = show_struct_type(p); /* rewrite func. ptr. types */ + break; + default: + tok_show(p); + break; + } + } + } + put_ch(')'); /* closing paren */ +} + +/* block_dcls - on the fly rewrite decls/initializers at start of block */ + +static void block_dcls() +{ + register struct token *t; + + /* + * Away from the top level, a declaration should be preceded by type or + * storage-class information. That is why inside blocks, structs and + * unions we insist on reading one word before passing the _next_ token + * to the dcl_flush() function. + * + * Struct and union declarations look the same everywhere: we make an + * exception for these more regular constructs and pass the "struct" and + * "union" tokens to the type_dcl() function. + */ + + while (t = tok_class(DO_WSPACE)) { + switch (t->tokno) { + case TOK_WSPACE: /* preserve white space */ + case '\n': /* preserve line count */ + tok_flush(t); + break; + case TOK_WORD: /* type declarations? */ + tok_flush(t); /* advance to next token */ + t = tok_class(DO_WSPACE); /* null return is ok */ + case TOK_COMPOSITE: /* struct or union */ + if ((t = dcl_flush(t)) == 0) + break; + /* FALLTRHOUGH */ + default: /* end of declarations */ + DPRINTF("/* end dcls */"); + /* FALLTRHOUGH */ + case '}': /* end of block */ + tok_unget(t); + return; + } + } +} + +/* block_flush - rewrite struct, union or statement block on the fly */ + +static void block_flush(t) +register struct token *t; +{ + static int count = 0; + + tok_flush(t); + DPRINTF("/*%d*/", ++count); + + /* + * Rewrite function pointer types in declarations and function pointer + * casts in initializers at start of block. + */ + + block_dcls(); + + /* Remainder of block: only rewrite function pointer casts. */ + + while (t = tok_class(DO_WSPACE)) { + if (t->tokno == TOK_LIST) { + check_cast_flush(t); + } else if (t->tokno == '{') { + block_flush(t); + } else { + tok_flush(t); + if (t->tokno == '}') { + DPRINTF("/*%d*/", count--); + return; + } + } + } + DPRINTF("/* missing '}' */"); +} + +/* pair_flush - on the fly rewrite casts in grouped stuff */ + +static void pair_flush(t, start, stop) +register struct token *t; +register int start; +register int stop; +{ + tok_flush(t); + + while (t = tok_class(DO_WSPACE)) { + if (t->tokno == start) { /* recurse */ + pair_flush(t, start, stop); + } else if (t->tokno == TOK_LIST) { /* expression or cast */ + check_cast_flush(t); + } else { /* other, copy */ + tok_flush(t); + if (t->tokno == stop) { /* done */ + return; + } + } + } + DPRINTF("/* missing '%c' */", stop); +} + +/* initializer - on the fly rewrite casts in initializer */ + +static void initializer() +{ + register struct token *t; + + while (t = tok_class(DO_WSPACE)) { + switch (t->tokno) { + case ',': /* list separator */ + case ';': /* list terminator */ + tok_unget(t); + return; + case TOK_LIST: /* expression or cast */ + check_cast_flush(t); + break; + case '[': /* array substript, may nest */ + pair_flush(t, '[', ']'); + break; + case '{': /* structured data, may nest */ + pair_flush(t, '{', '}'); + break; + default: /* other, just copy */ + tok_flush(t); + break; + } + } +} + +/* func_ptr_dcl_flush - rewrite function pointer declaration */ + +static struct token *func_ptr_dcl_flush(list) +register struct token *list; +{ + register struct token *t; + + /* + * Ignore blanks because they would be output earlier than the list that + * preceded them... Recover gracefully from syntax errors. + */ + + while (t = tok_class(NO_WSPACE)) { + switch (t->tokno) { + case '\n': /* preserve line count */ + tok_flush(t); + break; + case TOK_LIST: + /* Function pointer type: (list1) (list2) -> (list1) () */ + (void) show_func_ptr_type(list); /* may be recursive */ + tok_free(list); + tok_free(t); + return (0); + default: /* not a declaration */ + tok_unget(t); + return (list); + } + } + + /* Hit EOF; must be mistake, but do not waste any information. */ + + return (list); +} + +/* function_dcl_flush - rewrite function { heading, type declaration } */ + +static struct token *function_dcl_flush(list) +register struct token *list; +{ + register struct token *t; + + /* + * Ignore blanks because they would be output earlier than the list that + * preceded them... + */ + + while (t = tok_class(NO_WSPACE)) { + switch (t->tokno) { + case '\n': + /* Preserve line count */ + tok_flush(t); + break; + case '{': + /* Function heading: word (list) { -> old style heading */ + header_flush(list); + tok_unget(t); + return (0); + case TOK_WORD: + /* Old-style function heading: word (list) word...{ */ + tok_flush(list); + tok_unget(t); + return (0); + case TOK_LIST: + /* Function typedef? word (list1) (list) -> word (list1) () */ + tok_flush(list); + put_str("()"); + tok_free(t); + return (0); + case ',': + case ';': + /* Function type declaration: word (list) -> word () */ + tok_free(list); + put_str("()"); + tok_unget(t); + return (0); + default: + /* Something else, reject the list. */ + tok_unget(t); + return (list); + } + } + + /* Hit EOF; must be mistake, but do not waste any information. */ + + return (list); +} + +/* dcl_flush - parse declaration on the fly, return rejected token */ + +static struct token *dcl_flush(t) +register struct token *t; +{ + register int got_word; + + /* + * Away from the top level, type or storage-class information is required + * for an (extern or forward) function type declaration or a variable + * declaration. + * + * With our naive word-counting approach, this means that the caller should + * read one word before passing the next token to us. This is how we + * distinguish, for example, function declarations from function calls. + * + * An exception are structs and unions, because they look the same at any + * level. The caller should give is the "struct" or "union" token. + */ + + for (got_word = 0; t; t = tok_class(DO_WSPACE)) { + switch (t->tokno) { + case TOK_WSPACE: /* advance past blanks */ + case '\n': /* advance past newline */ + case '*': /* indirection: keep trying */ + tok_flush(t); + break; + case TOK_WORD: /* word: keep trying */ + case TOK_COMPOSITE: /* struct or union */ + got_word = 1; + tok_flush(t); + break; + default: + + /* + * Function pointer types can be preceded by zero or more words + * (at least one when not at the top level). Other stuff can be + * accepted only after we have seen at least one word (two words + * when not at the top level). See also the above comment on + * structs and unions. + */ + + if (t->tokno == TOK_LIST && LIST_BEGINS_WITH_STAR(t)) { + if (t = func_ptr_dcl_flush(t)) { + return (t); /* reject token */ + } else { + got_word = 1; /* for = and [ and , and ; */ + } + } else if (got_word == 0) { + return (t); /* reject token */ + } else { + switch (t->tokno) { + case TOK_LIST: /* function type */ + if (t = function_dcl_flush(t)) + return (t); /* reject token */ + break; + case '[': /* dimension, does not nest */ + pair_flush(t, '[', ']'); + break; + case '=': /* initializer follows */ + tok_flush(t); + initializer(); /* rewrite casts */ + break; + case '{': /* struct, union, may nest */ + block_flush(t); /* use code for stmt blocks */ + break; + case ',': /* separator: keep trying */ + got_word = 0; + tok_flush(t); + break; + case ';': /* terminator: succeed */ + tok_flush(t); + return (0); + default: /* reject token */ + return (t); + } + } + } + } + return (0); /* hit EOF */ +} diff --git a/unproto/varargs.c b/unproto/varargs.c new file mode 100644 index 0000000..4ca56d8 --- /dev/null +++ b/unproto/varargs.c @@ -0,0 +1,32 @@ + /* + * @(#) varargs.c 1.1 91/09/01 23:08:45 + * + * This program can be used to verify that the stdarg.h file is set up + * correctly for your system. If it works, it should print one line with the + * text "stdarg.h works". + */ + +#include <stdio.h> +#include "stdarg.h" + +main(int argc, char *argv[]) +{ + varargs_test("%s %s\n", "stdarg.h", "works"); +} + +varargs_test(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + while (*fmt) { + if (strncmp("%s", fmt, 2) == 0) { + fputs(va_arg(ap, char *), stdout); + fmt += 2; + } else { + putchar(*fmt); + fmt++; + } + } + va_end(ap); +} diff --git a/unproto/vstring.c b/unproto/vstring.c new file mode 100644 index 0000000..fb8d053 --- /dev/null +++ b/unproto/vstring.c @@ -0,0 +1,91 @@ +/*++ +/* NAME +/* vs_alloc(), VS_ADDCH() +/* SUMMARY +/* auto-resizing string library +/* PACKAGE +/* vstring +/* SYNOPSIS +/* #include "vstring.h" +/* +/* struct vstring *vs_alloc(len) +/* int len; +/* +/* int VS_ADDCH(vs, wp, ch) +/* struct vstring *vs; +/* char *wp; +/* int ch; +/* DESCRIPTION +/* These functions and macros implement a small library for +/* arbitrary-length strings that grow automatically when +/* they fill up. The allocation strategy is such that there +/* will always be place for the terminating null character. +/* +/* vs_alloc() allocates storage for a variable-length string. +/* +/* VS_ADDCH() adds a character to a variable-length string +/* and automagically extends the string if fills up. +/* \fIvs\fP is a pointer to a vstring structure; \fIwp\fP +/* the current write position in the corresponding character +/* array; \fIch\fP the character value to be written. +/* Note that VS_ADDCH() is a macro that evaluates some +/* arguments more than once. +/* DIAGNOSTICS +/* VS_ADDCH() returns zero if it was unable to dynamically +/* resize a string. +/* +/* vs_alloc() returns a null pointer in case of problems. +/* BUGS +/* Auto-resizing may change the address of the string data in +/* a vstring structure. Beware of dangling pointers. +/* AUTHOR(S) +/* Wietse Venema +/* Eindhoven University of Technology +/* Department of Mathematics and Computer Science +/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands +/* LAST MODIFICATION +/* 91/09/22 21:21:38 +/* VERSION/RELEASE +/* 1.2 +/*--*/ + +static char vstring_sccsid[] = "@(#) vstring.c 1.2 91/09/22 21:21:38"; + +/* C library */ + +extern char *malloc(); +extern char *realloc(); + +/* Application-specific stuff */ + +#include "vstring.h" + +/* vs_alloc - initial string allocation */ + +struct vstring *vs_alloc(len) +int len; +{ + register struct vstring *vp; + + if (len < 1 + || (vp = (struct vstring *) malloc(sizeof(struct vstring))) == 0 + || (vp->str = malloc(len)) == 0) + return (0); + vp->last = vp->str + len - 1; + return (vp); +} + +/* vs_realloc - extend string, update write pointer */ + +char *vs_realloc(vp, cp) +register struct vstring *vp; +char *cp; +{ + int where = cp - vp->str; + int len = vp->last - vp->str + 1; + + if ((vp->str = realloc(vp->str, len *= 2)) == 0) + return (0); + vp->last = vp->str + len - 1; + return (vp->str + where); +} diff --git a/unproto/vstring.h b/unproto/vstring.h new file mode 100644 index 0000000..ec02a6e --- /dev/null +++ b/unproto/vstring.h @@ -0,0 +1,14 @@ +/* @(#) vstring.h 1.1 91/09/01 23:08:42 */ + +struct vstring { + char *str; /* string value */ + char *last; /* last position */ +}; + +extern struct vstring *vs_alloc(); /* initial allocation */ +extern char *vs_realloc(); /* string extension */ + +/* macro to add one character to auto-resized string */ + +#define VS_ADDCH(vs,wp,c) \ + ((wp < (vs)->last || (wp = vs_realloc(vs,wp))) ? (*wp++ = c) : 0) |