diff --git a/lang/cem/cemcom/.distr b/lang/cem/cemcom/.distr index 30ad8143d..ad3a0a9f4 100644 --- a/lang/cem/cemcom/.distr +++ b/lang/cem/cemcom/.distr @@ -98,3 +98,4 @@ type.c type.str util.str util.c +stab.c diff --git a/lang/cem/cemcom/BigPars b/lang/cem/cemcom/BigPars index f875eaeb0..1c3888eb0 100644 --- a/lang/cem/cemcom/BigPars +++ b/lang/cem/cemcom/BigPars @@ -143,4 +143,8 @@ !File: regcount.h #undef REGCOUNT 1 /* count occurrences for register messages */ +!File: dbsymtab.h +#define DBSYMTAB 1 /* ability to produce symbol table for debugger +*/ + diff --git a/lang/cem/cemcom/SmallPars b/lang/cem/cemcom/SmallPars index d4e1ae18a..c50f1ad44 100644 --- a/lang/cem/cemcom/SmallPars +++ b/lang/cem/cemcom/SmallPars @@ -144,3 +144,8 @@ #undef REGCOUNT 1 /* count occurrences for register messages */ +!File: dbsymtab.h +#undef DBSYMTAB 1 /* ability to produce symbol table for debugger +*/ + + diff --git a/lang/cem/cemcom/cemcom.1 b/lang/cem/cemcom/cemcom.1 index 7151d1578..89807aaa9 100644 --- a/lang/cem/cemcom/cemcom.1 +++ b/lang/cem/cemcom/cemcom.1 @@ -29,6 +29,8 @@ the same as \fB\-D\fIname\fR=1. insert \fIdirname\fR in the list of include directories. .IP \fB\-M\fP\fIn\fP set maximum identifier length to \fIn\fP. +.IP \fB\-g\fP +produce a DBX-style symbol table. .IP \fB\-n\fR do not generate EM register messages. The user-declared variables are not stored into registers on the target diff --git a/lang/cem/cemcom/code.c b/lang/cem/cemcom/code.c index 6f03dd3ed..5805bf3c9 100644 --- a/lang/cem/cemcom/code.c +++ b/lang/cem/cemcom/code.c @@ -3,9 +3,10 @@ * See the copyright notice in the ACK home directory, in the file "Copyright". */ /* $Header$ */ -/* C O D E - G E N E R A T I N G R O U T I N E S */ +/* C O D E - G E N E R A T I N G R O U T I N E S */ #include "lint.h" +#include "dbsymtab.h" #ifndef LINT #include #else @@ -34,7 +35,10 @@ #include "atw.h" #include "assert.h" #include "noRoption.h" -#include "file_info.h" +#include "LLlex.h" +#ifdef DBSYMTAB +#include +#endif /* DBSYMTAB */ label lab_count = 1; label datlab_count = 1; @@ -68,6 +72,24 @@ init_code(dst_file) fatal("cannot write to %s\n", dst_file); C_magic(); C_ms_emx(word_size, pointer_size); +#ifdef DBSYMTAB + if (options['g']) { + extern char *source; + + C_ms_std(source, N_SO, 0); + stb_typedef(int_type, "int"); + stb_typedef(char_type, "char"); + stb_typedef(long_type, "long"); + stb_typedef(short_type, "short"); + stb_typedef(uchar_type, "unsigned char"); + stb_typedef(ushort_type, "unsigned short"); + stb_typedef(ulong_type, "unsigned long"); + stb_typedef(uint_type, "unsigned int"); + stb_typedef(float_type, "float"); + stb_typedef(double_type, "double"); + stb_typedef(void_type, "void"); + } +#endif /* DBSYMTAB */ #ifdef USE_TMP #ifdef PREPEND_SCOPES C_insertpart(tmp_id = C_getid()); @@ -140,7 +162,7 @@ prepend_scopes() while (se != 0) { register struct idf *id = se->se_idf; register struct def *df = id->id_def; - + if (df && (df->df_initialized || df->df_used || df->df_alloc)) code_scope(id->id_text, df); se = se->next; @@ -159,7 +181,7 @@ code_scope(text, def) as given by def, if meaningful. */ int fund = def->df_type->tp_fund; - + switch (def->df_sc) { case EXTERN: case GLOBAL: @@ -257,6 +279,14 @@ begin_proc(ds, idf) /* to be called when entering a procedure */ C_fil_dlb(file_name_label, (arith)0); C_lin((arith)LineNumber); } +#ifdef DBSYMTAB + if (options['g']) { + stb_string(def, FUNCTION, name); + if (! strcmp(name, "main")) { + C_ms_stb_cst(name, N_MAIN, 0, (arith) 0); + } + } +#endif } end_proc(fbytes) @@ -339,6 +369,9 @@ do_return() probably smarter than generating a direct return. Return sequences may be expensive. */ +#ifdef DBSYMTAB + if (options['g']) db_line(dot.tk_file, dot.tk_line); +#endif /* DBSYMTAB */ C_bra(return2_label); } @@ -373,7 +406,7 @@ code_declaration(idf, expr, lvl, sc) Since the expression may be modified in the process, code_declaration() frees it after use, as the caller can no longer do so. - + If there is a storage class indication (EXTERN/STATIC), code_declaration() will generate an exa or ina. The sc is the actual storage class, as given in the @@ -386,9 +419,15 @@ code_declaration(idf, expr, lvl, sc) register struct def *def = idf->id_def; register arith size = def->df_type->tp_size; int def_sc = def->df_sc; - - if (def_sc == TYPEDEF) /* no code for typedefs */ + + if (def_sc == TYPEDEF) { /* no code for typedefs */ +#ifdef DBSYMTAB + if (options['g']) { + stb_typedef(def->df_type, idf->id_text); + } +#endif /* DBSYMTAB */ return; + } if (sc == EXTERN && expr && !is_anon_idf(idf)) error("%s is extern; cannot initialize", idf->id_text); if (lvl == L_GLOBAL) { /* global variable */ @@ -418,6 +457,11 @@ code_declaration(idf, expr, lvl, sc) /* they are handled on the spot and get an integer label in EM. */ +#ifdef DBSYMTAB + if (options['g'] && ! expr) { + stb_string(def, sc, idf->id_text); + } +#endif /* DBSYMTAB */ C_df_dlb((label)def->df_address); if (expr) { /* there is an initialisation */ } @@ -436,6 +480,11 @@ code_declaration(idf, expr, lvl, sc) break; case AUTO: case REGISTER: +#ifdef DBSYMTAB + if (options['g']) { + stb_string(def, sc, idf->id_text); + } +#endif /* DBSYMTAB */ if (expr) loc_init(expr, idf); break; @@ -456,7 +505,7 @@ loc_init(expr, id) */ register struct expr *e = expr; register struct type *tp = id->id_def->df_type; - + ASSERT(id->id_def->df_sc != STATIC); switch (tp->tp_fund) { case ARRAY: @@ -507,17 +556,22 @@ bss(idf) /* bss() allocates bss space for the global idf. */ arith size = idf->id_def->df_type->tp_size; - + #ifndef PREPEND_SCOPES code_scope(idf->id_text, idf->id_def); #endif PREPEND_SCOPES +#ifdef DBSYMTAB + if (options['g']) { + stb_string(idf->id_def, idf->id_def->df_sc, idf->id_text); + } +#endif /* DBSYMTAB */ /* Since bss() is only called if df_alloc is non-zero, and since df_alloc is only non-zero if size >= 0, we have: */ /* but we already gave a warning at the declaration of the array. Besides, the message given here does not apply to voids - + if (options['R'] && size == 0) warning("actual array of size 0"); */ @@ -558,6 +612,9 @@ code_expr(expr, val, code, tlbl, flbl) #ifndef LINT if (! options['L']) /* profiling */ C_lin((arith)(expr->ex_line)); +#ifdef DBSYMTAB + if (options['g']) db_line(expr->ex_file, (unsigned int)expr->ex_line); +#endif EVAL(expr, val, code, tlbl, flbl); #else LINT lint_expr(expr, code ? USED : IGNORED); @@ -583,6 +640,9 @@ code_break() { register struct stmt_block *stmt_block = stmt_stack; +#ifdef DBSYMTAB + if (options['g']) db_line(dot.tk_file, dot.tk_line); +#endif /* DBSYMTAB */ if (stmt_block) C_bra(stmt_block->st_break); else @@ -600,6 +660,9 @@ code_continue() while (stmt_block) { if (stmt_block->st_continue) { +#ifdef DBSYMTAB + if (options['g']) db_line(dot.tk_file, dot.tk_line); +#endif /* DBSYMTAB */ C_bra(stmt_block->st_continue); return; } @@ -651,3 +714,19 @@ prc_exit() C_asp(pointer_size); } } + +#ifdef DBSYMTAB +db_line(file, line) + char *file; + unsigned int line; +{ + static unsigned oldline; + static char *oldfile; + + if (file != oldfile || line != oldline) { + C_ms_std((char *) 0, N_SLINE, (int) line); + oldline = line; + oldfile = file; + } +} +#endif /* DBSYMTAB */ diff --git a/lang/cem/cemcom/declar.g b/lang/cem/cemcom/declar.g index 4394ccc6e..a4bcdebcc 100644 --- a/lang/cem/cemcom/declar.g +++ b/lang/cem/cemcom/declar.g @@ -7,6 +7,7 @@ { #include "lint.h" +#include "dbsymtab.h" #include #include "nobitfield.h" #include "debug.h" @@ -54,7 +55,7 @@ declaration This means that something like: unsigned extern int short xx; is perfectly good C. - + On top of that, multiple occurrences of storage_class_specifiers, unsigned_specifiers and size_specifiers are errors, but a second type_specifier should end the decl_specifiers and be treated as @@ -65,7 +66,7 @@ declaration occurrence of the type_specifier in the grammar (we have no choice), collecting all data in a `struct decspecs' and turning that data structure into what we want. - + The existence of declarations like short typedef yepp; makes all hope of writing a specific grammar for typedefs illusory. @@ -223,8 +224,19 @@ initializer(struct idf *idf; int sc;) #ifdef LINT change_state(idf, SET); #endif LINT +#ifdef DBSYMTAB + if (options['g'] && level >= L_LOCAL && expr) { + db_line(expr->ex_file, (unsigned) expr->ex_line) +; + } +#endif /* DBSYMTAB */ code_declaration(idf, expr, level, sc); } +#ifdef DBSYMTAB + if (options['g'] && globalflag) { + stb_string(idf->id_def, sc, idf->id_text); + } +#endif /* DBSYMTAB */ init_idf(idf); } ; @@ -295,7 +307,7 @@ formal(struct formal **fmp;) identifier(&idf) { register struct formal *new = new_formal(); - + new->fm_idf = idf; new->next = *fmp; *fmp = new; @@ -318,6 +330,13 @@ enum_specifier(register struct type **tpp;) [ {declare_struct(ENUM, idf, tpp);} enumerator_pack(*tpp, &l) + { +#ifdef DBSYMTAB + if (options['g']) { + stb_tag(idf->id_enum, idf->id_text); + } +#endif /*DBSYMTAB */ + } | {apply_struct(ENUM, idf, tpp);} empty @@ -383,6 +402,11 @@ struct_or_union_specifier(register struct type **tpp;) struct_declaration_pack(*tpp) { (idf->id_struct->tg_busy)--; +#ifdef DBSYMTAB + if (options['g']) { + stb_tag(idf->id_struct, idf->id_text); + } +#endif /*DBSYMTAB */ } | {apply_struct(fund, idf, tpp);} diff --git a/lang/cem/cemcom/domacro.c b/lang/cem/cemcom/domacro.c index 2d8e8fcda..a687f2139 100644 --- a/lang/cem/cemcom/domacro.c +++ b/lang/cem/cemcom/domacro.c @@ -15,16 +15,22 @@ #include "nopp.h" #ifndef NOPP -#include "ifdepth.h" -#include "botch_free.h" -#include "nparams.h" -#include "parbufsize.h" -#include "textsize.h" -#include "idfsize.h" +#include "ifdepth.h" +#include "botch_free.h" +#include "nparams.h" +#include "parbufsize.h" +#include "textsize.h" +#include "idfsize.h" #include "assert.h" #include #include "class.h" #include "macro.h" +#include "dbsymtab.h" +#ifdef DBSYMTAB +#include +int IncludeLevel = 0; +extern char options[]; +#endif IMPORT char **inctable; /* list of include directories */ IMPORT char *getwdir(); @@ -269,6 +275,12 @@ do_include() FileName = result; LineNumber = 0; nestlow = nestlevel; +#ifdef DBSYMTAB + IncludeLevel++; + if (options['g']) { + C_ms_std(FileName, N_BINCL, 0); + } +#endif /* DBSYMTAB */ } } } @@ -294,7 +306,7 @@ do_define() return; } /* there is a formal parameter list if the identifier is - followed immediately by a '('. + followed immediately by a '('. */ LoadChar(ch); if (ch == '(') { @@ -500,7 +512,7 @@ macro_def(id, text, nformals, length, flags) else id->id_macro = newdef = new_macro(); newdef->mc_text = text; /* replacement text */ - newdef->mc_nps = nformals; /* nr of formals */ + newdef->mc_nps = nformals; /* nr of formals */ newdef->mc_length = length; /* length of repl. text */ newdef->mc_flag = flags; /* special flags */ newdef->mc_count = 0; @@ -634,7 +646,7 @@ PRIVATE macroeq(s, t) register char *s, *t; { - + /* skip leading spaces */ while (BLANK(*s)) s++; while (BLANK(*t)) t++; diff --git a/lang/cem/cemcom/idf.c b/lang/cem/cemcom/idf.c index 7dbf1756e..36e8d3e3f 100644 --- a/lang/cem/cemcom/idf.c +++ b/lang/cem/cemcom/idf.c @@ -100,12 +100,12 @@ hash_stat() { if (options['h']) { register int i; - + print("Hash table tally:\n"); for (i = 0; i < HASHSIZE; i++) { register struct idf *notch = idf_hashtable[i]; int cnt = 0; - + while (notch) { cnt++; notch = notch->next; @@ -113,7 +113,7 @@ hash_stat() print("%d %d\n", i, cnt); } print("End hash table tally\n"); - } + } } #endif DEBUG @@ -122,7 +122,7 @@ str2idf(tg) char tg[]; { /* str2idf() returns an entry in the symbol table for the - identifier tg. If necessary, an entry is created. + identifier tg. If necessary, an entry is created. It is used where the text of the identifier is available but its hash value is not; otherwise idf_hashed() is to be used. @@ -187,7 +187,7 @@ declare_idf(ds, dc, lvl) register struct type *type; struct stack_level *stl = stack_level_of(lvl); char formal_array = 0; - + /* determine the present type */ if (ds->ds_type == 0) { /* at the L_FORMAL1 level there is no type specified yet @@ -276,7 +276,7 @@ declare_idf(ds, dc, lvl) check_hiding(idf, lvl, sc); /* of some idf by this idf */ #endif LINT - if (def && + if (def && ( def->df_level == lvl || ( lvl != L_GLOBAL && def->df_level > lvl ) ) @@ -382,7 +382,7 @@ actual_declaration(sc, tp) /* An actual_declaration needs space, right here and now. */ register int fund = tp->tp_fund; - + if (sc == ENUM || sc == TYPEDEF) /* virtual declarations */ return 0; if (fund == FUNCTION || fund == ARRAY) @@ -569,7 +569,7 @@ declare_params(dc) /* Declares the formal parameters if they exist. */ register struct formal *fm = dc->dc_formal; - + while (fm) { declare_parameter(fm->fm_idf); fm = fm->next; @@ -584,7 +584,7 @@ init_idf(idf) /* The topmost definition of idf is set to initialized. */ register struct def *def = idf->id_def; /* the topmost */ - + if (def->df_initialized) error("multiple initialization of %s", idf->id_text); if (def->df_sc == TYPEDEF) { @@ -630,7 +630,7 @@ declare_formals(fp) #endif DEBUG while (se) { register struct def *def = se->se_idf->id_def; - + def->df_address = f_offset; /* the alignment convention for parameters is: align on word boundaries, i.e. take care that the following @@ -638,11 +638,16 @@ declare_formals(fp) */ f_offset = align(f_offset + def->df_type->tp_size, (int) word_size); formal_cvt(def); /* cvt int to char or short, if necessary */ - se = se->next; def->df_level = L_FORMAL2; /* CJ */ RegisterAccount(def->df_address, def->df_type->tp_size, regtype(def->df_type), def->df_sc); +#ifdef DBSYMTAB + if (options['g']) { + stb_string(def, FORMAL, se->se_idf->id_text); + } +#endif /* DBSYMTAB */ + se = se->next; } *fp = f_offset; } @@ -721,7 +726,7 @@ init_hmask() described in Knuth, vol 2. */ register int h, rnd = HASH_X; - + for (h = 0; h < IDFSIZE; h++) { hmask[h] = rnd; rnd = (HASH_A * rnd + HASH_C) & HASHMASK; diff --git a/lang/cem/cemcom/input.c b/lang/cem/cemcom/input.c index fb4bb6955..b28ffb863 100644 --- a/lang/cem/cemcom/input.c +++ b/lang/cem/cemcom/input.c @@ -14,7 +14,13 @@ extern int nestlevel; #include "nopp.h" #include +#include "dbsymtab.h" #ifndef NOPP +#ifdef DBSYMTAB +#include +extern int IncludeLevel; +extern char options[]; +#endif char * getwdir(fn) register char *fn; @@ -64,6 +70,12 @@ AtEoIF() if (NoUnstack) lexerror("unexpected EOF"); #ifndef NOPP nestlevel = nestlow; +#ifdef DBSYMTAB + if (options['g'] && IncludeLevel > 0) { + C_ms_std(FileName, N_EINCL, 0); + } + IncludeLevel--; +#endif #endif return 0; } diff --git a/lang/cem/cemcom/main.c b/lang/cem/cemcom/main.c index 95f3009e5..92711b921 100644 --- a/lang/cem/cemcom/main.c +++ b/lang/cem/cemcom/main.c @@ -265,6 +265,10 @@ compile(argc, argv) init(); LineNumber = 0; nestlow = -1; +#ifndef LINT + init_code(destination && strcmp(destination, "-") != 0 ? + destination : 0); +#endif #ifndef NOPP WorkingDir = getwdir(source); #endif NOPP @@ -279,8 +283,6 @@ compile(argc, argv) #endif DEBUG { #ifndef LINT - init_code(destination && strcmp(destination, "-") != 0 ? - destination : 0); /* compile the source text */ C_program(); #ifdef PREPEND_SCOPES diff --git a/lang/cem/cemcom/options b/lang/cem/cemcom/options index 0dd646879..a63b89a3d 100644 --- a/lang/cem/cemcom/options +++ b/lang/cem/cemcom/options @@ -4,12 +4,13 @@ C while running preprocessor, copy comment d perform a small dataflow analysis D see identifier following as a macro E run preprocessor only +g produce symbol table for debugger i suppress /usr/include include files in dependency list I expand include table with directory name following +L don't generate linenumbers and filename indications m generate file.o: file1.h format dependency lines M set identifier length n don't generate register messages -L don't generate linenumbers and filename indications p trace P in running the preprocessor do not output '# line' lines R restricted C diff --git a/lang/cem/cemcom/options.c b/lang/cem/cemcom/options.c index 346c88dfb..9bdd6e5d5 100644 --- a/lang/cem/cemcom/options.c +++ b/lang/cem/cemcom/options.c @@ -3,7 +3,7 @@ * See the copyright notice in the ACK home directory, in the file "Copyright". */ /* $Header$ */ -/* U S E R O P T I O N - H A N D L I N G */ +/* U S E R O P T I O N - H A N D L I N G */ #include "lint.h" #include "botch_free.h" @@ -21,6 +21,7 @@ #include "use_tmp.h" #include "dataflow.h" #include "noRoption.h" +#include "dbsymtab.h" #ifndef NOPP extern char **inctable; @@ -91,7 +92,7 @@ next_option: /* to allow combined one-char options */ #ifndef LINT #ifndef NOPP - case 'A' : /* Amake dependency generation */ + case 'A' : /* Amake dependency generation */ do_dependencies = 1; if (*text) { dep_file = text; @@ -103,7 +104,13 @@ next_option: /* to allow combined one-char options */ break; #endif NOPP #endif LINT - +#ifdef DBSYMTAB + case 'g': /* symbol table for debugger */ + options['g'] = 1; + options['n'] = 1; + break; +#endif /* DBSYMTAB */ + case 'R': /* strict version */ #ifndef NOROPTION options[opt] = 1; @@ -176,15 +183,15 @@ deleted, is now a debug-flag if (*text) { int i; register char *new = text; - + if (++inc_total > inc_max) { inctable = (char **) Realloc(inctable,(inc_max+=10)*sizeof(char *)); } - + for (i = inc_pos++; i < inc_total; i++) { char *tmp = inctable[i]; - + inctable[i] = new; new = tmp; } @@ -235,7 +242,7 @@ deleted, is now a debug-flag #endif USE_TMP break; } - + case 'U' : { /* -Uname : undefine predefined */ #ifndef NOPP register struct idf *idef; @@ -353,7 +360,7 @@ txt2int(tp) *tp; the resulting value is yielded. */ register int val = 0, ch; - + while (ch = **tp, ch >= '0' && ch <= '9') { val = val * 10 + ch - '0'; (*tp)++; diff --git a/lang/cem/cemcom/proto.make b/lang/cem/cemcom/proto.make index 340f4a054..9414d6d60 100644 --- a/lang/cem/cemcom/proto.make +++ b/lang/cem/cemcom/proto.make @@ -61,7 +61,9 @@ SRC_C = \ $(SRC_DIR)/switch.c \ $(SRC_DIR)/tokenname.c \ $(SRC_DIR)/type.c \ - $(SRC_DIR)/util.c + $(SRC_DIR)/util.c \ + $(SRC_DIR)/stab.c + GEN_C = tokenfile.c program.c declar.c expression.c statement.c ival.c \ symbol2str.c char.c Lpars.c next.c CFILES= $(SRC_C) $(GEN_C) @@ -96,7 +98,7 @@ GEN_H = botch_free.h dataflow.h debug.h density.h errout.h \ regcount.h \ code.h declar.h decspecs.h def.h expr.h field.h estack.h util.h \ idf.h macro.h stmt.h struct.h switch.h type.h l_brace.h l_state.h \ - l_outdef.h stack.h lapbuf.h noRoption.h nofloat.h + l_outdef.h stack.h lapbuf.h noRoption.h nofloat.h dbsymtab.h HFILES= $(GEN_H) $(SRC_H) diff --git a/lang/cem/cemcom/statement.g b/lang/cem/cemcom/statement.g index 5016f855c..cee729b39 100644 --- a/lang/cem/cemcom/statement.g +++ b/lang/cem/cemcom/statement.g @@ -16,6 +16,7 @@ #include "debug.h" #include "botch_free.h" +#include "dbsymtab.h" #include "arith.h" #include "LLlex.h" @@ -26,8 +27,12 @@ #include "code.h" #include "stack.h" #include "def.h" +#ifdef DBSYMTAB +#include +#endif /* DBSYMTAB */ extern int level; +extern char options[]; } /* Each statement construction is stacked in order to trace a ??? @@ -451,7 +456,14 @@ jump } ; -compound_statement: +compound_statement + { +#ifdef DBSYMTAB + static int brc_level = 1; + int decl_seen = brc_level == 1; +#endif /* DBSYMTAB */ + } +: '{' { stack_level(); @@ -460,13 +472,32 @@ compound_statement: (DOT == IDENTIFIER && AHEAD == IDENTIFIER)) /* >>> conflict on TYPE_IDENTIFIER, IDENTIFIER */ declaration + { +#ifdef DBSYMTAB + decl_seen++; +#endif /* DBSYMTAB */ + } ]* + { +#ifdef DBSYMTAB + ++brc_level; + if (options['g'] && decl_seen) { + C_ms_std((char *) 0, N_LBRAC, brc_level); + } +#endif /* DBSYMTAB */ + } [%persistent statement ]* '}' { unstack_level(); +#ifdef DBSYMTAB + if (options['g'] && decl_seen) { + C_ms_std((char *) 0, N_RBRAC, brc_level); + } + brc_level--; +#endif /* DBSYMTAB */ } ; diff --git a/lang/cem/cemcom/type.str b/lang/cem/cemcom/type.str index 9904216e6..587fc8d30 100644 --- a/lang/cem/cemcom/type.str +++ b/lang/cem/cemcom/type.str @@ -7,6 +7,7 @@ #include "nofloat.h" #include "nobitfield.h" +#include "dbsymtab.h" struct type { struct type *next; /* used only with ARRAY */ @@ -22,6 +23,9 @@ struct type { struct type *tp_pointer;/* to POINTER */ struct type *tp_array; /* to ARRAY */ struct type *tp_function;/* to FUNCTION */ +#ifdef DBSYMTAB + int tp_dbindex; +#endif }; extern struct type