⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unproto.c

📁 pgp soucecode pgp soucecode
💻 C
📖 第 1 页 / 共 2 页
字号:
/*++
/* 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 + -