📄 unproto.c
字号:
/*++
/* NAME
/* unproto 1
/* SUMMARY
/* compile ANSI C with traditional UNIX C compiler
/* PACKAGE
/* unproto
/* SYNOPSIS
/* /somewhere/cpp ...
/*
/* cc cflags -E file.c | unproto >file.i; cc cflags -c file.i
/* DESCRIPTION
/* 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.
/*
/* 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
/*
/* 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(1) command are implemented as a shell
/* script. It should require only minor modification for integration
/* 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
/* Problems are reported on the standard error stream.
/* A non-zero exit status means that there was a problem.
/* BUGS
/* 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.
/*
/* 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. 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
/* 93/06/18 22:29:37
/* VERSION/RELEASE
/* 1.6
/*--*/
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>
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();
static void show_empty_list();
#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 && (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 == '*')
#define IS_FUNC_PTR_TYPE(s) (s->tokno == TOK_LIST && s->next \
&& 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)
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);
#endif
sym_init(); /* prime the symbol table */
while (t = tok_class()) {
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 (errcount != 0 || wait_pid != cpp_pid || cpp_status != 0);
#else
return (errcount != 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;
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
* /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);
/* NOTREACHED */
case 0: /* child */
(void) close(pipefds[0]); /* close reading end */
(void) close(1); /* connect stdout to pipe */
if (dup(pipefds[1]) != 1)
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 */
(void) close(pipefds[1]); /* close writing end */
(void) close(0); /* connect stdin to pipe */
if (dup(pipefds[0]) != 0)
fatal("dup() problem");
close(pipefds[0]); /* close redundant fd */
return (pid);
}
}
#endif
/* show_arg_names - display function argument names */
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_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 */
tok_show_ch(s); /* ',' */
put_str(_VA_ALIST_); /* varargs magic */
#endif
} else {
tok_show_ch(s); /* '(' or ',' or ')' */
show_arg_name(s); /* extract argument name */
}
}
}
}
/* 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_ARG)) {
for (s = t->head; s; s = s->next) { /* foreach argument... */
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_type(s); /* rewrite type info */
put_ch(';');
put_nl(); /* make output look nicer */
}
}
}
}
}
/* 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 */
static void show_arg_name(s)
register struct token *s;
{
if (s->head) {
register struct token *p;
register struct token *t = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -