summaryrefslogtreecommitdiff
path: root/unproto/unproto.c
diff options
context:
space:
mode:
Diffstat (limited to 'unproto/unproto.c')
-rw-r--r--unproto/unproto.c466
1 files changed, 365 insertions, 101 deletions
diff --git a/unproto/unproto.c b/unproto/unproto.c
index 9ca26ce..f7c03f7 100644
--- a/unproto/unproto.c
+++ b/unproto/unproto.c
@@ -2,68 +2,140 @@
/* NAME
/* unproto 1
/* SUMMARY
-/* ANSI C to old C converter
+/* compile ANSI C with traditional UNIX C compiler
/* PACKAGE
/* unproto
/* SYNOPSIS
-/* /lib/cpp ... | unproto
-/*
/* /somewhere/cpp ...
+/*
+/* cc cflags -E file.c | unproto >file.i; cc cflags -c file.i
/* 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).
+/* This document describes a filter that sits in between the UNIX
+/* C preprocessor and the next UNIX C compiler stage, on the fly rewriting
+/* ANSI-style syntax to old-style syntax. Typically, the program is
+/* invoked by the native UNIX C compiler as an alternate preprocessor.
+/* The unprototyper in turn invokes the native C preprocessor and
+/* massages its output. Similar tricks can be used with the lint(1)
+/* command.
+/*
+/* Language constructs that are always rewritten:
+/* .TP
+/* function headings, prototypes, pointer types
+/* ANSI-C style function headings, function prototypes, function
+/* pointer types and type casts are rewritten to old style.
+/* <stdarg.h> support is provided for functions with variable-length
+/* argument lists.
+/* .TP
+/* character and string constants
+/* The \\a and \\x escape sequences are rewritten to their (three-digit)
+/* octal equivalents.
+/*
+/* Multiple string tokens are concatenated; an arbitrary number of
+/* whitespace or comment tokens may appear between successive
+/* string tokens.
/*
-/* 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.
+/* Within string constants, octal escape sequences are rewritten to the
+/* three-digit \\ddd form, so that string concatenation produces correct
+/* results.
+/* .TP
+/* date and time
+/* The __DATE__ and __TIME__ tokens are replaced by string constants
+/* of the form "Mmm dd yyyy" and "hh:mm:ss", respectively. The result
+/* is subjected to string concatenation, just like any other string
+/* constant.
+/* .PP
+/* Language constructs that are rewritten only if the program has been
+/* configured to do so:
+/* .TP
+/* void types
+/* The unprototyper can be configured to rewrite "void *" to "char *",
+/* and even to rewrite plain "void" to "int".
+/* These features are configurable because many traditional UNIX C
+/* compilers do not need them.
+/*
+/* Note: (void) argument lists are always replaced by empty ones.
+/* .PP
+/* ANSI C constructs that are not rewritten because the traditional
+/* UNIX C preprocessor provides suitable workarounds:
+/* .TP
+/* const and volatile
+/* Use the "-Dconst=" and/or "-Dvolatile=" preprocessor directives to
+/* get rid of unimplemented keywords.
+/* .TP
+/* token pasting and stringizing
+/* The traditional UNIX C preprocessor provides excellent alternatives.
+/* For example:
+/*
+/* .nf
+/* .ne 2
+/* #define string(bar) "bar" /* instead of: # x */
+/* #define paste(x,y) x/**\/y /* instead of: x##y */
+/* .fi
/*
-/* 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".
+/* There is a good reason why the # and ## operators are not implemented
+/* in the unprototyper.
+/* After program text has gone through a non-ANSI C preprocessor, all
+/* information about the grouping of the operands of # and ## is lost.
+/* Thus, if the unprototyper were to perform these operations, it would
+/* produce correct results only in the most trivial cases. Operands
+/* with embedded blanks, operands that expand to null tokens, and nested
+/* use of # and/or ## would cause all kinds of obscure problems.
+/* .PP
+/* Unsupported ANSI features:
+/* .TP
+/* trigraphs and #pragmas
+/* Trigraphs are useful only for systems with broken character sets.
+/* If the local compiler chokes on #pragma, insert a blank before the
+/* "#" character, and enclose the offending directive between #ifdef
+/* and #endif.
/* 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
+/* Some versions of the lint(1) 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.
+/* with the unprototyper. Other versions of the lint(1) command accept
+/* the same command syntax as the C compiler for the specification of a
+/* non-default preprocessor. Some research may be needed.
+/* FILES
+/* /wherever/stdarg.h, provided with the unproto filter.
/* DIAGNOSTICS
-/* The progam will complain if it unexpectedly
-/* reaches the end of input.
+/* Problems are reported on the standard error stream.
+/* A non-zero exit status means that there was a problem.
/* BUGS
-/* Should be run on preprocessed source only, i.e. after macro expansion.
+/* The unprototyper should be run on preprocessed source only:
+/* unexpanded macros may confuse the program.
+/*
+/* Declarations of (object) are misunderstood and will result in
+/* syntax errors: the objects between parentheses disappear.
/*
-/* Declarations of (whatever) are misunderstood and will result in
-/* syntax errors.
+/* Sometimes does not preserve whitespace after parentheses and commas.
+/* This is a purely aesthetical matter, and the compiler should not care.
+/* Whitespace within string constants is, of course, left intact.
/*
-/* Does not generate explicit type casts for function argument
-/* expressions.
+/* Does not generate explicit type casts for function-argument
+/* expressions. The lack of explicit conversions 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 between integral and
+/* floating-point argument types. Let lint(1) be your friend.
/* 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
+/* 93/06/18 22:29:37
/* VERSION/RELEASE
-/* 1.2
+/* 1.6
/*--*/
-static char unproto_sccsid[] = "@(#) unproto.c 1.3 91/11/30 21:10:30";
+static char unproto_sccsid[] = "@(#) unproto.c 1.6 93/06/18 22:29:37";
/* C library */
+#include <sys/types.h>
+#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
@@ -91,6 +163,7 @@ static void show_arg_name();
static void show_type();
static void pair_flush();
static void check_cast();
+static void show_empty_list();
#define check_cast_flush(t) (check_cast(t), tok_free(t))
@@ -110,8 +183,8 @@ static int pipe_stdin_through_cpp();
#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) \
+#define LAST_ARG_AND_EQUAL(s,c) ((s)->next && (s)->next->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 == '*')
@@ -120,6 +193,14 @@ static int pipe_stdin_through_cpp();
&& s->next->tokno == TOK_LIST \
&& LIST_BEGINS_WITH_STAR(s))
+/* What to look for to detect a (void) argument list. */
+
+#ifdef MAP_VOID
+#define VOID_ARG "int" /* bare "void" is mapped to "int" */
+#else
+#define VOID_ARG "void" /* bare "void" is left alone */
+#endif
+
/* main - driver */
int main(argc, argv)
@@ -133,7 +214,8 @@ char **argv;
int cpp_pid;
cpp_pid = pipe_stdin_through_cpp(argv);
-#else
+#endif
+#ifdef REOPEN
if ( argc > 1 ) {
if( freopen(argv[1], "r", stdin) == 0 ) {
fprintf(stderr, "Cannot open '%s'\n", argv[1]);
@@ -150,7 +232,7 @@ char **argv;
sym_init(); /* prime the symbol table */
- while (t = tok_class(DO_WSPACE)) {
+ while (t = tok_class()) {
if (t = dcl_flush(t)) { /* try declaration */
if (t->tokno == '{') { /* examine rejected token */
block_flush(t); /* body */
@@ -163,9 +245,9 @@ char **argv;
#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);
+ return (errcount != 0 || wait_pid != cpp_pid || cpp_status != 0);
#else
- return (0);
+ return (errcount != 0);
#endif
}
@@ -179,6 +261,22 @@ char **argv;
int pipefds[2];
int pid;
char **cpptr = argv;
+ int i;
+ struct stat st;
+
+ /*
+ * The code that sets up the pipe requires that file descriptors 0,1,2
+ * are already open. All kinds of mysterious things will happen if that
+ * is not the case. The following loops makes sure that descriptors 0,1,2
+ * are set up properly.
+ */
+
+ for (i = 0; i < 3; i++) {
+ if (fstat(i, &st) == -1 && open("/dev/null", 2) != i) {
+ perror("open /dev/null");
+ exit(1);
+ }
+ }
/*
* With most UNIX implementations, the second non-option argument to
@@ -226,20 +324,22 @@ char **argv;
case -1: /* error */
perror("fork");
exit(1);
+ /* NOTREACHED */
case 0: /* child */
- close(pipefds[0]); /* close reading end */
- close(1); /* connect stdout to pipe */
+ (void) close(pipefds[0]); /* close reading end */
+ (void) 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);
+ fatal("dup() problem");
+ (void) close(pipefds[1]); /* close redundant fd */
+ (void) execv(PIPE_THROUGH_CPP, argv);
perror(PIPE_THROUGH_CPP);
exit(1);
+ /* NOTREACHED */
default: /* parent */
- close(pipefds[1]); /* close writing end */
- close(0); /* connect stdin to pipe */
+ (void) close(pipefds[1]); /* close writing end */
+ (void) close(0); /* connect stdin to pipe */
if (dup(pipefds[0]) != 0)
- error(1, "dup() problem");
+ fatal("dup() problem");
close(pipefds[0]); /* close redundant fd */
return (pid);
}
@@ -247,47 +347,160 @@ char **argv;
#endif
-/* header_flush - rewrite new-style function header to old style */
+/* show_arg_names - display function argument names */
-static void header_flush(t)
+static void show_arg_names(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 */
+ if (LAST_ARG_AND_EQUAL(t->head, VOID_ARG)) {
+ show_empty_list(t); /* 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); /* ',' */
+ tok_show_ch(s); /* ',' */
put_str(_VA_ALIST_); /* varargs magic */
#endif
} else {
- put_ch(s->tokno); /* opening '(' or ',' */
+ tok_show_ch(s); /* '(' or ',' or ')' */
show_arg_name(s); /* extract argument name */
}
}
- put_str(")\n"); /* closing ')' */
}
+}
+
+/* show_arg_types - display function argument types */
+
+static void show_arg_types(t)
+register struct token *t;
+{
+ register struct token *s;
/* Do argument types, but suppress void and trailing ... */
- if (!LAST_ARG_AND_EQUAL(t->head, "void")) {
+ if (!LAST_ARG_AND_EQUAL(t->head, VOID_ARG)) {
for (s = t->head; s; s = s->next) { /* foreach argument... */
- if (!LAST_ARG_AND_EQUAL(s, "...")) {
+ if (LAST_ARG_AND_EQUAL(s, "...")) {
+#ifdef _VA_DCL_ /* see ./stdarg.h */
+ put_str(_VA_DCL_); /* varargs magic */
+ put_nl(); /* make output look nicer */
+#endif
+ } else {
if (s->head != s->tail) { /* really new-style argument? */
- show_line_control(); /* fix line number */
show_type(s); /* rewrite type info */
- put_str(";\n");
+ put_ch(';');
+ put_nl(); /* make output look nicer */
}
}
}
}
- tok_free(t);
- show_line_control(); /* because '{' follows */
+}
+
+/* header_flush - rewrite new-style function heading to old style */
+
+static void header_flush(t)
+register struct token *t;
+{
+ show_arg_names(t); /* show argument names */
+ put_nl(); /* make output look nicer */
+ show_arg_types(t); /* show argument types */
+ tok_free(t); /* discard token */
+}
+
+/* fpf_header_names - define func returning ptr to func, no argument types */
+
+static void fpf_header_names(list)
+struct token *list;
+{
+ register struct token *s;
+ register struct token *p;
+
+ /*
+ * Recurse until we find the argument list. Account for the rare case
+ * that list is a comma-separated list (which should be a syntax error).
+ * Display old-style fuction argument names.
+ */
+
+ for (s = list->head; s; s = s->next) {
+ tok_show_ch(s); /* '(' or ',' or ')' */
+ for (p = s->head; p; p = p->next) {
+ if (p->tokno == TOK_LIST) {
+ if (IS_FUNC_PTR_TYPE(p)) { /* recurse */
+ fpf_header_names(p);
+ show_empty_list(p = p->next);
+ } else { /* display argument names */
+ show_arg_names(p);
+ }
+ } else { /* pass through other stuff */
+ tok_show(p);
+ }
+ }
+ }
+}
+
+/* fpf_header_types - define func returning ptr to func, argument types only */
+
+static void fpf_header_types(list)
+struct token *list;
+{
+ register struct token *s;
+ register struct token *p;
+
+ /*
+ * Recurse until we find the argument list. Account for the rare case
+ * that list is a comma-separated list (which should be a syntax error).
+ * Display old-style function argument types.
+ */
+
+ for (s = list->head; s; s = s->next) {
+ for (p = s->head; p; p = p->next) {
+ if (p->tokno == TOK_LIST) {
+ if (IS_FUNC_PTR_TYPE(p)) { /* recurse */
+ fpf_header_types(p);
+ p = p->next;
+ } else { /* display argument types */
+ show_arg_types(p);
+ }
+ }
+ }
+ }
+}
+
+/* fpf_header - define function returning pointer to function */
+
+static void fpf_header(l1, l2)
+struct token *l1;
+struct token *l2;
+{
+ fpf_header_names(l1); /* strip argument types */
+ show_empty_list(l2); /* strip prototype */
+ put_nl(); /* nicer output */
+ fpf_header_types(l1); /* show argument types */
+}
+
+/* skip_enclosed - skip over enclosed tokens */
+
+static struct token *skip_enclosed(p, stop)
+register struct token *p;
+register int stop;
+{
+ register int start = p->tokno;
+
+ /* Always return a pointer to the last processed token, never NULL. */
+
+ while (p->next) {
+ p = p->next;
+ if (p->tokno == start) {
+ p = skip_enclosed(p, stop); /* recurse */
+ } else if (p->tokno == stop) {
+ break; /* done */
+ }
+ }
+ return (p);
}
/* show_arg_name - extract argument name from argument type info */
@@ -304,6 +517,10 @@ register struct token *s;
for (p = s->head; p; p = p->next) {
if (p->tokno == TOK_WORD) {
t = p; /* remember last word */
+ } else if (p->tokno == '{') {
+ p = skip_enclosed(p, '}'); /* skip structured stuff */
+ } else if (p->tokno == '[') {
+ break; /* dimension may be a macro */
} else if (IS_FUNC_PTR_TYPE(p)) {
t = p; /* or function pointer */
p = p->next;
@@ -328,35 +545,60 @@ register struct token *s;
{
register struct token *p;
+ /*
+ * Rewrite (*stuff)(args) to (*stuff)(). Rewrite word(args) to word(),
+ * but only if the word was preceded by a word, '*' or '}'. Leave
+ * anything else alone.
+ */
+
for (p = s->head; p; p = p->next) {
if (IS_FUNC_PTR_TYPE(p)) {
- p = show_func_ptr_type(p); /* function pointer type */
+ p = show_func_ptr_type(p, p->next); /* function pointer type */
} else {
+ register struct token *q;
+ register struct token *r;
+
tok_show(p); /* other */
+ if ((p->tokno == TOK_WORD || p->tokno == '*' || p->tokno == '}')
+ && (q = p->next) && q->tokno == TOK_WORD
+ && (r = q->next) && r->tokno == TOK_LIST) {
+ tok_show(q); /* show name */
+ show_empty_list(p = r); /* strip args */
+ }
}
}
}
/* show_func_ptr_type - display function_pointer type using old-style syntax */
-static struct token *show_func_ptr_type(t)
-struct token *t;
+static struct token *show_func_ptr_type(t1, t2)
+struct token *t1;
+struct token *t2;
{
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.
+ * Rewrite (list1) (list2) to (list1) (). 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 ',' */
+ for (s = t1->head; s; s = s->next) {
+ tok_show_ch(s); /* '(' or ',' or ')' */
show_type(s); /* recurse */
}
- put_str(")()"); /* closing paren */
- return (t->next);
+ show_empty_list(t2);
+ return (t2);
+}
+
+/* show_empty_list - display opening and closing parentheses (if available) */
+
+static void show_empty_list(t)
+register struct token *t;
+{
+ tok_show_ch(t->head); /* opening paren */
+ if (t->tail->tokno == ')')
+ tok_show_ch(t->tail); /* closing paren */
}
/* show_struct_type - display structured type, rewrite function-pointer types */
@@ -369,7 +611,7 @@ register struct token *p;
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 */
+ p = show_func_ptr_type(p, p->next); /* function-pointer member */
} else if (p->tokno == '{') {
p = show_struct_type(p); /* recurse */
} else {
@@ -409,6 +651,8 @@ register struct token *t;
return (is_func_ptr_cast(p));
case TOK_WORD: /* name in list */
return (0);
+ case '[':
+ return (1); /* dimension may be a macro */
}
}
return (1); /* no name found */
@@ -430,13 +674,13 @@ struct token *t;
*/
for (s = t->head; s; s = s->next) {
- put_ch(s->tokno); /* opening paren or ',' */
+ tok_show_ch(s); /* '(' or ',' 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 */
+ p = show_func_ptr_type(p, p->next);
+ } else {
check_cast(p); /* recurse */
}
break;
@@ -449,7 +693,6 @@ struct token *t;
}
}
}
- put_ch(')'); /* closing paren */
}
/* block_dcls - on the fly rewrite decls/initializers at start of block */
@@ -469,7 +712,7 @@ static void block_dcls()
* "union" tokens to the type_dcl() function.
*/
- while (t = tok_class(DO_WSPACE)) {
+ while (t = tok_class()) {
switch (t->tokno) {
case TOK_WSPACE: /* preserve white space */
case '\n': /* preserve line count */
@@ -477,7 +720,8 @@ static void block_dcls()
break;
case TOK_WORD: /* type declarations? */
tok_flush(t); /* advance to next token */
- t = tok_class(DO_WSPACE); /* null return is ok */
+ t = tok_class(); /* null return is ok */
+ /* FALLTRHOUGH */
case TOK_COMPOSITE: /* struct or union */
if ((t = dcl_flush(t)) == 0)
break;
@@ -511,7 +755,7 @@ register struct token *t;
/* Remainder of block: only rewrite function pointer casts. */
- while (t = tok_class(DO_WSPACE)) {
+ while (t = tok_class()) {
if (t->tokno == TOK_LIST) {
check_cast_flush(t);
} else if (t->tokno == '{') {
@@ -536,7 +780,7 @@ register int stop;
{
tok_flush(t);
- while (t = tok_class(DO_WSPACE)) {
+ while (t = tok_class()) {
if (t->tokno == start) { /* recurse */
pair_flush(t, start, stop);
} else if (t->tokno == TOK_LIST) { /* expression or cast */
@@ -557,7 +801,7 @@ static void initializer()
{
register struct token *t;
- while (t = tok_class(DO_WSPACE)) {
+ while (t = tok_class()) {
switch (t->tokno) {
case ',': /* list separator */
case ';': /* list terminator */
@@ -566,7 +810,7 @@ static void initializer()
case TOK_LIST: /* expression or cast */
check_cast_flush(t);
break;
- case '[': /* array substript, may nest */
+ case '[': /* array subscript, may nest */
pair_flush(t, '[', ']');
break;
case '{': /* structured data, may nest */
@@ -579,28 +823,47 @@ static void initializer()
}
}
-/* func_ptr_dcl_flush - rewrite function pointer declaration */
+/* func_ptr_dcl_flush - rewrite function pointer stuff */
static struct token *func_ptr_dcl_flush(list)
register struct token *list;
{
register struct token *t;
+ register struct token *t2;
/*
- * Ignore blanks because they would be output earlier than the list that
- * preceded them... Recover gracefully from syntax errors.
+ * Ignore blanks and newlines because we are too lazy to maintain more
+ * than one token worth of lookahead. The output routines will regenerate
+ * discarded newline tokens.
*/
- while (t = tok_class(NO_WSPACE)) {
+ while (t = tok_class()) {
switch (t->tokno) {
- case '\n': /* preserve line count */
- tok_flush(t);
+ case TOK_WSPACE:
+ case '\n':
+ tok_free(t);
break;
case TOK_LIST:
- /* Function pointer type: (list1) (list2) -> (list1) () */
- (void) show_func_ptr_type(list); /* may be recursive */
+ /* Function pointer or function returning pointer to function. */
+ while ((t2 = tok_class()) /* skip blanks etc. */
+ &&(t2->tokno == TOK_WSPACE || t2->tokno == '\n'))
+ tok_free(t2);
+ switch (t2 ? t2->tokno : 0) {
+ case '{': /* function heading (new) */
+ fpf_header(list, t);
+ break;
+ case TOK_WORD: /* function heading (old) */
+ tok_show(list);
+ tok_show(t);
+ break;
+ default: /* func pointer type */
+ (void) show_func_ptr_type(list, t);
+ break;
+ }
tok_free(list);
tok_free(t);
+ if (t2)
+ tok_unget(t2);
return (0);
default: /* not a declaration */
tok_unget(t);
@@ -621,15 +884,16 @@ register struct token *list;
register struct token *t;
/*
- * Ignore blanks because they would be output earlier than the list that
- * preceded them...
+ * Ignore blanks and newlines because we are too lazy to maintain more
+ * than one token worth of lookahead. The output routines will regenerate
+ * ignored newline tokens.
*/
- while (t = tok_class(NO_WSPACE)) {
+ while (t = tok_class()) {
switch (t->tokno) {
+ case TOK_WSPACE:
case '\n':
- /* Preserve line count */
- tok_flush(t);
+ tok_free(t);
break;
case '{':
/* Function heading: word (list) { -> old style heading */
@@ -637,21 +901,21 @@ register struct token *list;
tok_unget(t);
return (0);
case TOK_WORD:
- /* Old-style function heading: word (list) 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) () */
+ /* Function pointer: word (list1) (list2) -> word (list1) () */
tok_flush(list);
- put_str("()");
+ show_empty_list(t);
tok_free(t);
return (0);
case ',':
case ';':
/* Function type declaration: word (list) -> word () */
+ show_empty_list(list);
tok_free(list);
- put_str("()");
tok_unget(t);
return (0);
default:
@@ -686,7 +950,7 @@ register struct token *t;
* level. The caller should give is the "struct" or "union" token.
*/
- for (got_word = 0; t; t = tok_class(DO_WSPACE)) {
+ for (got_word = 0; t; t = tok_class()) {
switch (t->tokno) {
case TOK_WSPACE: /* advance past blanks */
case '\n': /* advance past newline */