summaryrefslogtreecommitdiff
path: root/unproto
diff options
context:
space:
mode:
Diffstat (limited to 'unproto')
-rw-r--r--unproto/Makefile97
-rw-r--r--unproto/README130
-rw-r--r--unproto/Shar.Headers50
-rw-r--r--unproto/cpp.sh9
-rw-r--r--unproto/error.c78
-rw-r--r--unproto/error.h4
-rw-r--r--unproto/example.c140
-rw-r--r--unproto/example.out173
-rw-r--r--unproto/stdarg.h38
-rw-r--r--unproto/symbol.c161
-rw-r--r--unproto/symbol.h11
-rw-r--r--unproto/tok_class.c180
-rw-r--r--unproto/tok_io.c464
-rw-r--r--unproto/tok_pool.c104
-rw-r--r--unproto/token.h47
-rw-r--r--unproto/unproto.179
-rw-r--r--unproto/unproto.c749
-rw-r--r--unproto/varargs.c32
-rw-r--r--unproto/vstring.c91
-rw-r--r--unproto/vstring.h14
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)