%{
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "parseproto.h"
#include <assert.h>
static decl_spec_t *declspec_Construct(void);
static void declspec_Destroy(decl_spec_t *);
static decl_spec_t *declspec_Init(stt_t, char *);
static char *declspec_VerifySTT(stt_t, stt_t);
static decl_spec_t *declspec_AddSTT(decl_spec_t *, stt_t, const char **);
static decl_spec_t *declspec_AddDS(decl_spec_t *,
decl_spec_t *, const char **);
static stt_t declspec_GetSTT(decl_spec_t *);
static char *declspec_GetTag(decl_spec_t *);
static type_t *type_Construct(void);
static void type_Destroy(type_t *);
static type_t *type_SetPtr(type_t *, stt_t);
static type_t *type_SetFun(type_t *, decl_t *);
static type_t *type_AddTail(type_t *, type_t *);
static const char *type_Verify(type_t *);
static decl_t *decl_Construct(void);
static decl_t *decl_AddArg(decl_t *, decl_t *);
static int decl_IsVoid(decl_t *);
static int decl_IsVoidArray(decl_t *);
static const char *decl_VerifyArgs(decl_t *);
static decl_t *decl_AddDS(decl_t *, decl_spec_t *, const char **);
static decl_t *decl_AddTypeTail(decl_t *, type_t *);
static decl_t *decl_addptr(decl_t *, type_t *);
static decl_t *decl_addary(decl_t *, char *);
static decl_t *decl_addfun(decl_t *, decl_t *);
static decl_t *decl_addellipsis(decl_t *);
#if defined(DEBUG)
static void type_PrintType(type_t *, int);
static void decl_PrintDecl(decl_t *, int);
static void decl_PrintTraceInfo(decl_t *);
static char *de_const(char *);
#endif
static int yylex(void);
static void yyerror(const char *);
static int yyparse(void);
#if defined(MEM_DEBUG)
static int declspec_Construct_calls;
static int type_Construct_calls;
static int decl_Construct_calls;
#endif
#if defined(DEBUG)
static char *de_const(char *);
#endif
%}
%union {
char *s_val;
int i_val;
stt_t stt_val;
decl_spec_t *ds_val;
type_t *t_val;
decl_t *d_val;
}
%token <i_val> ELLIPSIS
%token <s_val> INTEGER
%token <s_val> IDENTIFIER
%token <s_val> TYPEDEF_NAME
%type <s_val> constant_expression
%token <stt_val> REGISTER
%token <stt_val> TYPEDEF EXTERN AUTO STATIC
%token <stt_val> VOID CHAR SHORT INT LONG
%token <stt_val> FLOAT DOUBLE SIGNED UNSIGNED
%token <stt_val> CONST VOLATILE RESTRICT RESTRICT_KYWD
%type <stt_val> struct_or_union
%type <ds_val> storage_class_specifier
%type <ds_val> type_qualifier
%type <ds_val> type_qualifier_list
%token <ds_val> STRUCT UNION
%token <ds_val> ENUM
%type <ds_val> declaration_specifiers
%type <ds_val> type_specifier
%type <ds_val> struct_or_union_specifier enum_specifier
%type <ds_val> typedef_name
%type <t_val> pointer
%type <d_val> declaration
%type <d_val> init_declarator_list init_declarator
%type <d_val> declarator
%type <d_val> direct_declarator
%type <d_val> parameter_type_list parameter_list
%type <d_val> parameter_declaration
%type <d_val> abstract_declarator
%type <d_val> direct_abstract_declarator
%start declaration
%%
/*
* The grammar is derived from ANSI/ISO 9899-1990.
*/
declaration
: declaration_specifiers init_declarator_list ';'
{
decl_t *dp;
protop = $$ = $2;
/* only one declaration allowed */
assert(protop->d_next == NULL);
for (dp = $2; dp && (errstr == NULL);
dp = dp->d_next) {
const char *sp;
decl_AddDS(dp, $1, &errstr);
if (sp = decl_Verify(dp))
errstr = sp;
}
declspec_Destroy($1);
}
| error ';'
{
protop = $$ = NULL;
errstr = "function prototype syntax error";
}
/*
* XXX - Does not support a "stand-alone" declaration specifier. It is
* essentially a type declaration, for example:
*
* typedef enum { FALSE = 0, TRUE = 1 } boolean_t;
* or
* struct _name { char *first; char *last };
*/
/* XXX | declaration_specifiers */
;
declaration_specifiers
: storage_class_specifier declaration_specifiers
{
char const *ep;
$$ = declspec_AddDS($2, $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
| storage_class_specifier
| type_specifier declaration_specifiers
{
const char *ep;
$$ = declspec_AddDS($2, $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
| type_specifier
| type_qualifier declaration_specifiers
{
const char *ep;
$$ = declspec_AddDS($2, $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
| type_qualifier
;
storage_class_specifier
: REGISTER
{
$$ = declspec_Init(SCS_REGISTER, NULL);
}
/*
* XXX - Does not support any storage class specifier other than
* register, and then only for function arguments.
*
| TYPEDEF
{
$$ = declspec_Init(SCS_TYPEDEF, NULL);
}
| EXTERN
{
$$ = declspec_Init(SCS_EXTERN, NULL);
}
| STATIC
{
$$ = declspec_Init(SCS_STATIC, NULL);
}
| AUTO
{
$$ = declspec_Init(SCS_AUTO, NULL);
}
*/
;
type_specifier
: VOID
{
$$ = declspec_Init(TS_VOID, NULL);
atIDENT = 1;
}
| CHAR
{
$$ = declspec_Init(TS_CHAR, NULL);
atIDENT = 1;
}
| SHORT
{
$$ = declspec_Init(TS_SHORT, NULL);
atIDENT = 1;
}
| INT
{
$$ = declspec_Init(TS_INT, NULL);
atIDENT = 1;
}
| LONG
{
$$ = declspec_Init(TS_LONG, NULL);
atIDENT = 1;
}
| FLOAT
{
$$ = declspec_Init(TS_FLOAT, NULL);
atIDENT = 1;
}
| DOUBLE
{
$$ = declspec_Init(TS_DOUBLE, NULL);
atIDENT = 1;
}
| SIGNED
{
$$ = declspec_Init(TS_SIGNED, NULL);
atIDENT = 1;
}
| UNSIGNED
{
$$ = declspec_Init(TS_UNSIGNED, NULL);
atIDENT = 1;
}
| struct_or_union_specifier
| enum_specifier
| typedef_name
;
typedef_name
: TYPEDEF_NAME
{
$$ = declspec_Init(TS_TYPEDEF, $1);
atIDENT = 1;
free($1);
}
;
/*
* The "restrict" keyword is new in the C99 standard.
* It is type qualifier like const and volatile.
* We are using "_RESTRICT_KYWD" in headers and source code so
* it is easily turned on and off by various macros at compile time.
* In order for the "restrict" keyword to be recognized you must
* be using a C99 compliant compiler in its native mode.
*/
type_qualifier
: CONST
{
$$ = declspec_Init(TQ_CONST, NULL);
}
| VOLATILE
{
$$ = declspec_Init(TQ_VOLATILE, NULL);
}
| RESTRICT
{
$$ = declspec_Init(TQ_RESTRICT, NULL);
}
| RESTRICT_KYWD
{
$$ = declspec_Init(TQ_RESTRICT_KYWD, NULL);
}
;
struct_or_union_specifier
: struct_or_union { atIDENT = 1; } IDENTIFIER
{
$$ = declspec_Init($1, $3);
free($3);
}
/*
* XXX - struct or union definitions are not supported. It is generally
* not done within the context of a function declaration (prototype) or
* variable definition.
| struct_or_union IDENTIFIER '{' struct_declaration_list '}'
| struct_or_union '{' struct_declaration_list '}'
*/
;
struct_or_union
: STRUCT
{
$$ = TS_STRUCT;
}
| UNION
{
$$ = TS_UNION;
}
;
init_declarator_list
: init_declarator
{
$$ = $1;
atIDENT = 1;
}
/*
* XXX - Does not support a comma separated list of declarations or
* definitions. Function prototypes or variable definitions must be
* given as one per C statement.
| init_declarator_list ',' init_declarator
{
$$ = decl_AddArg($1, $3);
atIDENT = 1;
}
*/
;
init_declarator
: declarator
/*
* XXX - Initialization is not supported.
| declarator '=' initializer
*/
;
enum_specifier
: ENUM { atIDENT = 1; } IDENTIFIER
{
$$ = declspec_Init(TS_ENUM, $3);
free($3);
}
/*
* XXX - enumerator definition is not supported for the same reasons
* struct|union definition is not supported.
| ENUM IDENTIFIER '{' enumerator_list '}'
| ENUM '{' enumerator_list '}'
*/
;
declarator
: pointer direct_declarator
{
$$ = decl_addptr($2, $1);
}
| direct_declarator
;
direct_declarator
: IDENTIFIER
{
$$ = decl_SetName(decl_Construct(), $1);
atIDENT = 0;
free($1);
}
| '(' declarator ')'
{
$$ = $2;
}
| direct_declarator '[' constant_expression ']'
{
$$ = decl_addary($1, $3);
free($3);
}
| direct_declarator '[' ']'
{
$$ = decl_addary($1, NULL);
}
| direct_declarator '(' parameter_type_list ')'
{
$$ = decl_addfun($1, $3);
}
| direct_declarator '(' ')'
{
$$ = decl_addfun($1, NULL);
}
;
pointer
: '*' type_qualifier_list
{
$$ = type_SetPtr(type_Construct(), ($2)->ds_stt);
declspec_Destroy($2);
}
| '*'
{
$$ = type_SetPtr(type_Construct(), TQ_NONE);
}
| '*' type_qualifier_list pointer
{
type_t *tp = type_Construct();
type_SetPtr(tp, ($2)->ds_stt);
declspec_Destroy($2);
$$ = type_AddTail($3, tp);
}
| '*' pointer
{
type_t *tp = type_Construct();
type_SetPtr(tp, TQ_NONE);
$$ = type_AddTail($2, tp);
}
;
type_qualifier_list
: type_qualifier
| type_qualifier_list type_qualifier
{
const char *ep;
/* XXX - ignore any error */
$$ = declspec_AddDS($1, $2, &ep);
declspec_Destroy($2);
}
;
parameter_type_list
: parameter_list
| parameter_list ',' ELLIPSIS
{
$$ = decl_addellipsis($1);
}
;
parameter_list
: parameter_declaration
{
const char *sp = type_Verify($1->d_type);
if (sp)
errstr = sp;
$$ = $1;
atIDENT = 0;
}
| parameter_list ',' parameter_declaration
{
const char *sp = type_Verify($3->d_type);
if (sp)
errstr = sp;
$$ = decl_AddArg($1, $3);
atIDENT = 0;
}
;
parameter_declaration
: declaration_specifiers declarator
{
const char *ep;
$$ = decl_AddDS($2, $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
| declaration_specifiers abstract_declarator
{
const char *ep;
$$ = decl_AddDS($2, $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
| declaration_specifiers
{
const char *ep;
$$ = decl_AddDS(decl_Construct(), $1, &ep);
declspec_Destroy($1);
if (errstr == NULL)
errstr = ep;
}
;
abstract_declarator
: pointer
{
$$ = decl_addptr(decl_Construct(), $1);
}
| pointer direct_abstract_declarator
{
$$ = decl_addptr($2, $1);
}
| direct_abstract_declarator
;
direct_abstract_declarator
: '(' abstract_declarator ')'
{
$$ = $2;
}
| direct_abstract_declarator '[' constant_expression ']'
{
$$ = decl_addary($1, $3);
free($3);
}
| '[' constant_expression ']'
{
$$ = decl_addary(decl_Construct(), $2);
free($2);
}
| direct_abstract_declarator '[' ']'
{
$$ = decl_addary($1, NULL);
}
| '[' ']'
{
$$ = decl_addary(decl_Construct(), NULL);
}
| direct_abstract_declarator '(' parameter_type_list ')'
{
$$ = decl_addfun($1, $3);
}
| '(' parameter_type_list ')'
{
$$ = decl_addfun(decl_Construct(), $2);
}
| direct_abstract_declarator '(' ')'
{
$$ = decl_addfun($1, NULL);
}
| '(' ')'
{
$$ = decl_addfun(decl_Construct(), NULL);
}
;
/*
* XXX - General case constant expressions are not supported. It would
* be easy to implement (for the most part), but there are no cases to
* date that require such a facility. The grammar does allow an
* identifier (or typedef name) to be used since the prototype is not
* processed by CPP. The only integer constant that is supported is
* decimal.
*/
constant_expression
: INTEGER
| IDENTIFIER
| TYPEDEF_NAME
;
%%
/* Data Declarations */
typedef struct {
char *name;
int token;
stt_t stt;
} keyword_t;
typedef struct {
stt_t s_stt;
char *s_str;
} sttpair_t;
/* External Declarations */
static const keyword_t *lookup_keyword(const char *);
static const char *lookup_sttpair(stt_t);
static int getch(void);
static void ungetch(int);
static void skipwhitespace(void);
static int lookahead(int);
static void skipcomment(void);
/* External Definitions */
static char *input = NULL; /* current place in the input stream */
/* at point in stream were identifier is expected */
static int atIDENT = 0;
static decl_t *protop = NULL; /* pointer to prototype */
static const char *errstr = NULL; /* error message */
/*
* lookup_keyword - Given a string, return the keyword_t or NULL.
*/
static const keyword_t *
lookup_keyword(const char *name) {
static const keyword_t keytbl[] = {
{ "register", REGISTER, SCS_REGISTER },
#if UNSUPPORTED
{ "typedef", TYPEDEF, SCS_TYPEDEF },
{ "auto", AUTO, SCS_AUTO },
{ "static", STATIC, SCS_STATIC },
{ "extern", EXTERN, SCS_EXTERN },
#endif /* UNSUPPORTED */
{ "void", VOID, TS_VOID },
{ "char", CHAR, TS_CHAR },
{ "short", SHORT, TS_SHORT },
{ "int", INT, TS_INT },
{ "long", LONG, TS_LONG },
{ "float", FLOAT, TS_FLOAT },
{ "double", DOUBLE, TS_DOUBLE },
{ "signed", SIGNED, TS_SIGNED },
{ "unsigned", UNSIGNED, TS_UNSIGNED },
{ "struct", STRUCT, TS_STRUCT },
{ "union", UNION, TS_UNION },
{ "enum", ENUM, TS_ENUM },
{ "const", CONST, TQ_CONST },
{ "volatile", VOLATILE, TQ_VOLATILE },
{ "restrict", RESTRICT, TQ_RESTRICT },
{ "_RESTRICT_KYWD",RESTRICT_KYWD, TQ_RESTRICT_KYWD},
};
#define NKEYWORD (sizeof (keytbl)/sizeof (keyword_t))
int i;
for (i = 0; i < NKEYWORD; ++i) {
char *s = keytbl[i].name;
if ((*s == *name) && (strcmp(s, name) == 0))
return (&keytbl[i]);
}
return (NULL);
}
/*
* lookup_sttpair - Given an stt_t return a string or NULL.
*
*/
static const char *
lookup_sttpair(stt_t s) {
/* valid type specifier combinations */
static const sttpair_t stttbl[] = {
{ TS_VOID, "void" },
{ TS_CHAR, "char" },
{ TS_SIGNED | TS_CHAR, "signed char" },
{ TS_UNSIGNED | TS_CHAR, "unsigned char" },
{ TS_SHORT, "short" },
{ TS_SIGNED | TS_SHORT, "signed short" },
{ TS_SHORT | TS_INT, "short int" },
{ TS_SIGNED | TS_SHORT | TS_INT,
"signed short int" },
{ TS_UNSIGNED | TS_SHORT,
"unsigned short" },
{ TS_UNSIGNED | TS_SHORT | TS_INT,
"unsigned short int" },
{ TS_INT, "int" },
{ TS_SIGNED, "signed" },
{ TS_SIGNED | TS_INT, "signed int" },
{ TS_NO_TS, "" },
{ TS_UNSIGNED, "unsigned" },
{ TS_UNSIGNED | TS_INT, "unsigned int" },
{ TS_LONG, "long" },
{ TS_SIGNED | TS_LONG, "signed long" },
{ TS_LONG | TS_INT, "long int" },
{ TS_SIGNED | TS_LONG | TS_INT,
"signed long int" },
{ TS_UNSIGNED | TS_LONG, "unsigned long" },
{ TS_UNSIGNED | TS_LONG | TS_INT,
"unsigned long int" },
{ TS_FLOAT, "float" },
{ TS_DOUBLE, "double" },
{ TS_LONG | TS_DOUBLE, "long double" },
{ TS_STRUCT, "struct" },
{ TS_UNION, "union" },
{ TS_ENUM, "enum" },
{ TS_TYPEDEF, "" },
/* non-ANSI type: long long */
{ TS_LONGLONG, "long long" },
{ TS_LONGLONG | TS_INT, "long long int" },
{ TS_SIGNED | TS_LONGLONG,
"signed long long" },
{ TS_UNSIGNED | TS_LONGLONG,
"unsigned long long" },
{ TS_SIGNED | TS_LONGLONG | TS_INT,
"signed long long int" },
{ TS_UNSIGNED | TS_LONGLONG | TS_INT,
"unsigned long long int" },
};
#define NDECLSPEC (sizeof (stttbl)/sizeof (sttpair_t))
int i;
for (i = 0; i < NDECLSPEC; ++i)
if (s == stttbl[i].s_stt)
return (stttbl[i].s_str);
return (NULL);
}
/*
* yylex - return next token from the the input stream.
*
* The lexical analyzer does not recognize all possible C lexical
* elements. It only recognizes those associated with function
* declarations (read: prototypes) and data definitions.
*/
static int
yylex(void) {
char buf[BUFSIZ]; /* string version of token */
int c;
int i = 0;
restart:
skipwhitespace();
switch (c = getch()) {
case '/':
if (lookahead('*')) {
skipcomment();
goto restart;
}
case '.':
if (lookahead('.')) {
if (lookahead('.'))
return (ELLIPSIS);
}
case EOF:
case '(':
case ')':
case ',':
case '[':
case ']':
case ';':
case '*':
return (c);
default:
if ((c == '_') || isalpha(c)) {
const keyword_t *kp;
do {
buf[i++] = c;
c = getch();
} while ((c == '_') || isalnum(c));
ungetch(c);
buf[i] = '\0';
if ((kp = lookup_keyword(buf)) != NULL) {
yylval.stt_val = kp->stt;
return (kp->token);
} else {
yylval.s_val = strdup(buf);
return ((atIDENT) ? IDENTIFIER : TYPEDEF_NAME);
}
} else if (isdigit(c)) {
do {
buf[i++] = c;
} while (isdigit(c = getch()));
ungetch(c);
buf[i] = '\0';
yylval.s_val = strdup(buf);
return (INTEGER);
} else
return (c);
}
/* NOTREACHED */
}
/* getch - return the next character from the input stream. */
static int
getch(void) {
int c;
if ((c = *input) == '\0')
c = EOF;
else /* only advance on non-NULL */
input++;
return (c);
}
/* ungetch - return a character to the input stream. */
static void
ungetch(int c) {
*(--input) = c;
}
/* skipwhitespace - skip over whitespace in the input stream. */
static void
skipwhitespace(void) {
int c;
while (isspace(c = getch()))
;
ungetch(c);
}
/* skipcomment - scan ahead to the next end of comment. */
static void
skipcomment(void) {
loop {
int c;
switch (c = getch()) {
case EOF:
return;
case '*':
if (lookahead('/'))
return;
}
}
/* NOTREACHED */
}
/* lookahead - does next character match 'c'? */
static int
lookahead(int c) {
int ch = getch();
int match;
if (!(match = (ch == c)))
ungetch(ch);
return (match);
}
/* putNtabs - write N '\t' to standard output. */
#if defined(DEBUG)
static void
putNTabs(int n) {
int i;
for (i = 0; i < n; ++i)
putchar('\t');
}
#endif /* DEBUG */
/* D E C L A R A T I O N S P E C I F I E R S */
/*
* Declaration specifiers encode storage class, type specifier and type
* qualifier information. This includes any identifiers associated with
* struct, union or enum declarations. Typedef names are also encoded
* in declaration specifiers.
*/
/* declspec_Construct - allocate and initialize a declspec_t. */
static decl_spec_t *
declspec_Construct(void) {
decl_spec_t *dsp = malloc(sizeof (decl_spec_t));
assert(dsp != NULL);
dsp->ds_stt = SCS_NONE | TS_NO_TS | TQ_NONE;
dsp->ds_id = NULL;
#if defined(MEM_DEBUG)
++declspec_Construct_calls;
#endif
return (dsp);
}
/* declspec_Destroy - free a declspec_t. */
static void
declspec_Destroy(decl_spec_t *dsp) {
free(dsp->ds_id);
free(dsp);
#if defined(MEM_DEBUG)
--declspec_Construct_calls;
#endif
}
/*
* declspec_Init - allocate and initialize a declspec_t given an
* stt_t and identifier.
*
* Note:
* 1) identifier can be NULL.
* 2) errors resulting in the stt_t and identifier are ignored.
*/
static decl_spec_t *
declspec_Init(stt_t s, char *tagp) {
const char *p;
decl_spec_t *dsp = declspec_Construct();
decl_spec_t tmp;
tmp.ds_stt = s;
tmp.ds_id = tagp;
declspec_AddDS(dsp, &tmp, &p); /* XXX ignore any error */
return (dsp);
}
/*
* declspec_VerifySTT - verify that the two given stt_t can be combined.
*
* Note:
* 1) The return value is a const char *, non-NULL to indicate an error.
*/
static char *
declspec_VerifySTT(stt_t s1, stt_t s2) {
stt_t result;
if ((s1 | s2) != (s1 ^ s2))
return ("attempt to add declaration specifier "
"that is already present");
result = (s1 | s2) & TS_MASK;
if (lookup_sttpair(result) == NULL) {
if (STT_isbasic(result) && STT_isderived(result))
return ("attempt to combine basic and "
"derived types");
if (STT_isvoid(result) &&
(STT_isbasic(result) || STT_isderived(result)))
return ("attempt to combine void with "
"other type specifiers");
if (STT_isfloat(result) && STT_isint(result))
return ("attempt to combine floating and "
"integer type specifiers");
if (STT_ischar(result) && STT_isint(result))
return ("attempt to combine character and "
"integer type specifiers");
if (STT_has_explicit_sign(result) &&
(STT_isfloat(result) || STT_isderived(result)))
return ("attempt to combine signed or "
"unsigned with float or derived type");
return ("invalid declaration specifier");
}
return (NULL);
}
/*
* declspec_AddSTT - add an stt_t to a decl_spec_t.
*
* Note:
* 1) The "long long" type is handled here.
* If both stt_t include TS_LONG then this is an attempt to use
* "long long". The TS_LONG is cleared from the s1 and s2 and
* then TS_LONGLONG is added to s2. The resulting s1 and s2 are
* passed to declspec_VerifySTT to determine if the result is valid.
*
* 2) This method of handling "long long" does detect the case of
* "long double long" and all it's variant forms.
*/
static decl_spec_t *
declspec_AddSTT(decl_spec_t *dsp, stt_t s2, const char **err) {
stt_t s1 = dsp->ds_stt;
/* non-ANSI type: long long */
if ((s1 & TS_LONG) && (s2 & TS_LONG)) {
s1 &= ~(TS_LONG);
dsp->ds_stt = s1;
s2 &= ~(TS_LONG);
s2 |= TS_LONGLONG;
}
if ((*err = declspec_VerifySTT(s1, s2)) == NULL)
dsp->ds_stt |= s2;
return (dsp);
}
/*
* declpec_AddDS - add a decl_spec_t to an existing decl_spec_t.
*/
static decl_spec_t *
declspec_AddDS(decl_spec_t *dsp, decl_spec_t *tsp, const char **err) {
declspec_AddSTT(dsp, tsp->ds_stt, err);
if ((*err == NULL) && tsp->ds_id) {
free(dsp->ds_id);
dsp->ds_id = strdup(tsp->ds_id);
assert(dsp->ds_id != NULL);
}
return (dsp);
}
/*
* declspec_GetSTT - return the stt_t within a decl_spec_t.
*/
static stt_t
declspec_GetSTT(decl_spec_t *dsp) {
return (dsp->ds_stt);
}
/*
* declspec_GetTag - return the identifier within a decl_spec_t.
*/
static char *
declspec_GetTag(decl_spec_t *dsp) {
return (dsp->ds_id);
}
/*
* declspec_ToString - convert a decl_spec_t into a string.
*
* Note:
* 1) The form of the resulting string is always the same, i.e.
*
* [register] [type_specifier] [const] [volatile]
*
* dsp must be correct
*
*/
char *
declspec_ToString(char *bufp, decl_spec_t *dsp) {
const char *s;
int something = 0;
*bufp = '\0';
/* storage class specifier */
switch (dsp->ds_stt & SCS_MASK) {
case SCS_REGISTER:
strcat(bufp, "register");
something = 1;
break;
}
s = lookup_sttpair(dsp->ds_stt & TS_MASK);
/* type specifier */
switch (dsp->ds_stt & TS_MASK) {
case TS_STRUCT:
case TS_UNION:
case TS_ENUM:
if (something)
strcat(bufp, " ");
strcat(bufp, s);
strcat(bufp, " ");
strcat(bufp, dsp->ds_id);
break;
case TS_TYPEDEF:
if (something)
strcat(bufp, " ");
strcat(bufp, dsp->ds_id);
break;
default:
if (something)
strcat(bufp, " ");
strcat(bufp, s);
break;
}
if (s)
something = 1;
if (something && (dsp->ds_stt & TQ_MASK))
strcat(bufp, " ");
if (dsp->ds_stt & TQ_CONST) /* type qualifier */
strcat(bufp, "const");
if (dsp->ds_stt & TQ_VOLATILE) {
if (dsp->ds_stt & TQ_CONST)
strcat(bufp, " ");
strcat(bufp, "volatile");
}
/*
* It currently acknowledges and ignores restrict or _RESTRICT_KYWD
* in code generation because of the uncertain behavior of "restrict".
*/
if (dsp->ds_stt & TQ_RESTRICT)
strcat(bufp, "");
if (dsp->ds_stt & TQ_RESTRICT_KYWD)
strcat(bufp, "");
return (bufp);
}
/* T Y P E M O D I F I E R S */
/*
* Type modifiers encode the "array of...", "pointer to ..." and
* "function returning ..." aspects of C types. The modifiers are kept
* as a linked list in precedence order. The grammar encodes the
* precedence order described by the standard.
*
* Type modifiers are always added at the end of list and the list is
* always traversed from head to tail.
*/
/* type_Construct - allocate and initialize a type_t. */
static type_t *
type_Construct(void) {
type_t *tp = malloc(sizeof (type_t));
assert(tp != NULL);
tp->t_next = NULL; /* generic */
tp->t_dt = DD_NONE;
tp->t_nargs = 0; /* DD_FUN */
tp->t_ellipsis = 0;
tp->t_args = NULL;
/* DD_PTR */
tp->t_stt = (SCS_NONE | TS_NO_TS | TQ_NONE);
tp->t_sizestr = NULL; /* DD_ARY */
#if defined(MEM_DEBUG)
++type_Construct_calls;
#endif
return (tp);
}
/* type_Destroy - free a type_t list. */
static void
type_Destroy(type_t *tp) {
while (tp) {
type_t *nextp = tp->t_next;
switch (tp->t_dt) {
case DD_FUN:
decl_Destroy(tp->t_args);
break;
case DD_PTR:
break;
case DD_ARY:
free(tp->t_sizestr);
break;
}
free(tp);
tp = nextp;
#if defined(MEM_DEBUG)
--type_Construct_calls;
#endif
}
}
/*
* type_SetPtr - make a type_t into a "pointer to ..." variant.
*
* Note:
* 1) The stt_t will encode any type qualifiers (const, volatile).
*/
static type_t *
type_SetPtr(type_t *tp, stt_t s) {
assert(tp->t_dt == DD_NONE);
tp->t_dt = DD_PTR;
tp->t_stt = s & TQ_MASK;
return (tp);
}
/*
* type_SetAry - make a type_t into an "array of ...", variant.
*
* Note:
* 1) The array dimension can be NULL to indicate undefined, i.e. [].
*/
static type_t *
type_SetAry(type_t *tp, char *dim) {
assert(tp->t_dt == DD_NONE);
assert(tp->t_sizestr == NULL);
tp->t_dt = DD_ARY;
if (dim) {
tp->t_sizestr = strdup(dim);
assert(tp->t_sizestr != NULL);
} else
tp->t_sizestr = NULL;
return (tp);
}
/*
* type_SetFun - make a type_t into a "function returning ..." variant.
*
* Note:
* 1) The argument list can be NULL to indicate undefined, i.e. ().
*/
static type_t *
type_SetFun(type_t *tp, decl_t *arglist) {
assert(tp->t_dt == DD_NONE);
tp->t_dt = DD_FUN;
if (arglist) {
tp->t_nargs = decl_GetArgLength(arglist);
tp->t_args = arglist;
tp->t_ellipsis = arglist->d_ellipsis;
}
return (tp);
}
/*
* type_AddTail - add a type_t to the end of an existing type_t list.
*
* Note:
* 1) The type_t *tp is added to the end of the type_t *dp list.
*/
static type_t *
type_AddTail(type_t *dp, type_t *tp) {
type_t *lastp = dp;
type_t *p;
while (p = lastp->t_next)
lastp = p;
lastp->t_next = tp;
return (dp);
}
#if defined(DEBUG)
/* type_PrintType - print a type_t list onto standard output. */
static void
type_PrintType(type_t *tp, int lvl) {
decl_spec_t tmp;
char buf[BUFSIZ];
while (tp) {
putNTabs(lvl);
switch (tp->t_dt) {
case DD_PTR:
tmp.ds_stt = tp->t_stt;
tmp.ds_id = NULL;
printf("[%s] ptr to\n", declspec_ToString(buf, &tmp));
break;
case DD_FUN:
printf("fun [%d%c] %s\n",
tp->t_nargs,
(tp->t_ellipsis)? '+' : '=',
(tp->t_args)? "with arguments" :
"undefined arguments");
if (tp->t_args) {
decl_PrintDecl(tp->t_args, lvl + 1);
if (tp->t_ellipsis) {
putNTabs(lvl + 1);
printf("...\n");
}
}
break;
case DD_ARY:
printf("ary [%s] of\n",
(tp->t_sizestr)? tp->t_sizestr : "");
break;
}
tp = tp->t_next;
}
}
#endif /* DEBUG */
/*
* type_Verify - verify a type_t list for semantic correctness.
*
* Note:
* 1) C supports most combinations of type modifiers.
* It does not support three combinations, they are:
*
* function returning array
* array of functions
* function returning function
*
* 2) The enum values associated with type modifiers (i.e. DD_*)
* cannot be modified without changing the table included within the
* function.
*
* 3) The function returns NULL to indicate that the type modifier
* list is valid and non-NULL to indicate an error.
*
* 4) A type_t of NULL is permitted to indicate an empty type_t list.
*/
static const char *
type_Verify(type_t *tp) {
static const char *dttbl[4][4] = {
/* NONE ARY FUN PTR */
/* NONE */ {NULL, NULL, NULL, NULL},
/* ARY */ {NULL, NULL, "array of functions", NULL},
/* FUN */ {NULL, "function returning array",
"function returning function", NULL},
/* PTR */ {NULL, NULL, NULL, NULL},
};
if (tp) {
type_t *nextp;
do {
const char *p;
decl_type_t nt;
nt = (nextp = tp->t_next)? nextp->t_dt : DD_NONE;
if ((p = dttbl[tp->t_dt][nt]) != NULL)
return (p);
} while (tp = nextp);
}
return (NULL);
}
/* type_GetNext - return the next type_t in the list. */
type_t *
type_GetNext(type_t *tp) {
return (tp->t_next);
}
/*
* The following group of functions return and or
* test various aspects of type modifiers.
*
* 1) The three functions: type_IsPtrTo, type_IsFunction and
* type_IsArray will accept an argument of NULL.
*
* 2) All other functions require one of the above three to be true.
* Various asserts are in place to verify correct usage.
*/
int
type_IsArray(type_t *tp) {
return (tp && (tp->t_dt == DD_ARY));
}
char *
type_GetArraySize(type_t *tp) {
assert(tp->t_dt == DD_ARY);
return (tp->t_sizestr);
}
int
type_IsPtrTo(type_t *tp) {
return (tp && (tp->t_dt == DD_PTR));
}
stt_t
type_GetPtrToTypeQual(type_t *tp) {
assert(tp->t_dt == DD_PTR);
return (tp->t_stt);
}
int
type_IsFunction(type_t *tp) {
return (tp && (tp->t_dt == DD_FUN));
}
int
type_GetArgLength(type_t *tp) {
assert(tp->t_dt == DD_FUN);
return (tp->t_nargs);
}
int
type_IsVarargs(type_t *tp) {
while (tp && tp->t_dt == DD_PTR)
tp = tp->t_next;
assert(tp->t_dt == DD_FUN);
return (tp->t_ellipsis);
}
decl_t *
type_GetArg(type_t *tp) {
assert(tp->t_dt == DD_FUN);
return (tp->t_args);
}
/*
* type_IsPtrFun - determine if the type_t results in a call-able function.
*
* Note:
* 1) The argument can be NULL.
*
* 2) The test is true if the type_t list is number of DD_PTR followed
* by a DD_FUN.
*/
int
type_IsPtrFun(type_t *tp) {
if (! (tp && (tp->t_dt == DD_PTR)))
return (0);
tp = tp->t_next;
while (tp && (tp->t_dt == DD_PTR))
tp = tp->t_next;
return (tp && (tp->t_dt == DD_FUN));
}
/* D E C L A R A T O R */
/*
* A decl_t encodes the name,
* declaration specifiers and type modifiers of an object.
*/
/* decl_Construct - allocate a decl_t. */
static decl_t *
decl_Construct(void) {
decl_t *dp = malloc(sizeof (decl_t));
assert(dp != NULL);
dp->d_name = NULL;
dp->d_type = NULL;
dp->d_next = NULL;
dp->d_ds = declspec_Construct();
dp->d_ellipsis = 0;
#if defined(MEM_DEBUG)
++decl_Construct_calls;
#endif
return (dp);
}
/* decl_Destroy - free a decl_t list. */
void
decl_Destroy(decl_t *dp) {
while (dp) {
decl_t *nextp = dp->d_next;
type_Destroy(dp->d_type);
declspec_Destroy(dp->d_ds);
free(dp->d_name);
free(dp);
dp = nextp;
#if defined(MEM_DEBUG)
--decl_Construct_calls;
#endif
}
}
/*
* decl_GetArgLength - return the length of a decl_t list.
*
* Note:
* 1) The argument may be NULL to indicate an empty list, len == 0.
*/
int
decl_GetArgLength(decl_t *dp) {
int len;
for (len = 0; dp; dp = dp->d_next)
++len;
return (len);
}
/*
* The following group of functions get or test various aspects of a decl_t.
*/
decl_t *
decl_GetNext(decl_t *dp) {
return (dp->d_next);
}
stt_t
decl_GetDeclSpec(decl_t *dp) {
return (declspec_GetSTT(dp->d_ds));
}
char *
decl_GetDSName(decl_t *dp) {
return (declspec_GetTag(dp->d_ds));
}
type_t *
decl_GetType(decl_t *dp) {
return (dp->d_type);
}
int
decl_IsVarargs(decl_t *dp) {
return (dp->d_ellipsis);
}
int
decl_IsFunction(decl_t *dp) {
return (type_IsFunction(dp->d_type));
}
char *
decl_GetName(decl_t *dp) {
return (dp->d_name);
}
/*
* decl_AddArg - add a decl_t to the end of an decl_t list.
*/
static decl_t *
decl_AddArg(decl_t *dp, decl_t *tp) {
decl_t *lastp = dp;
decl_t *p;
while (p = lastp->d_next)
lastp = p;
lastp->d_next = tp;
return (dp);
}
/*
* decl_IsVoid - return true if the decl_t is a "pure" void declaration.
*/
static int
decl_IsVoid(decl_t *dp) {
return ((declspec_GetSTT(dp->d_ds) & TS_VOID) && (dp->d_type == NULL));
}
/*
* decl_IsVoidArray - return true if the decl_t includes "void []".
*/
static int
decl_IsVoidArray(decl_t *dp) {
int retval = 0;
type_t *tp = dp->d_type;
if (tp) {
type_t *np;
while (np = type_GetNext(tp))
tp = np;
retval = type_IsArray(tp) &&
(declspec_GetSTT(dp->d_ds) & TS_VOID);
}
return (retval);
}
/*
* decl_Verify - verify a decl_t.
*/
static const char *
decl_Verify(decl_t *dp) {
const char *ep = NULL;
if (decl_IsVoid(dp))
ep = "type is void";
else if (decl_IsVoidArray(dp))
ep = "type is void []";
else
ep = type_Verify(dp->d_type);
return (ep);
}
/*
* decl_VerifyArgs - verify a decl_t list.
*/
static const char *
decl_VerifyArgs(decl_t *dp) {
decl_t *tp = dp;
const char *ep = NULL;
if (dp) {
int nv = 0;
int nargs = decl_GetArgLength(dp);
for (; dp; dp = dp->d_next)
if (decl_IsVoid(dp)) {
++nv;
if (decl_GetName(dp))
ep = "argument list includes "
"void with identifier";
} else if (decl_IsVoidArray(dp))
ep = "argument list includes void []";
if (nv) { /* there was some void */
if (nargs > 1)
ep = "argument list includes void";
if (tp->d_ellipsis)
ep = "argument list includes void and \"...\"";
}
}
return (ep);
}
/* decl_AddDS - add a decl_spec_t to a decl_t. */
static decl_t *
decl_AddDS(decl_t *dp, decl_spec_t *dsp, const char **err) {
declspec_AddDS(dp->d_ds, dsp, err);
return (dp);
}
/*
* decl_SetName - set the name associated with a decl_t.
*
* Note:
* 1) Any previously known name is free'd.
*/
decl_t *
decl_SetName(decl_t *dp, char *s) {
free(dp->d_name);
dp->d_name = strdup(s);
assert(dp->d_name != NULL);
return (dp);
}
/*
* decl_AddTypeTail - add a type_t to the end of a decl_t type_t list.
*/
static decl_t *
decl_AddTypeTail(decl_t *dp, type_t *tp) {
if (dp->d_type)
type_AddTail(dp->d_type, tp);
else
dp->d_type = tp;
return (dp);
}
/*
* decl_addptr - add a DD_PTR type_t to the end of a decl_t type_t list.
*/
static decl_t *
decl_addptr(decl_t *dp, type_t *tp) {
decl_AddTypeTail(dp, tp);
return (dp);
}
/*
* decl_addary - allocate and add a DD_ARY type_t to the end of
* a decl_t type_t list.
*/
static decl_t *
decl_addary(decl_t *dp, char *sizep) {
type_t *tp = type_Construct();
type_SetAry(tp, sizep);
decl_AddTypeTail(dp, tp);
return (dp);
}
/*
* decl_addfun - allocate and add a DD_FUN type_t to the end of a
* decl_t type_t list.
*/
static decl_t *
decl_addfun(decl_t *dp, decl_t *arglist) {
const char *sp;
type_t *tp = type_Construct();
if (sp = decl_VerifyArgs(arglist))
yyerror(sp);
type_SetFun(tp, arglist);
decl_AddTypeTail(dp, tp);
return (dp);
}
/*
* decl_addellipsis - set the ellipsis state in a decl_t.
*
* Note:
* 1) This function is only used in the grammar in the
* parameter list parsing.
*/
static decl_t *
decl_addellipsis(decl_t *dp) {
dp->d_ellipsis = 1;
return (dp);
}
#if defined(DEBUG)
static void
decl_PrintDecl(decl_t *dp, int lvl) {
char buf[BUFSIZ];
while (dp) {
putNTabs(lvl);
printf("name = %s, ds = %s\n",
(dp->d_name)? dp->d_name : "<null>",
declspec_ToString(buf, dp->d_ds));
if (dp->d_type)
type_PrintType(dp->d_type, lvl + 1);
dp = dp->d_next;
}
}
#endif /* DEBUG */
static char *
char_getend(char *s) {
while (*s != '\0')
++s;
return (s);
}
char *
decl_ToString(char *bufp, decl_dts_t out, decl_t *dp,
const char *altname) {
char tmp[BUFSIZ];
char tmp2[BUFSIZ];
const char *namep;
char *bend = bufp;
type_t *tp = dp->d_type;
int ffun = 1;
switch (out) {
default:
out = DTS_DECL;
/* FALLTHRU */
case DTS_DECL:
if (altname == NULL) {
namep = dp->d_name;
} else {
namep = altname;
}
break;
case DTS_CAST:
namep = "(*)";
break;
case DTS_RET:
if (altname == NULL) {
namep = "_return";
} else {
namep = altname;
}
break;
}
*bufp = '\0';
strcpy(tmp, (namep) ? namep : "");
while (tp) {
switch (tp->t_dt) {
case DD_PTR:
if (tp->t_next &&
((tp->t_next->t_dt == DD_ARY) ||
(tp->t_next->t_dt == DD_FUN))) {
if (out == DTS_RET) {
sprintf(bufp, "(*%s)", namep);
} else {
sprintf(bufp, "(*%s)", tmp);
}
} else if (tp->t_stt == TQ_CONST) {
sprintf(bufp, "*const %s", tmp);
} else if (tp->t_stt == TQ_VOLATILE) {
sprintf(bufp, "*volatile %s", tmp);
/*
* It currently acknowledges and ignores restrict
* or _RESTRICT_KYWD in code generation because
* of the uncertain behavior of "restrict".
*/
} else if (tp->t_stt == TQ_RESTRICT) {
sprintf(bufp, "*%s", tmp);
} else if (tp->t_stt == TQ_RESTRICT_KYWD) {
sprintf(bufp, "*%s", tmp);
} else {
sprintf(bufp, "*%s", tmp);
}
break;
case DD_ARY:
sprintf(bufp, "%s[%s]",
tmp, (tp->t_sizestr)? tp->t_sizestr : "");
break;
case DD_FUN:
if (out == DTS_RET && ffun == 1) {
strcpy(bufp, namep);
ffun = 0;
} else if (tp->t_args == NULL) {
sprintf(bufp, "%s()", tmp);
} else {
char buf2[BUFSIZ];
decl_t *argp = tp->t_args;
sprintf(bufp, "%s(", tmp);
bend = char_getend(bufp);
for (argp = tp->t_args; argp; /* noinc */) {
decl_ToString(buf2, DTS_DECL, argp,
NULL);
sprintf(bend, " %s", buf2);
bend = char_getend(bend);
if (argp = argp->d_next) {
sprintf(bend, ",");
bend = char_getend(bend);
}
}
if (tp->t_ellipsis) {
sprintf(bend, ", ...");
bend = char_getend(bend);
}
sprintf(bend, ")");
}
break;
}
tp = tp->t_next;
strcpy(tmp, bufp);
}
if (out == DTS_CAST) {
sprintf(bufp, "(%s %s)",
declspec_ToString(tmp2, dp->d_ds), tmp);
} else {
sprintf(bufp, "%s %s",
declspec_ToString(tmp2, dp->d_ds), tmp);
}
return (bufp);
}
decl_t *
decl_AddArgNames(decl_t *dp) {
int argno = 0;
decl_t *p = dp;
if (decl_IsFunction(dp)) {
int argno = 0;
decl_t *p = dp->d_type->t_args;
while (p) {
char *s = decl_GetName(p);
if ((s == NULL) && !decl_IsVoid(p)) {
char buf[BUFSIZ];
sprintf(buf, "arg%d", argno);
s = strdup(buf);
decl_SetName(p, s);
}
p = p->d_next;
++argno;
}
}
return (dp);
}
const char *
decl_Parse(char *str, decl_t **dpp) {
errstr = NULL; /* setup the (static) globals */
input = str;
atIDENT = 0;
protop = NULL;
yyparse(); /* parse the prototype */
if (errstr == NULL) { /* success */
*dpp = protop;
decl_AddArgNames(protop);
} else { /* failure */
*dpp = NULL;
decl_Destroy(protop);
}
return (errstr);
}
static void
yyerror(const char *err) {
errstr = err;
}
#if defined(DEBUG)
/* main */
static int yydebug = 1;
int
main(int argc, char *argv[]) {
int i;
yydebug = 1;
for (i = 1; i < argc; ++i) {
const char *es;
char buf[BUFSIZ];
decl_t *pp;
if (es = decl_Parse(argv[i], &pp))
printf("parse failure: %s\n", es);
else {
#if GR_DEBUG
decl_PrintDecl(pp, 0);
decl_AddArgNames(pp);
#endif
printf("---\n%s;\n",
decl_ToString(buf, DTS_DECL, pp, NULL));
printf("%s\n",
decl_ToString(buf, DTS_CAST, pp, NULL));
printf("%s;\n",
decl_ToString(buf, DTS_RET, pp, "%s"));
#ifdef TRACE
printf("\n\nTrace Info\n");
decl_PrintTraceInfo(pp);
#endif
}
decl_Destroy(pp);
#if defined(MEM_DEBUG)
printf("declspec : %d\n", declspec_Construct_calls);
printf("type : %d\n", type_Construct_calls);
printf("decl : %d\n", decl_Construct_calls);
#endif
}
return (0);
}
#ifdef TRACE
void
decl_PrintTraceInfo(decl_t *dp) {
char buf[BUFSIZ];
char f_type[BUFSIZ];
char f_print[BUFSIZ];
char a_name[BUFSIZ];
char a_type[BUFSIZ];
char a_print[BUFSIZ];
decl_t *funargs;
type_t *tp;
int isptrfun;
if (dp == NULL)
return;
fprintf(stderr, "interface = %s\n",
(dp->d_name) ? dp->d_name : "<null>");
isptrfun = type_IsPtrFun(dp->d_type);
if (type_IsFunction(dp->d_type) || isptrfun)
decl_GetTraceInfo(dp, f_type, f_print, &funargs);
else
return;
fprintf(stderr, "return type = %s\n", f_type);
fprintf(stderr, "print function = %s\n", f_print);
if (isptrfun)
fprintf(stderr, "function is function pointer\n");
if (type_IsVarargs(dp->d_type))
fprintf(stderr, "function is varargs\n");
while (funargs) {
snprintf(a_type, BUFSIZ, "%s ",
declspec_ToString(buf, funargs->d_ds));
snprintf(a_print, BUFSIZ, "%s",
de_const(declspec_ToString(buf, funargs->d_ds)));
tp = funargs->d_type;
while (tp) {
if (tp->t_dt == DD_PTR || tp->t_dt == DD_ARY) {
strcat(a_type, "*");
strcat(a_print, "_P");
}
tp = tp->t_next;
}
if (funargs->d_name) {
snprintf(a_name, BUFSIZ, "%s",
funargs->d_name ? funargs->d_name : "<nil>");
fprintf(stderr, "arg name = %s\n", a_name);
fprintf(stderr, "arg type = %s\n", a_type);
fprintf(stderr, "print function = %s\n", a_print);
} else {
strcpy(a_name, "");
strcpy(a_print, "");
fprintf(stderr, "arg type = %s\n", a_type);
}
funargs = funargs->d_next;
}
}
#endif /* TRACE */
#endif /* DEBUG */
static char *
de_const(char *str)
{
return (str);
}
void
decl_GetTraceInfo(decl_t *dp, char *f_type, char *f_print, decl_t **funargs)
{
char buf[BUFSIZ];
type_t *tp;
if (dp == NULL)
return;
snprintf(f_type, BUFSIZ, "%s ",
declspec_ToString(buf, dp->d_ds));
snprintf(f_print, BUFSIZ, "%s",
de_const(declspec_ToString(buf, dp->d_ds)));
tp = dp->d_type;
while (tp) {
if (tp->t_dt == DD_PTR) {
strcat(f_type, "*");
strcat(f_print, "*");
}
tp = tp->t_next;
}
strcat(f_type, "%s");
tp = decl_GetType(dp);
if (type_IsPtrFun(tp)) {
while (tp->t_dt != DD_FUN)
tp = tp->t_next;
*funargs = tp->t_args;
} else {
*funargs = dp->d_type->t_args;
}
}
char *
decl_ToFormal(decl_t *dp)
{
char tmp[BUFSIZ];
static char bufp[BUFSIZ];
char *bend;
type_t *tp = dp->d_type;
tmp[0] = 0;
bufp[0] = 0;
bend = bufp;
while (tp) {
switch (tp->t_dt) {
case DD_ARY:
sprintf(bufp, "%s[%s]", tmp,
(tp->t_sizestr)? tp->t_sizestr : "");
break;
case DD_FUN:
if (tp->t_args != NULL) {
char buf2[BUFSIZ];
decl_t *argp = tp->t_args;
bend = char_getend(bufp);
for (argp = tp->t_args; argp; /* noinc */) {
decl_ToString(buf2, DTS_DECL, argp,
NULL);
sprintf(bend, " %s", buf2);
bend = char_getend(bend);
if (argp = argp->d_next) {
sprintf(bend, ",");
bend = char_getend(bend);
}
}
if (tp->t_ellipsis) {
sprintf(bend, ", ...");
bend = char_getend(bend);
}
sprintf(bend, "");
}
break;
}
tp = tp->t_next;
strcpy(tmp, bufp);
}
sprintf(bufp, "%s", tmp);
return (bufp);
}