sourcepawn/compiler/sc1.c
author David Anderson <dvander@alliedmods.net>
Fri Jun 08 03:50:23 2007 +0000 (2007-06-08)
changeset 925 2cd3d817b5ab
parent 876 a6095922d169
child 1044 ff0047e53c65
permissions -rw-r--r--
added sm_unban
     1 /*  Pawn compiler
     2  *
     3  *  Function and variable definition and declaration, statement parser.
     4  *
     5  *  Copyright (c) ITB CompuPhase, 1997-2006
     6  *
     7  *  This software is provided "as-is", without any express or implied warranty.
     8  *  In no event will the authors be held liable for any damages arising from
     9  *  the use of this software.
    10  *
    11  *  Permission is granted to anyone to use this software for any purpose,
    12  *  including commercial applications, and to alter it and redistribute it
    13  *  freely, subject to the following restrictions:
    14  *
    15  *  1.  The origin of this software must not be misrepresented; you must not
    16  *      claim that you wrote the original software. If you use this software in
    17  *      a product, an acknowledgment in the product documentation would be
    18  *      appreciated but is not required.
    19  *  2.  Altered source versions must be plainly marked as such, and must not be
    20  *      misrepresented as being the original software.
    21  *  3.  This notice may not be removed or altered from any source distribution.
    22  *
    23  *  Version: $Id$
    24  */
    25 #include <assert.h>
    26 #include <ctype.h>
    27 #include <limits.h>
    28 #include <stdarg.h>
    29 #include <stdio.h>
    30 #include <stdlib.h>
    31 #include <string.h>
    32 
    33 #if defined	__WIN32__ || defined _WIN32 || defined __MSDOS__
    34   #include <conio.h>
    35   #include <io.h>
    36 #endif
    37 
    38 #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
    39   #include <sclinux.h>
    40   #include <binreloc.h> /* from BinReloc, see www.autopackage.org */
    41 #endif
    42 
    43 #if defined FORTIFY
    44   #include <alloc/fortify.h>
    45 #endif
    46 
    47 #if defined __BORLANDC__ || defined __WATCOMC__
    48   #include <dos.h>
    49   static unsigned total_drives; /* dummy variable */
    50   #define dos_setdrive(i)       _dos_setdrive(i,&total_drives)
    51 #elif defined _MSC_VER && defined _WIN32
    52   #include <direct.h>           /* for _chdrive() */
    53   #define dos_setdrive(i)       _chdrive(i)
    54 #endif
    55 #if defined __BORLANDC__
    56   #include <dir.h>              /* for chdir() */
    57 #elif defined __WATCOMC__
    58   #include <direct.h>           /* for chdir() */
    59 #endif
    60 #if defined __WIN32__ || defined _WIN32 || defined _Windows
    61   #include <windows.h>
    62 #endif
    63 
    64 #include <time.h>
    65 
    66 #include "lstring.h"
    67 #include "sc.h"
    68 #include "svn_version.h"
    69 #include "sctracker.h"
    70 #define VERSION_STR "3.2.3636"
    71 #define VERSION_INT 0x0302
    72 
    73 int pc_anytag = 0;
    74 int pc_functag = 0;
    75 int pc_tag_string = 0;
    76 
    77 static void resetglobals(void);
    78 static void initglobals(void);
    79 static char *get_extension(char *filename);
    80 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
    81                    char *rname,char *codepage);
    82 static void setconfig(char *root);
    83 static void setcaption(void);
    84 static void about(void);
    85 static void setconstants(void);
    86 static void parse(void);
    87 static void dumplits(void);
    88 static void dumpzero(int count);
    89 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst);
    90 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
    91                     int stock,int fconst);
    92 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
    93 static int declloc(int fstatic);
    94 static void decl_const(int table);
    95 static void declstruct();
    96 static void decl_enum(int table);
    97 static cell needsub(int *tag,constvalue **enumroot);
    98 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
    99                      constvalue *enumroot);
   100 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
   101                       int startlit,int counteddim[],constvalue *lastdim,
   102                       constvalue *enumroot,int *errorfound);
   103 static cell initvector(int ident,int tag,cell size,int fillzero,
   104                        constvalue *enumroot,int *errorfound);
   105 static cell init(int ident,int *tag,int *errorfound);
   106 static int getstates(const char *funcname);
   107 static void attachstatelist(symbol *sym, int state_id);
   108 static void funcstub(int fnative);
   109 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
   110 static int declargs(symbol *sym,int chkshadow);
   111 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
   112                   int fpublic,int fconst,int chkshadow,arginfo *arg);
   113 static void make_report(symbol *root,FILE *log,char *sourcefile);
   114 static void reduce_referrers(symbol *root);
   115 static long max_stacksize(symbol *root,int *recursion);
   116 static int testsymbols(symbol *root,int level,int testlabs,int testconst);
   117 static void destructsymbols(symbol *root,int level);
   118 static constvalue *find_constval_byval(constvalue *table,cell val);
   119 static symbol *fetchlab(char *name);
   120 static void statement(int *lastindent,int allow_decl);
   121 static void compound(int stmt_sameline,int starttok);
   122 static int test(int label,int parens,int invert);
   123 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
   124                   int *tag,symbol **symptr,int chkfuncresult);
   125 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
   126 				  int *tag,symbol **symptr,int chkfuncresult,value *lval);
   127 static void doassert(void);
   128 static void doexit(void);
   129 static int doif(void);
   130 static int dowhile(void);
   131 static int dodo(void);
   132 static int dofor(void);
   133 static void doswitch(void);
   134 static void dogoto(void);
   135 static void dolabel(void);
   136 static void doreturn(void);
   137 static void dofuncenum(int listmode);
   138 static void dobreak(void);
   139 static void docont(void);
   140 static void dosleep(void);
   141 static void dostate(void);
   142 static void addwhile(int *ptr);
   143 static void delwhile(void);
   144 static int *readwhile(void);
   145 static void inst_datetime_defines(void);
   146 
   147 enum {
   148   TEST_PLAIN,           /* no parentheses */
   149   TEST_THEN,            /* '(' <expr> ')' or <expr> 'then' */
   150   TEST_DO,              /* '(' <expr> ')' or <expr> 'do' */
   151   TEST_OPT,             /* '(' <expr> ')' or <expr> */
   152 };
   153 static int norun      = 0;      /* the compiler never ran */
   154 static int autozero   = 1;      /* if 1 will zero out the variable, if 0 omit the zeroing */
   155 static int lastst     = 0;      /* last executed statement type */
   156 static int nestlevel  = 0;      /* number of active (open) compound statements */
   157 static int endlessloop= 0;      /* nesting level of endless loop */
   158 static int rettype    = 0;      /* the type that a "return" expression should have */
   159 static int skipinput  = 0;      /* number of lines to skip from the first input file */
   160 static int optproccall = TRUE;  /* support "procedure call" */
   161 static int verbosity  = 1;      /* verbosity level, 0=quiet, 1=normal, 2=verbose */
   162 static int sc_reparse = 0;      /* needs 3th parse because of changed prototypes? */
   163 static int sc_parsenum = 0;     /* number of the extra parses */
   164 static int wq[wqTABSZ];         /* "while queue", internal stack for nested loops */
   165 static int *wqptr;              /* pointer to next entry */
   166 #if !defined SC_LIGHT
   167   static char sc_rootpath[_MAX_PATH];
   168   static char *sc_documentation=NULL;/* main documentation */
   169 #endif
   170 #if defined	__WIN32__ || defined _WIN32 || defined _Windows
   171   static HWND hwndFinish = 0;
   172 #endif
   173 
   174 SC_VDECL int glbstringread = 0;
   175 char g_tmpfile[_MAX_PATH] = {0};
   176 
   177 /*  "main" of the compiler
   178  */
   179 #if defined __cplusplus
   180   extern "C"
   181 #endif
   182 int pc_compile(int argc, char *argv[])
   183 {
   184   int entry,i,jmpcode;
   185   int retcode;
   186   char incfname[_MAX_PATH];
   187   char reportname[_MAX_PATH];
   188   char codepage[MAXCODEPAGE+1];
   189   FILE *binf;
   190   void *inpfmark;
   191   int lcl_packstr,lcl_needsemicolon,lcl_tabsize;
   192   #if !defined SC_LIGHT
   193     int hdrsize=0;
   194   #endif
   195   char *ptr;
   196 
   197   /* set global variables to their initial value */
   198   binf=NULL;
   199   initglobals();
   200   errorset(sRESET,0);
   201   errorset(sEXPRRELEASE,0);
   202   lexinit();
   203 
   204   /* make sure that we clean up on a fatal error; do this before the first
   205    * call to error(). */
   206   if ((jmpcode=setjmp(errbuf))!=0)
   207     goto cleanup;
   208 
   209   /* allocate memory for fixed tables */
   210   inpfname=(char*)malloc(_MAX_PATH);
   211   if (inpfname==NULL)
   212     error(123);         /* insufficient memory */
   213   litq=(cell*)malloc(litmax*sizeof(cell));
   214   if (litq==NULL)
   215     error(123);         /* insufficient memory */
   216   if (!phopt_init())
   217     error(123);         /* insufficient memory */
   218 
   219   setopt(argc,argv,outfname,errfname,incfname,reportname,codepage);
   220   strcpy(binfname,outfname);
   221   ptr=get_extension(binfname);
   222   if (ptr!=NULL && stricmp(ptr,".asm")==0)
   223     set_extension(binfname,".smx",TRUE);
   224   else
   225     set_extension(binfname,".smx",FALSE);
   226   /* set output names that depend on the input name */
   227   if (sc_listing)
   228     set_extension(outfname,".lst",TRUE);
   229   else
   230     set_extension(outfname,".asm",TRUE);
   231   if (strlen(errfname)!=0)
   232     remove(errfname);   /* delete file on startup */
   233   else if (verbosity>0)
   234     setcaption();
   235   setconfig(argv[0]);   /* the path to the include and codepage files */
   236   sc_ctrlchar_org=sc_ctrlchar;
   237   lcl_packstr=sc_packstr;
   238   lcl_needsemicolon=sc_needsemicolon;
   239   lcl_tabsize=sc_tabsize;
   240   #if !defined NO_CODEPAGE
   241     if (!cp_set(codepage))      /* set codepage */
   242       error(128);               /* codepage mapping file not found */
   243   #endif
   244   /* optionally create a temporary input file that is a collection of all
   245    * input files
   246    */
   247   assert(get_sourcefile(0)!=NULL);  /* there must be at least one source file */
   248   if (get_sourcefile(1)!=NULL) {
   249     /* there are at least two or more source files */
   250     char *tname,*sname;
   251     FILE *ftmp,*fsrc;
   252     int fidx;
   253     #if defined	__WIN32__ || defined _WIN32
   254       tname=_tempnam(NULL,"pawn");
   255     #elif defined __MSDOS__ || defined _Windows
   256       tname=tempnam(NULL,"pawn");
   257     #elif defined(MACOS) && !defined(__MACH__)
   258       /* tempnam is not supported for the Macintosh CFM build. */
   259       error(124,get_sourcefile(1));
   260       tname=NULL;
   261       sname=NULL;
   262     #else
   263       tname=tempnam(NULL,"pawn");
   264     #endif
   265     ftmp=(FILE*)pc_createsrc(tname);
   266     for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) {
   267       unsigned char tstring[128];
   268       fsrc=(FILE*)pc_opensrc(sname);
   269       if (fsrc==NULL) {
   270         pc_closesrc(ftmp);
   271         remove(tname);
   272         strcpy(inpfname,sname); /* avoid invalid filename */
   273         error(120,sname);
   274       } /* if */
   275       pc_writesrc(ftmp,(unsigned char*)"#file \"");
   276       pc_writesrc(ftmp,(unsigned char*)sname);
   277       pc_writesrc(ftmp,(unsigned char*)"\"\n");
   278       while (!pc_eofsrc(fsrc) && pc_readsrc(fsrc,tstring,sizeof tstring)) {
   279         pc_writesrc(ftmp,tstring);
   280       } /* while */
   281       pc_closesrc(fsrc);
   282     } /* for */
   283     pc_closesrc(ftmp);
   284     strcpy(inpfname,tname);
   285 	strcpy(g_tmpfile,tname);
   286     free(tname);
   287   } else {
   288     strcpy(inpfname,get_sourcefile(0));
   289   } /* if */
   290   inpf_org=(FILE*)pc_opensrc(inpfname);
   291   if (inpf_org==NULL)
   292     error(120,inpfname);
   293   freading=TRUE;
   294   outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */
   295   if (outf==NULL)
   296     error(121,outfname);
   297   /* immediately open the binary file, for other programs to check */
   298   if (sc_asmfile || sc_listing) {
   299     binf=NULL;
   300   } else {
   301     binf=(FILE*)pc_openbin(binfname);
   302     if (binf==NULL)
   303       error(121,binfname);
   304   } /* if */
   305   setconstants();               /* set predefined constants and tagnames */
   306   for (i=0; i<skipinput; i++)   /* skip lines in the input file */
   307     if (pc_readsrc(inpf_org,pline,sLINEMAX)!=NULL)
   308       fline++;                  /* keep line number up to date */
   309   skipinput=fline;
   310   sc_status=statFIRST;
   311   /* write starting options (from the command line or the configuration file) */
   312   if (sc_listing) {
   313     char string[150];
   314     sprintf(string,"#pragma ctrlchar 0x%02x\n"
   315                    "#pragma pack %s\n"
   316                    "#pragma semicolon %s\n"
   317                    "#pragma tabsize %d\n",
   318             sc_ctrlchar,
   319             sc_packstr ? "true" : "false",
   320             sc_needsemicolon ? "true" : "false",
   321             sc_tabsize);
   322     pc_writeasm(outf,string);
   323     setfiledirect(inpfname);
   324   } /* if */
   325   /* do the first pass through the file (or possibly two or more "first passes") */
   326   sc_parsenum=0;
   327   inpfmark=pc_getpossrc(inpf_org);
   328   do {
   329     /* reset "defined" flag of all functions and global variables */
   330     reduce_referrers(&glbtab);
   331     delete_symbols(&glbtab,0,TRUE,FALSE);
   332     #if !defined NO_DEFINE
   333       delete_substtable();
   334       inst_datetime_defines();
   335     #endif
   336     resetglobals();
   337     pstructs_free();
   338 	funcenums_free();
   339     sc_ctrlchar=sc_ctrlchar_org;
   340     sc_packstr=lcl_packstr;
   341     sc_needsemicolon=lcl_needsemicolon;
   342     sc_tabsize=lcl_tabsize;
   343     errorset(sRESET,0);
   344     /* reset the source file */
   345     inpf=inpf_org;
   346     freading=TRUE;
   347     pc_resetsrc(inpf,inpfmark); /* reset file position */
   348     fline=skipinput;            /* reset line number */
   349     sc_reparse=FALSE;           /* assume no extra passes */
   350     sc_status=statFIRST;        /* resetglobals() resets it to IDLE */
   351 
   352     if (strlen(incfname)>0) {
   353       if (strcmp(incfname,sDEF_PREFIX)==0) {
   354         plungefile(incfname,FALSE,TRUE);    /* parse "default.inc" */
   355       } else {
   356         if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */
   357           error(120,incfname);          /* cannot read from ... (fatal error) */
   358       } /* if */
   359     } /* if */
   360     preprocess();                       /* fetch first line */
   361     parse();                            /* process all input */
   362     sc_parsenum++;
   363   } while (sc_reparse);
   364 
   365   /* second (or third) pass */
   366   sc_status=statWRITE;                  /* set, to enable warnings */
   367   state_conflict(&glbtab);
   368 
   369   /* write a report, if requested */
   370   #if !defined SC_LIGHT
   371     if (sc_makereport) {
   372       FILE *frep=stdout;
   373       if (strlen(reportname)>0)
   374         frep=fopen(reportname,"wb");    /* avoid translation of \n to \r\n in DOS/Windows */
   375       if (frep!=NULL) {
   376         make_report(&glbtab,frep,get_sourcefile(0));
   377         if (strlen(reportname)>0)
   378           fclose(frep);
   379       } /* if */
   380       if (sc_documentation!=NULL) {
   381         free(sc_documentation);
   382         sc_documentation=NULL;
   383       } /* if */
   384     } /* if */
   385   #endif
   386   if (sc_listing)
   387     goto cleanup;
   388 
   389   /* ??? for re-parsing the listing file instead of the original source
   390    * file (and doing preprocessing twice):
   391    * - close input file, close listing file
   392    * - re-open listing file for reading (inpf)
   393    * - open assembler file (outf)
   394    */
   395 
   396   /* reset "defined" flag of all functions and global variables */
   397   reduce_referrers(&glbtab);
   398   delete_symbols(&glbtab,0,TRUE,FALSE);
   399   funcenums_free();
   400   pstructs_free();
   401   #if !defined NO_DEFINE
   402     delete_substtable();
   403     inst_datetime_defines();
   404   #endif
   405   resetglobals();
   406   sc_ctrlchar=sc_ctrlchar_org;
   407   sc_packstr=lcl_packstr;
   408   sc_needsemicolon=lcl_needsemicolon;
   409   sc_tabsize=lcl_tabsize;
   410   errorset(sRESET,0);
   411   /* reset the source file */
   412   inpf=inpf_org;
   413   freading=TRUE;
   414   pc_resetsrc(inpf,inpfmark);   /* reset file position */
   415   fline=skipinput;              /* reset line number */
   416   lexinit();                    /* clear internal flags of lex() */
   417   sc_status=statWRITE;          /* allow to write --this variable was reset by resetglobals() */
   418   writeleader(&glbtab);
   419   insert_dbgfile(inpfname);
   420   if (strlen(incfname)>0) {
   421     if (strcmp(incfname,sDEF_PREFIX)==0)
   422       plungefile(incfname,FALSE,TRUE);  /* parse "default.inc" (again) */
   423     else
   424       plungequalifiedfile(incfname);    /* parse implicit include file (again) */
   425   } /* if */
   426   preprocess();                         /* fetch first line */
   427   parse();                              /* process all input */
   428   /* inpf is already closed when readline() attempts to pop of a file */
   429   writetrailer();                       /* write remaining stuff */
   430 
   431   entry=testsymbols(&glbtab,0,TRUE,FALSE);  /* test for unused or undefined
   432                                              * functions and variables */
   433   if (!entry)
   434     error(13);                  /* no entry point (no public functions) */
   435 
   436 cleanup:
   437   if (inpf!=NULL)               /* main source file is not closed, do it now */
   438     pc_closesrc(inpf);
   439   /* write the binary file (the file is already open) */
   440   if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) {
   441     assert(binf!=NULL);
   442     pc_resetasm(outf);          /* flush and loop back, for reading */
   443     #if !defined SC_LIGHT
   444       hdrsize=
   445     #endif
   446     assemble(binf,outf);        /* assembler file is now input */
   447   } /* if */
   448   if (outf!=NULL) {
   449     pc_closeasm(outf,!(sc_asmfile || sc_listing));
   450     outf=NULL;
   451   } /* if */
   452   if (binf!=NULL) {
   453     pc_closebin(binf,errnum!=0);
   454     binf=NULL;
   455   } /* if */
   456 
   457   #if !defined SC_LIGHT
   458     if (errnum==0 && strlen(errfname)==0) {
   459       int recursion;
   460       long stacksize=max_stacksize(&glbtab,&recursion);
   461       int flag_exceed=0;
   462       if (pc_amxlimit>0) {
   463         long totalsize=hdrsize+code_idx;
   464         if (pc_amxram==0)
   465           totalsize+=(glb_declared+pc_stksize)*sizeof(cell);
   466         if (totalsize>=pc_amxlimit)
   467           flag_exceed=1;
   468       } /* if */
   469       if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram)
   470         flag_exceed=1;
   471       if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) {
   472         pc_printf("Header size:       %8ld bytes\n", (long)hdrsize);
   473         pc_printf("Code size:         %8ld bytes\n", (long)code_idx);
   474         pc_printf("Data size:         %8ld bytes\n", (long)glb_declared*sizeof(cell));
   475         pc_printf("Stack/heap size:   %8ld bytes; ", (long)pc_stksize*sizeof(cell));
   476         pc_printf("estimated max. usage");
   477         if (recursion)
   478           pc_printf(": unknown, due to recursion\n");
   479         else if ((pc_memflags & suSLEEP_INSTR)!=0)
   480           pc_printf(": unknown, due to the \"sleep\" instruction\n");
   481         else
   482           pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell));
   483         pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell));
   484       } /* if */
   485       if (flag_exceed)
   486         error(126,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */
   487     } /* if */
   488   #endif
   489 
   490   if (g_tmpfile[0] != '\0') {
   491     remove(g_tmpfile);
   492   }
   493   if (inpfname!=NULL) {
   494     free(inpfname);
   495   } /* if */
   496   if (litq!=NULL)
   497     free(litq);
   498   phopt_cleanup();
   499   stgbuffer_cleanup();
   500   clearstk();
   501   assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols
   502                                            * should already have been deleted */
   503   delete_symbols(&loctab,0,TRUE,TRUE);    /* delete local variables if not yet
   504                                            * done (i.e. on a fatal error) */
   505   delete_symbols(&glbtab,0,TRUE,TRUE);
   506   delete_consttable(&tagname_tab);
   507   delete_consttable(&libname_tab);
   508   delete_consttable(&sc_automaton_tab);
   509   delete_consttable(&sc_state_tab);
   510   state_deletetable();
   511   delete_aliastable();
   512   delete_pathtable();
   513   delete_sourcefiletable();
   514   delete_dbgstringtable();
   515   funcenums_free();
   516   pstructs_free();
   517   #if !defined NO_DEFINE
   518     delete_substtable();
   519   #endif
   520   #if !defined SC_LIGHT
   521     delete_docstringtable();
   522     if (sc_documentation!=NULL)
   523       free(sc_documentation);
   524   #endif
   525   delete_autolisttable();
   526   if (errnum!=0) {
   527     if (strlen(errfname)==0)
   528       pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : "");
   529     retcode=1;
   530   } else if (warnnum!=0){
   531     if (strlen(errfname)==0)
   532       pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : "");
   533     retcode=0;          /* use "0", so that MAKE and similar tools continue */
   534   } else {
   535     retcode=jmpcode;
   536     if (retcode==0 && verbosity>=2)
   537       pc_printf("\nDone.\n");
   538   } /* if */
   539   #if defined	__WIN32__ || defined _WIN32 || defined _Windows
   540     if (IsWindow(hwndFinish))
   541       PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L);
   542   #endif
   543   #if defined FORTIFY
   544     Fortify_ListAllMemory();
   545   #endif
   546   return retcode;
   547 }
   548 
   549 #if defined __cplusplus
   550   extern "C"
   551 #endif
   552 int pc_addconstant(char *name,cell value,int tag)
   553 {
   554   errorset(sFORCESET,0);        /* make sure error engine is silenced */
   555   sc_status=statIDLE;
   556   add_constant(name,value,sGLOBAL,tag);
   557   return 1;
   558 }
   559 
   560 static void inst_datetime_defines(void)
   561 {
   562   char date[64];
   563   char ltime[64];
   564   time_t td;
   565   struct tm *curtime;
   566 
   567   time(&td);
   568   curtime = localtime(&td);
   569   strftime(date, 31, "\"%m/%d/%Y\"", curtime);
   570   strftime(ltime, 31, "\"%H:%M:%S\"", curtime);
   571 
   572   insert_subst("__DATE__", date, 8);
   573   insert_subst("__TIME__", ltime, 8);
   574 }
   575 
   576 #if defined __cplusplus
   577   extern "C"
   578 #endif
   579 int pc_addtag(char *name)
   580 {
   581   cell val;
   582   constvalue *ptr;
   583   int last,tag;
   584 
   585   if (name==NULL) {
   586     /* no tagname was given, check for one */
   587     if (lex(&val,&name)!=tLABEL) {
   588       lexpush();
   589       return 0;         /* untagged */
   590     } /* if */
   591   } /* if */
   592 
   593   assert(strchr(name,':')==NULL); /* colon should already have been stripped */
   594   last=0;
   595   ptr=tagname_tab.next;
   596   while (ptr!=NULL) {
   597     tag=(int)(ptr->value & TAGMASK);
   598     if (strcmp(name,ptr->name)==0)
   599       return tag;       /* tagname is known, return its sequence number */
   600     tag &= (int)~FIXEDTAG;
   601     tag &= (int)~FUNCTAG;
   602     if (tag>last)
   603       last=tag;
   604     ptr=ptr->next;
   605   } /* while */
   606 
   607   /* tagname currently unknown, add it */
   608   tag=last+1;           /* guaranteed not to exist already */
   609   if (isupper(*name))
   610     tag |= (int)FIXEDTAG;
   611   append_constval(&tagname_tab,name,(cell)tag,0);
   612   return tag;
   613 }
   614 
   615 int pc_addfunctag(char *name)
   616 {
   617   cell val;
   618   constvalue *ptr;
   619   int last,tag;
   620 
   621   if (name==NULL) {
   622     /* no tagname was given, check for one */
   623     if (lex(&val,&name)!=tLABEL) {
   624       lexpush();
   625       return 0;         /* untagged */
   626     } /* if */
   627   } /* if */
   628 
   629   assert(strchr(name,':')==NULL); /* colon should already have been stripped */
   630   last=0;
   631   ptr=tagname_tab.next;
   632   while (ptr!=NULL) {
   633     tag=(int)(ptr->value & TAGMASK);
   634     if (strcmp(name,ptr->name)==0)
   635       return tag;       /* tagname is known, return its sequence number */
   636     tag &= (int)~FIXEDTAG;
   637     tag &= (int)~FUNCTAG;
   638     if (tag>last)
   639       last=tag;
   640     ptr=ptr->next;
   641   } /* while */
   642 
   643   /* tagname currently unknown, add it */
   644   tag=last+1;           /* guaranteed not to exist already */
   645   if (isupper(*name))
   646     tag |= (int)FIXEDTAG;
   647   tag |= (int)FUNCTAG;
   648   append_constval(&tagname_tab,name,(cell)tag,0);
   649   return tag;
   650 }
   651 
   652 static void resetglobals(void)
   653 {
   654   /* reset the subset of global variables that is modified by the first pass */
   655   curfunc=NULL;         /* pointer to current function */
   656   lastst=0;             /* last executed statement type */
   657   nestlevel=0;          /* number of active (open) compound statements */
   658   rettype=0;            /* the type that a "return" expression should have */
   659   litidx=0;             /* index to literal table */
   660   stgidx=0;             /* index to the staging buffer */
   661   sc_labnum=0;          /* top value of (internal) labels */
   662   staging=0;            /* true if staging output */
   663   declared=0;           /* number of local cells declared */
   664   glb_declared=0;       /* number of global cells declared */
   665   code_idx=0;           /* number of bytes with generated code */
   666   ntv_funcid=0;         /* incremental number of native function */
   667   curseg=0;             /* 1 if currently parsing CODE, 2 if parsing DATA */
   668   freading=FALSE;       /* no input file ready yet */
   669   fline=0;              /* the line number in the current file */
   670   fnumber=0;            /* the file number in the file table (debugging) */
   671   fcurrent=0;           /* current file being processed (debugging) */
   672   sc_intest=FALSE;      /* true if inside a test */
   673   sideeffect=0;         /* true if an expression causes a side-effect */
   674   stmtindent=0;         /* current indent of the statement */
   675   indent_nowarn=FALSE;  /* do not skip warning "217 loose indentation" */
   676   sc_allowtags=TRUE;    /* allow/detect tagnames */
   677   sc_status=statIDLE;
   678   sc_allowproccall=FALSE;
   679   pc_addlibtable=TRUE;  /* by default, add a "library table" to the output file */
   680   sc_alignnext=FALSE;
   681   pc_docexpr=FALSE;
   682   pc_deprecate=NULL;
   683   sc_curstates=0;
   684   pc_memflags=0;
   685 }
   686 
   687 static void initglobals(void)
   688 {
   689   resetglobals();
   690 
   691   sc_asmfile=FALSE;     /* do not create .ASM file */
   692   sc_listing=FALSE;     /* do not create .LST file */
   693   skipinput=0;          /* number of lines to skip from the first input file */
   694   sc_ctrlchar=CTRL_CHAR;/* the escape character */
   695   litmax=sDEF_LITMAX;   /* current size of the literal table */
   696   errnum=0;             /* number of errors */
   697   warnnum=0;            /* number of warnings */
   698   optproccall=FALSE;    /* sourcemod: do not support "procedure call" */
   699   verbosity=1;          /* verbosity level, no copyright banner */
   700   sc_debug=sCHKBOUNDS|sSYMBOLIC;   /* sourcemod: full debug stuff */
   701   pc_optimize=sOPTIMIZE_DEFAULT;   /* sourcemod: full optimization */
   702   sc_packstr=TRUE;     /* strings are packed by default */
   703   sc_compress=FALSE;	/* always disable compact encoding! */
   704   sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */
   705   sc_dataalign=sizeof(cell);
   706   pc_stksize=sDEF_AMXSTACK;/* default stack size */
   707   pc_amxlimit=0;        /* no limit on size of the abstract machine */
   708   pc_amxram=0;          /* no limit on data size of the abstract machine */
   709   sc_tabsize=8;         /* assume a TAB is 8 spaces */
   710   sc_rationaltag=0;     /* assume no support for rational numbers */
   711   rational_digits=0;    /* number of fractional digits */
   712 
   713   outfname[0]='\0';     /* output file name */
   714   errfname[0]='\0';     /* error file name */
   715   inpf=NULL;            /* file read from */
   716   inpfname=NULL;        /* pointer to name of the file currently read from */
   717   outf=NULL;            /* file written to */
   718   litq=NULL;            /* the literal queue */
   719   glbtab.next=NULL;     /* clear global variables/constants table */
   720   loctab.next=NULL;     /*   "   local      "    /    "       "   */
   721   tagname_tab.next=NULL;/* tagname table */
   722   libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */
   723 
   724   pline[0]='\0';        /* the line read from the input file */
   725   lptr=NULL;            /* points to the current position in "pline" */
   726   curlibrary=NULL;      /* current library */
   727   inpf_org=NULL;        /* main source file */
   728 
   729   wqptr=wq;             /* initialize while queue pointer */
   730 
   731 #if !defined SC_LIGHT
   732   sc_documentation=NULL;
   733   sc_makereport=FALSE;  /* do not generate a cross-reference report */
   734 #endif
   735 }
   736 
   737 static char *get_extension(char *filename)
   738 {
   739   char *ptr;
   740 
   741   assert(filename!=NULL);
   742   ptr=strrchr(filename,'.');
   743   if (ptr!=NULL) {
   744     /* ignore extension on a directory or at the start of the filename */
   745     if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR)
   746       ptr=NULL;
   747   } /* if */
   748   return ptr;
   749 }
   750 
   751 /* set_extension
   752  * Set the default extension, or force an extension. To erase the
   753  * extension of a filename, set "extension" to an empty string.
   754  */
   755 SC_FUNC void set_extension(char *filename,char *extension,int force)
   756 {
   757   char *ptr;
   758 
   759   assert(extension!=NULL && (*extension=='\0' || *extension=='.'));
   760   assert(filename!=NULL);
   761   ptr=get_extension(filename);
   762   if (force && ptr!=NULL)
   763     *ptr='\0';          /* set zero terminator at the position of the period */
   764   if (force || ptr==NULL)
   765     strcat(filename,extension);
   766 }
   767 
   768 static const char *option_value(const char *optptr)
   769 {
   770   return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1;
   771 }
   772 
   773 static int toggle_option(const char *optptr, int option)
   774 {
   775   switch (*option_value(optptr)) {
   776   case '\0':
   777     option=!option;
   778     break;
   779   case '-':
   780     option=FALSE;
   781     break;
   782   case '+':
   783     option=TRUE;
   784     break;
   785   default:
   786     about();
   787   } /* switch */
   788   return option;
   789 }
   790 
   791 /* Parsing command line options is indirectly recursive: parseoptions()
   792  * calls parserespf() to handle options in a a response file and
   793  * parserespf() calls parseoptions() at its turn after having created
   794  * an "option list" from the contents of the file.
   795  */
   796 static void parserespf(char *filename,char *oname,char *ename,char *pname,
   797                        char *rname, char *codepage);
   798 
   799 static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname,
   800                          char *rname, char *codepage)
   801 {
   802   char str[_MAX_PATH],*name;
   803   const char *ptr;
   804   int arg,i,isoption;
   805 
   806   for (arg=1; arg<argc; arg++) {
   807     #if DIRSEP_CHAR=='/'
   808       isoption= argv[arg][0]=='-';
   809     #else
   810       isoption= argv[arg][0]=='/' || argv[arg][0]=='-';
   811     #endif
   812     if (isoption) {
   813       ptr=&argv[arg][1];
   814       switch (*ptr) {
   815       case 'A':
   816         i=atoi(option_value(ptr));
   817         if ((i % sizeof(cell))==0)
   818           sc_dataalign=i;
   819         else
   820           about();
   821         break;
   822       case 'a':
   823         if (*(ptr+1)!='\0')
   824           about();
   825         sc_asmfile=TRUE;        /* skip last pass of making binary file */
   826         if (verbosity>1)
   827           verbosity=1;
   828         break;
   829 #if 0	/* not allowed in SourceMod */
   830       case 'C':
   831         #if AMX_COMPACTMARGIN > 2
   832           sc_compress=toggle_option(ptr,sc_compress);
   833         #else
   834           about();
   835         #endif
   836         break;
   837 #endif
   838       case 'c':
   839         strlcpy(codepage,option_value(ptr),MAXCODEPAGE);  /* set name of codepage */
   840         break;
   841 #if defined dos_setdrive
   842       case 'D':                 /* set active directory */
   843         ptr=option_value(ptr);
   844         if (ptr[1]==':')
   845           dos_setdrive(toupper(*ptr)-'A'+1);    /* set active drive */
   846         chdir(ptr);
   847         break;
   848 #endif
   849 #if 0 /* not allowed to change for SourceMod */
   850       case 'd':
   851         switch (*option_value(ptr)) {
   852         case '0':
   853           sc_debug=0;
   854           break;
   855         case '1':
   856           sc_debug=sCHKBOUNDS;  /* assertions and bounds checking */
   857           break;
   858         case '2':
   859           sc_debug=sCHKBOUNDS | sSYMBOLIC;  /* also symbolic info */
   860           break;
   861         case '3':
   862           sc_debug=sCHKBOUNDS | sSYMBOLIC;
   863           pc_optimize=sOPTIMIZE_NONE;
   864           /* also avoid peephole optimization */
   865           break;
   866         default:
   867           about();
   868         } /* switch */
   869         break;
   870 #endif
   871       case 'e':
   872         strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */
   873         break;
   874 #if defined	__WIN32__ || defined _WIN32 || defined _Windows
   875       case 'H':
   876         hwndFinish=(HWND)atoi(option_value(ptr));
   877         if (!IsWindow(hwndFinish))
   878           hwndFinish=(HWND)0;
   879         break;
   880 #endif
   881       case 'i':
   882         strlcpy(str,option_value(ptr),sizeof str);  /* set name of include directory */
   883         i=strlen(str);
   884         if (i>0) {
   885           if (str[i-1]!=DIRSEP_CHAR) {
   886             str[i]=DIRSEP_CHAR;
   887             str[i+1]='\0';
   888           } /* if */
   889           insert_path(str);
   890         } /* if */
   891         break;
   892       case 'l':
   893         if (*(ptr+1)!='\0')
   894           about();
   895         sc_listing=TRUE;        /* skip second pass & code generation */
   896         break;
   897       case 'o':
   898         strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */
   899         break;
   900       case 'O':
   901         pc_optimize=*option_value(ptr) - '0';
   902         if (pc_optimize<sOPTIMIZE_NONE || pc_optimize>=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO)
   903           about();
   904         break;
   905       case 'p':
   906         strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */
   907         break;
   908 #if !defined SC_LIGHT
   909       case 'r':
   910         strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */
   911         sc_makereport=TRUE;
   912         if (strlen(rname)>0) {
   913           set_extension(rname,".xml",FALSE);
   914         } else if ((name=get_sourcefile(0))!=NULL) {
   915           assert(strlen(rname)==0);
   916           assert(strlen(name)<_MAX_PATH);
   917           if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
   918             ptr++;          /* strip path */
   919           else
   920             ptr=name;
   921           assert(strlen(ptr)<_MAX_PATH);
   922           strcpy(rname,ptr);
   923           set_extension(rname,".xml",TRUE);
   924         } /* if */
   925         break;
   926 #endif
   927       case 'S':
   928         i=atoi(option_value(ptr));
   929         if (i>32)
   930           pc_stksize=(cell)i;   /* stack size has minimum size */
   931         else
   932           about();
   933         break;
   934       case 's':
   935         skipinput=atoi(option_value(ptr));
   936         break;
   937       case 't':
   938         sc_tabsize=atoi(option_value(ptr));
   939         break;
   940       case 'v':
   941         verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2;
   942         if (sc_asmfile && verbosity>1)
   943           verbosity=1;
   944         break;
   945       case 'w':
   946         i=(int)strtol(option_value(ptr),(char **)&ptr,10);
   947         if (*ptr=='-')
   948           pc_enablewarning(i,0);
   949         else if (*ptr=='+')
   950           pc_enablewarning(i,1);
   951         else if (*ptr=='\0')
   952           pc_enablewarning(i,2);
   953         break;
   954       case 'X':
   955         if (*(ptr+1)=='D') {
   956           i=atoi(option_value(ptr+1));
   957           if (i>64)
   958             pc_amxram=(cell)i;  /* abstract machine data/stack has minimum size */
   959           else
   960             about();
   961         } else {
   962           i=atoi(option_value(ptr));
   963           if (i>64)
   964             pc_amxlimit=(cell)i;/* abstract machine has minimum size */
   965           else
   966             about();
   967         } /* if */
   968         break;
   969       case '\\':                /* use \ instead for escape characters */
   970         sc_ctrlchar='\\';
   971         break;
   972       case '^':                 /* use ^ instead for escape characters */
   973         sc_ctrlchar='^';
   974         break;
   975       case ';':
   976         sc_needsemicolon=toggle_option(ptr,sc_needsemicolon);
   977         break;
   978 #if 0 /* not allowed to change in SourceMod */
   979       case '(':
   980         optproccall=!toggle_option(ptr,!optproccall);
   981         break;
   982 #endif
   983       default:                  /* wrong option */
   984         about();
   985       } /* switch */
   986     } else if (argv[arg][0]=='@') {
   987       #if !defined SC_LIGHT
   988         parserespf(&argv[arg][1],oname,ename,pname,rname,codepage);
   989       #endif
   990     } else if ((ptr=strchr(argv[arg],'='))!=NULL) {
   991       i=(int)(ptr-argv[arg]);
   992       if (i>sNAMEMAX) {
   993         i=sNAMEMAX;
   994         error(200,argv[arg],sNAMEMAX);  /* symbol too long, truncated to sNAMEMAX chars */
   995       } /* if */
   996       strlcpy(str,argv[arg],i+1);       /* str holds symbol name */
   997       i=atoi(ptr+1);
   998       add_constant(str,i,sGLOBAL,0);
   999     } else {
  1000       strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".sp" */
  1001       set_extension(str,".sp",FALSE);
  1002       insert_sourcefile(str);
  1003       /* The output name is the first input name with a different extension,
  1004        * but it is stored in a different directory
  1005        */
  1006       if (strlen(oname)==0) {
  1007         if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
  1008           ptr++;          /* strip path */
  1009         else
  1010           ptr=str;
  1011         assert(strlen(ptr)<_MAX_PATH);
  1012         strcpy(oname,ptr);
  1013       } /* if */
  1014       set_extension(oname,".asm",TRUE);
  1015 #if !defined SC_LIGHT
  1016       if (sc_makereport && strlen(rname)==0) {
  1017         if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL)
  1018           ptr++;          /* strip path */
  1019         else
  1020           ptr=str;
  1021         assert(strlen(ptr)<_MAX_PATH);
  1022         strcpy(rname,ptr);
  1023         set_extension(rname,".xml",TRUE);
  1024       } /* if */
  1025 #endif
  1026     } /* if */
  1027   } /* for */
  1028 }
  1029 
  1030 #if !defined SC_LIGHT
  1031 static void parserespf(char *filename,char *oname,char *ename,char *pname,
  1032                        char *rname,char *codepage)
  1033 {
  1034 #define MAX_OPTIONS     100
  1035   FILE *fp;
  1036   char *string, *ptr, **argv;
  1037   int argc;
  1038   long size;
  1039 
  1040   if ((fp=fopen(filename,"r"))==NULL)
  1041     error(120,filename);        /* error reading input file */
  1042   /* load the complete file into memory */
  1043   fseek(fp,0L,SEEK_END);
  1044   size=ftell(fp);
  1045   fseek(fp,0L,SEEK_SET);
  1046   assert(size<INT_MAX);
  1047   if ((string=(char *)malloc((int)size+1))==NULL)
  1048     error(123);                 /* insufficient memory */
  1049   /* fill with zeros; in MS-DOS, fread() may collapse CR/LF pairs to
  1050    * a single '\n', so the string size may be smaller than the file
  1051    * size. */
  1052   memset(string,0,(int)size+1);
  1053   fread(string,1,(int)size,fp);
  1054   fclose(fp);
  1055   /* allocate table for option pointers */
  1056   if ((argv=(char **)malloc(MAX_OPTIONS*sizeof(char*)))==NULL)
  1057     error(123);                 /* insufficient memory */
  1058   /* fill the options table */
  1059   ptr=strtok(string," \t\r\n");
  1060   for (argc=1; argc<MAX_OPTIONS && ptr!=NULL; argc++) {
  1061     /* note: the routine skips argv[0], for compatibility with main() */
  1062     argv[argc]=ptr;
  1063     ptr=strtok(NULL," \t\r\n");
  1064   } /* for */
  1065   if (ptr!=NULL)
  1066     error(122,"option table");   /* table overflow */
  1067   /* parse the option table */
  1068   parseoptions(argc,argv,oname,ename,pname,rname,codepage);
  1069   /* free allocated memory */
  1070   free(argv);
  1071   free(string);
  1072 }
  1073 #endif
  1074 
  1075 static void setopt(int argc,char **argv,char *oname,char *ename,char *pname,
  1076                    char *rname,char *codepage)
  1077 {
  1078   delete_sourcefiletable(); /* make sure it is empty */
  1079   *oname='\0';
  1080   *ename='\0';
  1081   *pname='\0';
  1082   *rname='\0';
  1083   *codepage='\0';
  1084   strcpy(pname,sDEF_PREFIX);
  1085 
  1086   #if 0 /* needed to test with BoundsChecker for DOS (it does not pass
  1087          * through arguments) */
  1088     insert_sourcefile("test.p");
  1089     strcpy(oname,"test.asm");
  1090   #endif
  1091 
  1092   #if !defined SC_LIGHT
  1093     /* first parse a "config" file with default options */
  1094     if (argv[0]!=NULL) {
  1095       char cfgfile[_MAX_PATH];
  1096       char *ext;
  1097       strcpy(cfgfile,argv[0]);
  1098       if ((ext=strrchr(cfgfile,DIRSEP_CHAR))!=NULL) {
  1099         *(ext+1)='\0';          /* strip the program filename */
  1100         strcat(cfgfile,"pawn.cfg");
  1101       } else {
  1102         strcpy(cfgfile,"pawn.cfg");
  1103       } /* if */
  1104       if (access(cfgfile,4)==0)
  1105         parserespf(cfgfile,oname,ename,pname,rname,codepage);
  1106     } /* if */
  1107   #endif
  1108   parseoptions(argc,argv,oname,ename,pname,rname,codepage);
  1109   if (get_sourcefile(0)==NULL)
  1110     about();
  1111 }
  1112 
  1113 #if defined __BORLANDC__ || defined __WATCOMC__
  1114   #pragma argsused
  1115 #endif
  1116 static void setconfig(char *root)
  1117 {
  1118   #if defined macintosh
  1119     insert_path(":include:");
  1120   #else
  1121     char path[_MAX_PATH];
  1122     char *ptr,*base;
  1123     int len;
  1124 
  1125     /* add the default "include" directory */
  1126     #if defined __WIN32__ || defined _WIN32
  1127       GetModuleFileNameA(NULL,path,_MAX_PATH);
  1128     #else
  1129       if (root!=NULL)
  1130         strlcpy(path,root,sizeof path); /* path + filename (hopefully) */
  1131     #endif
  1132     #if defined __MSDOS__
  1133       /* strip the options (appended to the path + filename) */
  1134       if ((ptr=strpbrk(path," \t/"))!=NULL)
  1135         *ptr='\0';
  1136     #endif
  1137     /* terminate just behind last \ or : */
  1138     if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL || (ptr=strchr(path,':'))!=NULL) {
  1139       /* If there is no "\" or ":", the string probably does not contain the
  1140        * path; so we just don't add it to the list in that case
  1141        */
  1142       *(ptr+1)='\0';
  1143       base=ptr;
  1144       strcat(path,"include");
  1145       len=strlen(path);
  1146       path[len]=DIRSEP_CHAR;
  1147       path[len+1]='\0';
  1148       /* see if it exists */
  1149       if (access(path,0)!=0 && *base==DIRSEP_CHAR) {
  1150         /* There is no "include" directory below the directory where the compiler
  1151          * is found. This typically means that the compiler is in a "bin" sub-directory
  1152          * and the "include" is below the *parent*. So find the parent...
  1153          */
  1154         *base='\0';
  1155         if ((ptr=strrchr(path,DIRSEP_CHAR))!=NULL) {
  1156           *(ptr+1)='\0';
  1157           strcat(path,"include");
  1158           len=strlen(path);
  1159           path[len]=DIRSEP_CHAR;
  1160           path[len+1]='\0';
  1161         } else {
  1162           *base=DIRSEP_CHAR;
  1163         } /* if */
  1164       } /* if */
  1165       insert_path(path);
  1166       /* same for the codepage root */
  1167       #if !defined NO_CODEPAGE
  1168         *ptr='\0';
  1169         if (!cp_path(path,"codepage"))
  1170           error(129,path);        /* codepage path */
  1171       #endif
  1172       /* also copy the root path (for the XML documentation) */
  1173       #if !defined SC_LIGHT
  1174         *ptr='\0';
  1175         strcpy(sc_rootpath,path);
  1176       #endif
  1177     } /* if */
  1178   #endif /* macintosh */
  1179 }
  1180 
  1181 static void setcaption(void)
  1182 {
  1183   pc_printf("SourcePawn Compiler " SVN_FULL_VERSION "\n");
  1184   pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2007 AlliedModders, LLC\n\n");
  1185 }
  1186 
  1187 static void about(void)
  1188 {
  1189   if (strlen(errfname)==0) {
  1190     setcaption();
  1191     pc_printf("Usage:   pawncc <filename> [filename...] [options]\n\n");
  1192     pc_printf("Options:\n");
  1193     pc_printf("         -A<num>  alignment in bytes of the data segment and the stack\n");
  1194     pc_printf("         -a       output assembler code\n");
  1195 #if 0	/* not toggleable in SourceMod */
  1196     pc_printf("         -C[+/-]  compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-');
  1197 #endif
  1198     pc_printf("         -c<name> codepage name or number; e.g. 1252 for Windows Latin-1\n");
  1199 #if defined dos_setdrive
  1200     pc_printf("         -Dpath   active directory path\n");
  1201 #endif
  1202 #if 0 /* not used for SourceMod */
  1203     pc_printf("         -d<num>  debugging level (default=-d%d)\n",sc_debug);
  1204     pc_printf("             0    no symbolic information, no run-time checks\n");
  1205     pc_printf("             1    run-time checks, no symbolic information\n");
  1206     pc_printf("             2    full debug information and dynamic checking\n");
  1207     pc_printf("             3    same as -d2, but implies -O0\n");
  1208 #endif
  1209     pc_printf("         -e<name> set name of error file (quiet compile)\n");
  1210 #if defined	__WIN32__ || defined _WIN32 || defined _Windows
  1211     pc_printf("         -H<hwnd> window handle to send a notification message on finish\n");
  1212 #endif
  1213     pc_printf("         -i<name> path for include files\n");
  1214     pc_printf("         -l       create list file (preprocess only)\n");
  1215     pc_printf("         -o<name> set base name of (P-code) output file\n");
  1216     pc_printf("         -O<num>  optimization level (default=-O%d)\n",pc_optimize);
  1217     pc_printf("             0    no optimization\n");
  1218 #if 0 /* not used for SourceMod */
  1219     pc_printf("             1    JIT-compatible optimizations only\n");
  1220 #endif
  1221     pc_printf("             2    full optimizations\n");
  1222     pc_printf("         -p<name> set name of \"prefix\" file\n");
  1223 #if !defined SC_LIGHT
  1224     pc_printf("         -r[name] write cross reference report to console or to specified file\n");
  1225 #endif
  1226     pc_printf("         -S<num>  stack/heap size in cells (default=%d)\n",(int)pc_stksize);
  1227     pc_printf("         -s<num>  skip lines from the input file\n");
  1228     pc_printf("         -t<num>  TAB indent size (in character positions, default=%d)\n",sc_tabsize);
  1229     pc_printf("         -v<num>  verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity);
  1230     pc_printf("         -w<num>  disable a specific warning by its number\n");
  1231     pc_printf("         -X<num>  abstract machine size limit in bytes\n");
  1232     pc_printf("         -XD<num> abstract machine data/stack size limit in bytes\n");
  1233     pc_printf("         -\\       use '\\' for escape characters\n");
  1234     pc_printf("         -^       use '^' for escape characters\n");
  1235     pc_printf("         -;[+/-]  require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-');
  1236 #if 0 /* not allowed in SourceMod */
  1237     pc_printf("         -([+/-]  require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+');
  1238 #endif
  1239     pc_printf("         sym=val  define constant \"sym\" with value \"val\"\n");
  1240     pc_printf("         sym=     define constant \"sym\" with value 0\n");
  1241 #if defined	__WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__
  1242     pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n");
  1243     pc_printf("equivalent.\n");
  1244 #endif
  1245     pc_printf("\nOptions with a value may optionally separate the value from the option letter\n");
  1246     pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n");
  1247     pc_printf("and \"-d:0\" are all equivalent.\n");
  1248   } /* if */
  1249   norun = 1;
  1250   longjmp(errbuf,3);        /* user abort */
  1251 }
  1252 
  1253 static void setconstants(void)
  1254 {
  1255   int debug;
  1256 
  1257   assert(sc_status==statIDLE);
  1258   append_constval(&tagname_tab,"_",0,0);/* "untagged" */
  1259   append_constval(&tagname_tab,"bool",1,0);
  1260 
  1261   pc_anytag = pc_addtag("any");
  1262   pc_functag = pc_addfunctag("Function");
  1263   pc_tag_string = pc_addtag("String");
  1264 
  1265   add_constant("true",1,sGLOBAL,1);     /* boolean flags */
  1266   add_constant("false",0,sGLOBAL,1);
  1267   add_constant("EOS",0,sGLOBAL,0);      /* End Of String, or '\0' */
  1268   #if PAWN_CELL_SIZE==16
  1269     add_constant("cellbits",16,sGLOBAL,0);
  1270     #if defined _I16_MAX
  1271       add_constant("cellmax",_I16_MAX,sGLOBAL,0);
  1272       add_constant("cellmin",_I16_MIN,sGLOBAL,0);
  1273     #else
  1274       add_constant("cellmax",SHRT_MAX,sGLOBAL,0);
  1275       add_constant("cellmin",SHRT_MIN,sGLOBAL,0);
  1276     #endif
  1277   #elif PAWN_CELL_SIZE==32
  1278     add_constant("cellbits",32,sGLOBAL,0);
  1279     #if defined _I32_MAX
  1280       add_constant("cellmax",_I32_MAX,sGLOBAL,0);
  1281       add_constant("cellmin",_I32_MIN,sGLOBAL,0);
  1282     #else
  1283       add_constant("cellmax",LONG_MAX,sGLOBAL,0);
  1284       add_constant("cellmin",LONG_MIN,sGLOBAL,0);
  1285     #endif
  1286   #elif PAWN_CELL_SIZE==64
  1287     #if !defined _I64_MIN
  1288       #define _I64_MIN  (-9223372036854775807ULL - 1)
  1289       #define _I64_MAX    9223372036854775807ULL
  1290     #endif
  1291     add_constant("cellbits",64,sGLOBAL,0);
  1292     add_constant("cellmax",_I64_MAX,sGLOBAL,0);
  1293     add_constant("cellmin",_I64_MIN,sGLOBAL,0);
  1294   #else
  1295     #error Unsupported cell size
  1296   #endif
  1297   add_constant("charbits",sCHARBITS,sGLOBAL,0);
  1298   add_constant("charmin",0,sGLOBAL,0);
  1299   add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0);
  1300   add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0);
  1301 
  1302   add_constant("__Pawn",VERSION_INT,sGLOBAL,0);
  1303 
  1304   debug=0;
  1305   if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC))
  1306     debug=2;
  1307   else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS)
  1308     debug=1;
  1309   add_constant("debug",debug,sGLOBAL,0);
  1310 
  1311   append_constval(&sc_automaton_tab,"",0,0);    /* anonymous automaton */
  1312 }
  1313 
  1314 static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst)
  1315 {
  1316   int tok,err;
  1317   cell val;
  1318   char *str;
  1319 
  1320   assert(fconst!=NULL);
  1321   assert(fstock!=NULL);
  1322   assert(fstatic!=NULL);
  1323   assert(fpublic!=NULL);
  1324   *fconst=FALSE;
  1325   *fstock=FALSE;
  1326   *fstatic=FALSE;
  1327   *fpublic=FALSE;
  1328   switch (initialtok) {
  1329   case tCONST:
  1330     *fconst=TRUE;
  1331     break;
  1332   case tSTOCK:
  1333     *fstock=TRUE;
  1334     break;
  1335   case tSTATIC:
  1336     *fstatic=TRUE;
  1337     break;
  1338   case tPUBLIC:
  1339     *fpublic=TRUE;
  1340     break;
  1341   } /* switch */
  1342 
  1343   err=0;
  1344   do {
  1345     tok=lex(&val,&str);  /* read in (new) token */
  1346     switch (tok) {
  1347     case tCONST:
  1348       if (*fconst)
  1349         err=42;          /* invalid combination of class specifiers */
  1350       *fconst=TRUE;
  1351       break;
  1352     case tSTOCK:
  1353       if (*fstock)
  1354         err=42;          /* invalid combination of class specifiers */
  1355       *fstock=TRUE;
  1356       break;
  1357     case tSTATIC:
  1358       if (*fstatic)
  1359         err=42;          /* invalid combination of class specifiers */
  1360       *fstatic=TRUE;
  1361       break;
  1362     case tPUBLIC:
  1363       if (*fpublic)
  1364         err=42;          /* invalid combination of class specifiers */
  1365       *fpublic=TRUE;
  1366       break;
  1367     default:
  1368       lexpush();
  1369       tok=0;             /* force break out of loop */
  1370     } /* switch */
  1371   } while (tok && err==0);
  1372 
  1373   /* extra checks */
  1374   if (*fstatic && *fpublic) {
  1375     err=42;              /* invalid combination of class specifiers */
  1376     *fstatic=*fpublic=FALSE;
  1377   } /* if */
  1378 
  1379   if (err)
  1380     error(err);
  1381   return err==0;
  1382 }
  1383 
  1384 /*  parse       - process all input text
  1385  *
  1386  *  At this level, only static declarations and function definitions are legal.
  1387  */
  1388 static void parse(void)
  1389 {
  1390   int tok,fconst,fstock,fstatic,fpublic;
  1391   cell val;
  1392   char *str;
  1393 
  1394   while (freading){
  1395     /* first try whether a declaration possibly is native or public */
  1396     tok=lex(&val,&str);  /* read in (new) token */
  1397     switch (tok) {
  1398     case 0:
  1399       /* ignore zero's */
  1400       break;
  1401     case tNEW:
  1402       if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst))
  1403         declglb(NULL,0,fpublic,fstatic,fstock,fconst);
  1404       break;
  1405     case tSTATIC:
  1406       /* This can be a static function or a static global variable; we know
  1407        * which of the two as soon as we have parsed up to the point where an
  1408        * opening paranthesis of a function would be expected. To back out after
  1409        * deciding it was a declaration of a static variable after all, we have
  1410        * to store the symbol name and tag.
  1411        */
  1412       if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
  1413         assert(!fpublic);
  1414         declfuncvar(fpublic,fstatic,fstock,fconst);
  1415       } /* if */
  1416       break;
  1417     case tCONST:
  1418       decl_const(sGLOBAL);
  1419       break;
  1420     case tENUM:
  1421       decl_enum(sGLOBAL);
  1422       break;
  1423     case tFUNCENUM:
  1424       dofuncenum(TRUE);
  1425       break;
  1426     case tFUNCTAG:
  1427       dofuncenum(FALSE);
  1428       break;
  1429     case tSTRUCT:
  1430       declstruct();
  1431       break;
  1432     case tPUBLIC:
  1433       /* This can be a public function or a public variable; see the comment
  1434        * above (for static functions/variables) for details.
  1435        */
  1436       if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
  1437         assert(!fstatic);
  1438         declfuncvar(fpublic,fstatic,fstock,fconst);
  1439       } /* if */
  1440       break;
  1441     case tSTOCK:
  1442       /* This can be a stock function or a stock *global*) variable; see the
  1443        * comment above (for static functions/variables) for details.
  1444        */
  1445       if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) {
  1446         assert(fstock);
  1447         declfuncvar(fpublic,fstatic,fstock,fconst);
  1448       } /* if */
  1449       break;
  1450     case tLABEL:
  1451     case tSYMBOL:
  1452     case tOPERATOR:
  1453       lexpush();
  1454       if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) {
  1455         error(10);              /* illegal function or declaration */
  1456         lexclr(TRUE);           /* drop the rest of the line */
  1457         litidx=0;               /* drop the literal queue too */
  1458       } /* if */
  1459       break;
  1460     case tNATIVE:
  1461       funcstub(TRUE);           /* create a dummy function */
  1462       break;
  1463     case tFORWARD:
  1464       funcstub(FALSE);
  1465       break;
  1466     case '}':
  1467       error(54);                /* unmatched closing brace */
  1468       break;
  1469     case '{':
  1470       error(55);                /* start of function body without function header */
  1471       break;
  1472     default:
  1473       if (freading) {
  1474         error(10);              /* illegal function or declaration */
  1475         lexclr(TRUE);           /* drop the rest of the line */
  1476         litidx=0;               /* drop any literal arrays (strings) */
  1477       } /* if */
  1478     } /* switch */
  1479   } /* while */
  1480 }
  1481 
  1482 /*  dumplits
  1483  *
  1484  *  Dump the literal pool (strings etc.)
  1485  *
  1486  *  Global references: litidx (referred to only)
  1487  */
  1488 static void dumplits(void)
  1489 {
  1490   int j,k;
  1491 
  1492   if (sc_status==statSKIP)
  1493     return;
  1494 
  1495   k=0;
  1496   while (k<litidx){
  1497     /* should be in the data segment */
  1498     assert(curseg==2);
  1499     defstorage();
  1500     j=16;       /* 16 values per line */
  1501     while (j && k<litidx){
  1502       outval(litq[k], FALSE);
  1503       stgwrite(" ");
  1504       k++;
  1505       j--;
  1506       if (j==0 || k>=litidx)
  1507         stgwrite("\n");         /* force a newline after 10 dumps */
  1508       /* Note: stgwrite() buffers a line until it is complete. It recognizes
  1509        * the end of line as a sequence of "\n\0", so something like "\n\t"
  1510        * so should not be passed to stgwrite().
  1511        */
  1512     } /* while */
  1513   } /* while */
  1514 }
  1515 
  1516 /*  dumpzero
  1517  *
  1518  *  Dump zero's for default initial values
  1519  */
  1520 static void dumpzero(int count)
  1521 {
  1522   int i;
  1523 
  1524   if (sc_status==statSKIP || count<=0)
  1525     return;
  1526   assert(curseg==2);
  1527   defstorage();
  1528   i=0;
  1529   while (count-- > 0) {
  1530     outval(0, FALSE);
  1531     i=(i+1) % 16;
  1532     stgwrite((i==0 || count==0) ? "\n" : " ");
  1533     if (i==0 && count>0)
  1534       defstorage();
  1535   } /* while */
  1536 }
  1537 
  1538 static void aligndata(int numbytes)
  1539 {
  1540   assert(numbytes % sizeof(cell) == 0);   /* alignment must be a multiple of
  1541                                            * the cell size */
  1542   assert(numbytes!=0);
  1543 
  1544   if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) {
  1545     while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0)
  1546       litadd(0);
  1547   } /* if */
  1548 
  1549 }
  1550 
  1551 #if !defined SC_LIGHT
  1552 /* sc_attachdocumentation()
  1553  * appends documentation comments to the passed-in symbol, or to a global
  1554  * string if "sym" is NULL.
  1555  */
  1556 void sc_attachdocumentation(symbol *sym)
  1557 {
  1558   int line;
  1559   size_t length;
  1560   char *str,*doc;
  1561 
  1562   if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) {
  1563     /* just clear the entire table */
  1564     delete_docstringtable();
  1565     return;
  1566   } /* if */
  1567   /* in the case of state functions, multiple documentation sections may
  1568    * appear; we should concatenate these
  1569    * (with forward declarations, this is also already the case, so the assertion
  1570    * below is invalid)
  1571    */
  1572   // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL);
  1573 
  1574   /* first check the size */
  1575   length=0;
  1576   for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
  1577     if (length>0)
  1578       length++;   /* count 1 extra for a separating space */
  1579     length+=strlen(str);
  1580   } /* for */
  1581   if (sym==NULL && sc_documentation!=NULL) {
  1582     length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
  1583     assert(length>strlen(sc_documentation));
  1584   } /* if */
  1585 
  1586   if (length>0) {
  1587     /* allocate memory for the documentation */
  1588     if (sym!=NULL && sym->documentation!=NULL)
  1589       length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
  1590     doc=(char*)malloc((length+1)*sizeof(char));
  1591     if (doc!=NULL) {
  1592       /* initialize string or concatenate */
  1593       if (sym==NULL && sc_documentation!=NULL) {
  1594         strcpy(doc,sc_documentation);
  1595         strcat(doc,"<p/>");
  1596       } else if (sym!=NULL && sym->documentation!=NULL) {
  1597         strcpy(doc,sym->documentation);
  1598         strcat(doc,"<p/>");
  1599         free(sym->documentation);
  1600         sym->documentation=NULL;
  1601       } else {
  1602         doc[0]='\0';
  1603       } /* if */
  1604       /* collect all documentation */
  1605       while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
  1606         if (doc[0]!='\0')
  1607           strcat(doc," ");
  1608         strcat(doc,str);
  1609         delete_docstring(0);
  1610       } /* while */
  1611       if (str!=NULL) {
  1612         /* also delete the separator */
  1613         assert(*str==sDOCSEP);
  1614         delete_docstring(0);
  1615       } /* if */
  1616       if (sym!=NULL) {
  1617         assert(sym->documentation==NULL);
  1618         sym->documentation=doc;
  1619       } else {
  1620         if (sc_documentation!=NULL)
  1621           free(sc_documentation);
  1622         sc_documentation=doc;
  1623       } /* if */
  1624     } /* if */
  1625   } else {
  1626     /* delete an empty separator, if present */
  1627     if ((str=get_docstring(0))!=NULL && *str==sDOCSEP)
  1628       delete_docstring(0);
  1629   } /* if */
  1630 }
  1631 
  1632 static void insert_docstring_separator(void)
  1633 {
  1634   char sep[2]={sDOCSEP,'\0'};
  1635   insert_docstring(sep);
  1636 }
  1637 #else
  1638   #define sc_attachdocumentation(s)      (void)(s)
  1639   #define insert_docstring_separator()
  1640 #endif
  1641 
  1642 static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst)
  1643 {
  1644   char name[sNAMEMAX+11];
  1645   int tok,tag=0;
  1646   char *str;
  1647   cell val;
  1648   int invalidfunc;
  1649   pstruct_t *pstruct = NULL;
  1650 
  1651   tok=lex(&val,&str);
  1652   if (tok == tLABEL) {
  1653     pstruct=pstructs_find(str);
  1654     tag=pc_addtag(str);
  1655   } else {
  1656     lexpush();
  1657   }
  1658   tok=lex(&val,&str);
  1659   /* if we arrived here, this may not be a declaration of a native function
  1660    * or variable
  1661    */
  1662   if (tok==tNATIVE) {
  1663     error(42);          /* invalid combination of class specifiers */
  1664     return;
  1665   } /* if */
  1666 
  1667   if (tok!=tSYMBOL && tok!=tOPERATOR) {
  1668     lexpush();
  1669     needtoken(tSYMBOL);
  1670     lexclr(TRUE);       /* drop the rest of the line */
  1671     litidx=0;           /* drop the literal queue too */
  1672     return;
  1673   } /* if */
  1674   if (tok==tOPERATOR) {
  1675     lexpush();          /* push "operator" keyword back (for later analysis) */
  1676     if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) {
  1677       error(10);        /* illegal function or declaration */
  1678       lexclr(TRUE);     /* drop the rest of the line */
  1679       litidx=0;         /* drop the literal queue too */
  1680     } /* if */
  1681   } else {
  1682     /* so tok is tSYMBOL */
  1683     assert(strlen(str)<=sNAMEMAX);
  1684     strcpy(name,str);
  1685     /* only variables can be "const" or both "public" and "stock" */
  1686     invalidfunc= fconst || (fpublic && fstock);
  1687     if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) {
  1688       /* if not a function, try a global variable */
  1689       if (pstruct) {
  1690         declstructvar(name,fpublic,pstruct);
  1691       } else {
  1692         declglb(name,tag,fpublic,fstatic,fstock,fconst);
  1693       }
  1694     } /* if */
  1695   } /* if */
  1696 }
  1697 
  1698 /* declstruct	- declare global struct symbols
  1699  * 
  1700  * global references: glb_declared		(altered)
  1701  */
  1702 static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
  1703 {
  1704 	char name[sNAMEMAX+1];
  1705 	int tok,i;
  1706 	cell val;
  1707 	char *str;
  1708 	int cur_litidx = 0;
  1709 	cell *values, *found;
  1710 	int usage;
  1711 	symbol *mysym,*sym;
  1712 
  1713 	strcpy(name, firstname);
  1714 
  1715 	values = (cell *)malloc(pstruct->argcount * sizeof(cell));
  1716 	found = (cell *)malloc(pstruct->argcount * sizeof(cell));
  1717 	
  1718 	memset(found, 0, sizeof(cell) * pstruct->argcount);
  1719 
  1720 	//:TODO: Make this work with stock
  1721 
  1722 	/**
  1723 	 * Lastly, very lastly, we will insert a copy of this variable.
  1724 	 * This is soley to expose the pubvar.
  1725 	 */
  1726 	usage = uREAD|uCONST|uSTRUCT;
  1727 	if (fpublic)
  1728 	{
  1729 		usage |= uPUBLIC;
  1730 	}
  1731 	mysym = NULL;
  1732 	for (sym=glbtab.next; sym!=NULL; sym=sym->next)
  1733 	{
  1734 		if (strcmp(name, sym->name) == 0)
  1735 		{
  1736 			if ((sym->usage & uSTRUCT) && sym->vclass == sGLOBAL)
  1737 			{
  1738 				if (sym->usage & uDEFINE)
  1739 				{
  1740 					error(21, name);
  1741 				} else {
  1742 					if (sym->usage & uPUBLIC && !fpublic)
  1743 					{
  1744  						error(42);
  1745 					}
  1746 				}
  1747 			} else {
  1748 				error(21, name);
  1749 			}
  1750 			mysym = sym;
  1751 			break;
  1752 		}
  1753 	}
  1754 	if (!mysym)
  1755 	{
  1756 		mysym=addsym(name, 0, iVARIABLE, sGLOBAL, pc_addtag(pstruct->name), usage);
  1757 	}
  1758 
  1759 	if (!matchtoken('='))
  1760 	{
  1761 		matchtoken(';');
  1762 		/* Mark it as undefined instead */
  1763 		mysym->usage = uSTOCK|uSTRUCT;
  1764 		return;
  1765 	} else {
  1766 		mysym->usage = usage;
  1767 	}
  1768 	needtoken('{');
  1769 	do
  1770 	{
  1771 		structarg_t *arg;
  1772 		/* Detect early exit */
  1773 		if (matchtoken('}'))
  1774 		{
  1775 			lexpush();
  1776 			break;
  1777 		}
  1778 		tok=lex(&val,&str);
  1779 		if (tok != tSYMBOL)
  1780 		{
  1781 			error(1, "-identifier-", str);
  1782 			continue;
  1783 		}
  1784 		arg=pstructs_getarg(pstruct,str);
  1785 		if (arg == NULL)
  1786 		{
  1787 			error(96, str, sym->name);
  1788 		}
  1789 		needtoken('=');
  1790 		cur_litidx = litidx;
  1791 		tok=lex(&val,&str);
  1792 		if (!arg)
  1793 		{
  1794 			continue;
  1795 		}
  1796 		if (tok == tSTRING)
  1797 		{
  1798 			assert(litidx != 0);
  1799 			if (arg->dimcount != 1)
  1800 			{
  1801 				error(48);
  1802 			} else if (arg->tag != pc_tag_string) {
  1803 				error(213);
  1804 			}
  1805 			values[arg->index] = glb_declared * sizeof(cell);
  1806 			glb_declared += (litidx-cur_litidx);
  1807 			found[arg->index] = 1;
  1808 		} else if (tok == tNUMBER || tok == tRATIONAL) {
  1809 			/* eat optional 'f' */
  1810 			matchtoken('f');
  1811 			if (arg->ident != iVARIABLE && arg->ident != iREFERENCE)
  1812 			{
  1813 				error(23);
  1814 			} else {
  1815 				if (arg->tag == pc_addtag("Float") && tok == tNUMBER ||
  1816 					arg->tag == 0 && tok == tRATIONAL)
  1817 				{
  1818 					error(213);
  1819 				}
  1820 				if (arg->ident == iVARIABLE)
  1821 				{
  1822 					values[arg->index] = val;
  1823 				} else if (arg->ident == iREFERENCE) {
  1824 					values[arg->index] = glb_declared * sizeof(cell);
  1825 					glb_declared += 1;
  1826 					litadd(val);
  1827 					cur_litidx = litidx;
  1828 				}
  1829 				found[arg->index] = 1;
  1830 			}
  1831 		} else if (tok == tSYMBOL) {
  1832 			for (sym=glbtab.next; sym!=NULL; sym=sym->next)
  1833 			{
  1834 				if (sym->vclass != sGLOBAL)
  1835 				{
  1836 					continue;
  1837 				}
  1838 				if (strcmp(sym->name, str) == 0)
  1839 				{
  1840 					if (arg->ident == iREFERENCE && sym->ident != iVARIABLE)
  1841 					{
  1842 						error(97, str);
  1843 					} else if (arg->ident == iARRAY) {
  1844 						if (sym->ident != iARRAY)
  1845 						{
  1846 							error(97, str);
  1847 						} else {
  1848 							/* :TODO: We should check dimension sizes here... */
  1849 						}
  1850 					} else if (arg->ident == iREFARRAY) {
  1851 						if (sym->ident != iARRAY)
  1852 						{
  1853 							error(97, str);
  1854 						}
  1855 						/* :TODO: Check dimension sizes! */
  1856 					} else {
  1857 						error(97, str);
  1858 					}
  1859 					if (sym->tag != arg->tag)
  1860 					{
  1861 						error(213);
  1862 					}
  1863 					sym->usage |= uREAD;
  1864 					values[arg->index] = sym->addr;
  1865 					found[arg->index] = 1;
  1866 					refer_symbol(sym, mysym);
  1867 					break;
  1868 				}
  1869 			}
  1870 			if (!sym)
  1871 			{
  1872 				error(17, str);
  1873 			}
  1874 		} else {
  1875 			error(1, "-identifier-", str);
  1876 		}
  1877 	} while (matchtoken(','));
  1878 	needtoken('}');
  1879 	matchtoken(';');	/* eat up optional semicolon */
  1880 
  1881 	for (i=0; i<pstruct->argcount; i++)
  1882 	{
  1883 		if (!found[i])
  1884 		{
  1885 			structarg_t *arg = pstruct->args[i];
  1886 			if (arg->ident == iREFARRAY)
  1887 			{
  1888 				values[arg->index] = glb_declared * sizeof(cell);
  1889 				glb_declared += 1;
  1890 				litadd(0);
  1891 				cur_litidx = litidx;
  1892 			} else if (arg->ident == iVARIABLE) {
  1893 				values[arg->index] = 0;
  1894 			} else {
  1895 				/* :TODO: broken for iARRAY! (unused tho) */
  1896 			}
  1897 		}
  1898 	}
  1899 
  1900 	mysym->addr = glb_declared * sizeof(cell);
  1901 	glb_declared += pstruct->argcount;
  1902 
  1903 	for (i=0; i<pstruct->argcount; i++)
  1904 	{
  1905 		litadd(values[i]);
  1906 	}
  1907 
  1908 	begdseg();
  1909 	dumplits();
  1910 	litidx=0;
  1911 
  1912 	free(found);
  1913 	free(values);
  1914 }
  1915 
  1916 /*  declglb     - declare global symbols
  1917  *
  1918  *  Declare a static (global) variable. Global variables are stored in
  1919  *  the DATA segment.
  1920  *
  1921  *  global references: glb_declared     (altered)
  1922  */
  1923 static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst)
  1924 {
  1925   int ident,tag,ispublic;
  1926   int idxtag[sDIMEN_MAX];
  1927   char name[sNAMEMAX+1];
  1928   cell val,size,cidx;
  1929   ucell address;
  1930   int glb_incr;
  1931   char *str;
  1932   int dim[sDIMEN_MAX];
  1933   int numdim;
  1934   int slength=0;
  1935   short filenum;
  1936   symbol *sym;
  1937   constvalue *enumroot;
  1938   #if !defined NDEBUG
  1939     cell glbdecl=0;
  1940   #endif
  1941 
  1942   assert(!fpublic || !fstatic);         /* may not both be set */
  1943   insert_docstring_separator();         /* see comment in newfunc() */
  1944   filenum=fcurrent;                     /* save file number at the start of the declaration */
  1945   do {
  1946     size=1;                             /* single size (no array) */
  1947     numdim=0;                           /* no dimensions */
  1948     ident=iVARIABLE;
  1949     if (firstname!=NULL) {
  1950       assert(strlen(firstname)<=sNAMEMAX);
  1951       strcpy(name,firstname);           /* save symbol name */
  1952       tag=firsttag;
  1953       firstname=NULL;
  1954     } else {
  1955       tag=pc_addtag(NULL);
  1956       if (lex(&val,&str)!=tSYMBOL)      /* read in (new) token */
  1957         error(20,str);                  /* invalid symbol name */
  1958       assert(strlen(str)<=sNAMEMAX);
  1959       strcpy(name,str);                 /* save symbol name */
  1960     } /* if */
  1961     ispublic=fpublic;
  1962     if (name[0]==PUBLIC_CHAR) {
  1963       ispublic=TRUE;                    /* implicitly public variable */
  1964       assert(!fstatic);
  1965     } /* if */
  1966 	while (matchtoken('[')) {
  1967       ident=iARRAY;
  1968       if (numdim == sDIMEN_MAX) {
  1969         error(53);                      /* exceeding maximum number of dimensions */
  1970         return;
  1971       } /* if */
  1972       size=needsub(&idxtag[numdim],&enumroot);  /* get size; size==0 for "var[]" */
  1973       #if INT_MAX < LONG_MAX
  1974         if (size > INT_MAX)
  1975           error(125);                   /* overflow, exceeding capacity */
  1976       #endif
  1977 #if 0	/* We don't actually care */
  1978       if (ispublic)
  1979         error(56,name);                 /* arrays cannot be public */
  1980 #endif
  1981       dim[numdim++]=(int)size;
  1982     } /* while */
  1983     if (ident == iARRAY && tag == pc_tag_string && dim[numdim-1]) {
  1984       slength=dim[numdim-1];
  1985       dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
  1986     }
  1987     assert(sc_curstates==0);
  1988     sc_curstates=getstates(name);
  1989     if (sc_curstates<0) {
  1990       error(85,name);           /* empty state list on declaration */
  1991       sc_curstates=0;
  1992     } else if (sc_curstates>0 && ispublic) {
  1993       error(88,name);           /* public variables may not have states */
  1994       sc_curstates=0;
  1995     } /* if */
  1996     sym=findconst(name,NULL);
  1997     if (sym==NULL) {
  1998       sym=findglb(name,sSTATEVAR);
  1999       /* if a global variable without states is found and this declaration has
  2000        * states, the declaration is okay
  2001        */
  2002       if (sym!=NULL && sym->states==NULL && sc_curstates>0)
  2003         sym=NULL;               /* set to NULL, we found the global variable */
  2004       if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL)
  2005         error(233,name);        /* state variable shadows a global variable */
  2006     } /* if */
  2007     /* we have either:
  2008      * a) not found a matching variable (or rejected it, because it was a shadow)
  2009      * b) found a global variable and we were looking for that global variable
  2010      * c) found a state variable in the automaton that we were looking for
  2011      */
  2012     assert(sym==NULL
  2013            || sym->states==NULL && sc_curstates==0
  2014            || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates);
  2015     /* a state variable may only have a single id in its list (so either this
  2016      * variable has no states, or it has a single list)
  2017      */
  2018     assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL);
  2019     /* it is okay for the (global) variable to exist, as long as it belongs to
  2020      * a different automaton
  2021      */
  2022     if (sym!=NULL && (sym->usage & uDEFINE)!=0)
  2023       error(21,name);                   /* symbol already defined */
  2024     /* if this variable is never used (which can be detected only in the
  2025      * second stage), shut off code generation
  2026      */
  2027     cidx=0;             /* only to avoid a compiler warning */
  2028     if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) {
  2029       sc_status=statSKIP;
  2030       cidx=code_idx;
  2031       #if !defined NDEBUG
  2032         glbdecl=glb_declared;
  2033       #endif
  2034     } /* if */
  2035     begdseg();          /* real (initialized) data in data segment */
  2036     assert(litidx==0);  /* literal queue should be empty */
  2037     if (sc_alignnext) {
  2038       litidx=0;
  2039       aligndata(sc_dataalign);
  2040       dumplits();       /* dump the literal queue */
  2041       sc_alignnext=FALSE;
  2042       litidx=0;         /* global initial data is dumped, so restart at zero */
  2043     } /* if */
  2044     assert(litidx==0);  /* literal queue should be empty (again) */
  2045     initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */
  2046     if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
  2047       slength = glbstringread;
  2048     }
  2049     assert(size>=litidx);
  2050     if (numdim==1)
  2051       dim[0]=(int)size;
  2052     /* before dumping the initial values (or zeros) check whether this variable
  2053      * overlaps another
  2054      */
  2055     if (sc_curstates>0) {
  2056       unsigned char *map;
  2057 
  2058       if (litidx!=0)
  2059         error(89,name); /* state variables may not be initialized */
  2060       /* find an appropriate address for the state variable */
  2061       /* assume that it cannot be found */
  2062       address=sizeof(cell)*glb_declared;
  2063       glb_incr=(int)size;
  2064       /* use a memory map in which every cell occupies one bit */
  2065       if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) {
  2066         int fsa=state_getfsa(sc_curstates);
  2067         symbol *sweep;
  2068         cell sweepsize,addr;
  2069         memset(map,0,(glb_declared+7)/8);
  2070         assert(fsa>=0);
  2071         /* fill in all variables belonging to this automaton */
  2072         for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
  2073           if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
  2074             continue;   /* hierarchical type, or no states, or same as this variable */
  2075           if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
  2076             continue;   /* a function or a constant */
  2077           if ((sweep->usage & uDEFINE)==0)
  2078             continue;   /* undefined variable, ignore */
  2079           if (fsa!=state_getfsa(sweep->states->next->index))
  2080             continue;   /* wrong automaton */
  2081           /* when arrived here, this is a global variable, with states and
  2082            * belonging to the same automaton as the variable we are declaring
  2083            */
  2084           sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
  2085           assert(sweep->addr % sizeof(cell) == 0);
  2086           addr=sweep->addr/sizeof(cell);
  2087           /* mark this address range */
  2088           while (sweepsize-->0) {
  2089             map[addr/8] |= (unsigned char)(1 << (addr % 8));
  2090             addr++;
  2091           } /* while */
  2092         } /* for */
  2093         /* go over it again, clearing any ranges that have conflicts */
  2094         for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) {
  2095           if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym)
  2096             continue;   /* hierarchical type, or no states, or same as this variable */
  2097           if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY)
  2098             continue;   /* a function or a constant */
  2099           if ((sweep->usage & uDEFINE)==0)
  2100             continue;   /* undefined variable, ignore */
  2101           if (fsa!=state_getfsa(sweep->states->next->index))
  2102             continue;   /* wrong automaton */
  2103           /* when arrived here, this is a global variable, with states and
  2104            * belonging to the same automaton as the variable we are declaring
  2105            */
  2106           /* if the lists of states of the existing variable and the new
  2107            * variable have a non-empty intersection, this is not a suitable
  2108            * overlap point -> wipe the address range
  2109            */
  2110           if (state_conflict_id(sc_curstates,sweep->states->next->index)) {
  2111             sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep);
  2112             assert(sweep->addr % sizeof(cell) == 0);
  2113             addr=sweep->addr/sizeof(cell);
  2114             /* mark this address range */
  2115             while (sweepsize-->0) {
  2116               map[addr/8] &= (unsigned char)(~(1 << (addr % 8)));
  2117               addr++;
  2118             } /* while */
  2119           } /* if */
  2120         } /* for */
  2121         /* now walk through the map and find a starting point that is big enough */
  2122         sweepsize=0;
  2123         for (addr=0; addr<glb_declared; addr++) {
  2124           if ((map[addr/8] & (1 << (addr % 8)))==0)
  2125             continue;
  2126           for (sweepsize=addr+1; sweepsize<glb_declared; sweepsize++) {
  2127             if ((map[sweepsize/8] & (1 << (sweepsize % 8)))==0)
  2128               break;    /* zero bit found, skip this range */
  2129             if (sweepsize-addr>=size)
  2130               break;    /* fitting range found, no need to search further */
  2131           } /* for */
  2132           if (sweepsize-addr>=size)
  2133             break;      /* fitting range found, no need to search further */
  2134           addr=sweepsize;
  2135         } /* for */
  2136         free(map);
  2137         if (sweepsize-addr>=size) {
  2138           address=sizeof(cell)*addr;    /* fitting range found, set it */
  2139           glb_incr=0;
  2140         } /* if */
  2141       } /* if */
  2142     } else {
  2143       address=sizeof(cell)*glb_declared;
  2144       glb_incr=(int)size;
  2145     } /* if */
  2146     if (address==sizeof(cell)*glb_declared) {
  2147       dumplits();       /* dump the literal queue */
  2148       dumpzero((int)size-litidx);
  2149     } /* if */
  2150     litidx=0;
  2151     if (sym==NULL) {    /* define only if not yet defined */
  2152       sym=addvariable2(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag,slength);
  2153       if (sc_curstates>0)
  2154         attachstatelist(sym,sc_curstates);
  2155     } else {            /* if declared but not yet defined, adjust the variable's address */
  2156       assert(sym->states==NULL && sc_curstates==0
  2157              || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL);
  2158       sym->addr=address;
  2159       sym->codeaddr=code_idx;
  2160       sym->usage|=uDEFINE;
  2161     } /* if */
  2162     assert(sym!=NULL);
  2163     sc_curstates=0;
  2164     if (ispublic)
  2165       sym->usage|=uPUBLIC|uREAD;
  2166     if (fconst)
  2167       sym->usage|=uCONST;
  2168     if (fstock)
  2169       sym->usage|=uSTOCK;
  2170     if (fstatic)
  2171       sym->fnumber=filenum;
  2172     sc_attachdocumentation(sym);/* attach any documenation to the variable */
  2173     if (sc_status==statSKIP) {
  2174       sc_status=statWRITE;
  2175       code_idx=cidx;
  2176       assert(glb_declared==glbdecl);
  2177     } else {
  2178       glb_declared+=glb_incr;   /* add total number of cells (if added to the end) */
  2179     } /* if */
  2180   } while (matchtoken(',')); /* enddo */   /* more? */
  2181   needtoken(tTERM);    /* if not comma, must be semicolumn */
  2182 }
  2183 
  2184 /*  declloc     - declare local symbols
  2185  *
  2186  *  Declare local (automatic) variables. Since these variables are relative
  2187  *  to the STACK, there is no switch to the DATA segment. These variables
  2188  *  cannot be initialized either.
  2189  *
  2190  *  global references: declared   (altered)
  2191  *                     funcstatus (referred to only)
  2192  */
  2193 static int declloc(int fstatic)
  2194 {
  2195   int ident,tag;
  2196   int idxtag[sDIMEN_MAX];
  2197   char name[sNAMEMAX+1];
  2198   symbol *sym;
  2199   constvalue *enumroot=NULL;
  2200   cell val,size;
  2201   char *str;
  2202   value lval = {0};
  2203   int cur_lit=0;
  2204   int dim[sDIMEN_MAX];
  2205   int numdim;
  2206   int fconst;
  2207   int staging_start;
  2208   int slength = 0;
  2209 
  2210   fconst=matchtoken(tCONST);
  2211   do {
  2212     ident=iVARIABLE;
  2213     size=1;
  2214     slength=0;
  2215     numdim=0;                           /* no dimensions */
  2216     tag=pc_addtag(NULL);
  2217     if (lex(&val,&str)!=tSYMBOL)        /* read in (new) token */
  2218       error(20,str);                    /* invalid symbol name */
  2219     assert(strlen(str)<=sNAMEMAX);
  2220     strcpy(name,str);                   /* save symbol name */
  2221     if (name[0]==PUBLIC_CHAR)
  2222       error(56,name);                   /* local variables cannot be public */
  2223     /* Note: block locals may be named identical to locals at higher
  2224      * compound blocks (as with standard C); so we must check (and add)
  2225      * the "nesting level" of local variables to verify the
  2226      * multi-definition of symbols.
  2227      */
  2228     if ((sym=findloc(name))!=NULL && sym->compound==nestlevel)
  2229       error(21,name);                   /* symbol already defined */
  2230     /* Although valid, a local variable whose name is equal to that
  2231      * of a global variable or to that of a local variable at a lower
  2232      * level might indicate a bug.
  2233      */
  2234     if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL)
  2235       error(219,name);                  /* variable shadows another symbol */
  2236 	if (matchtoken('[')) {
  2237       int _index;
  2238       cell _code;
  2239       int dim_ident;
  2240       symbol *dim_sym;
  2241       value dim_val;
  2242       int all_constant = 1;
  2243       int _staging = staging;
  2244 
  2245       if (!_staging)
  2246         stgset(TRUE);
  2247       stgget(&_index, &_code);
  2248 
  2249       do {
  2250         if (numdim == sDIMEN_MAX) {
  2251           error(53);                      /* exceeding maximum number of dimensions */
  2252           return (all_constant ? iARRAY : iREFARRAY);
  2253         } else if (numdim) { /* if */
  2254           /* If we have a dimension on the stack, push it */
  2255           pushreg(sPRI);
  2256         }
  2257         if (matchtoken(']')) {
  2258           idxtag[numdim] = 0;
  2259           dim[numdim] = 0;
  2260 		  numdim++;
  2261           continue;
  2262         }
  2263         dim_ident = doexpr2(TRUE,FALSE,FALSE,FALSE,&idxtag[numdim],&dim_sym,0,&dim_val);
  2264         if (dim_ident == iVARIABLE || dim_ident == iEXPRESSION || dim_ident == iARRAYCELL) {
  2265           all_constant = 0;
  2266         } else if (dim_ident == iCONSTEXPR) {
  2267           dim[numdim] = dim_val.constval;
  2268 		  /* :TODO: :URGENT: Make sure this still works */
  2269           if (dim_sym && dim_sym->usage & uENUMROOT)
  2270             enumroot = dim_sym->dim.enumlist;
  2271 		  idxtag[numdim] = dim_sym ? dim_sym->tag : 0;
  2272           #if INT_MAX < LONG_MAX
  2273 		    if (dim[numdim] > INT_MAX)
  2274 			  error(125);                   /* overflow, exceeding capacity */
  2275           #endif
  2276         } else {
  2277           error(29); /* invalid expression, assumed 0 */
  2278         }
  2279         numdim++;
  2280 		needtoken(']');
  2281       } while (matchtoken('['));
  2282       if (all_constant) {
  2283         /* Change the last dimension to be based on chars instead if we have a string */
  2284         if (tag == pc_tag_string && numdim && dim[numdim-1]) {
  2285           slength = dim[numdim-1];
  2286           dim[numdim-1] = (dim[numdim-1] + sizeof(cell)-1) / sizeof(cell);
  2287         }
  2288         /* Scrap the code generated */
  2289         ident = iARRAY;
  2290         stgdel(_index, _code);
  2291       } else {
  2292         if (tag == pc_tag_string && numdim && dim[numdim-1]) {
  2293           stradjust(sPRI);
  2294         }
  2295         pushreg(sPRI);
  2296         memset(dim, 0, sizeof(int)*sDIMEN_MAX);
  2297         ident = iREFARRAY;
  2298         genarray(numdim, autozero);
  2299       }
  2300       stgout(_index);
  2301       if (!_staging)
  2302         stgset(FALSE);
  2303     }
  2304     if (getstates(name))
  2305       error(88,name);           /* local variables may not have states */
  2306     if (ident==iARRAY || fstatic) {
  2307       if (sc_alignnext) {
  2308         aligndata(sc_dataalign);
  2309         sc_alignnext=FALSE;
  2310       } /* if */
  2311       cur_lit=litidx;           /* save current index in the literal table */
  2312       if (numdim && !dim[numdim-1])
  2313         size = 0;
  2314       initials(ident,tag,&size,dim,numdim,enumroot);
  2315       if (tag == pc_tag_string && (numdim == 1) && !dim[numdim-1]) {
  2316         slength = glbstringread;
  2317       }
  2318       if (size==0)
  2319         return ident;           /* error message already given */
  2320       if (numdim==1)
  2321         dim[0]=(int)size;
  2322     }
  2323     /* reserve memory (on the stack) for the variable */
  2324     if (fstatic) {
  2325       /* write zeros for uninitialized fields */
  2326       while (litidx<cur_lit+size)
  2327         litadd(0);
  2328       sym=addvariable2(name,(cur_lit+glb_declared)*sizeof(cell),ident,sSTATIC,
  2329                       tag,dim,numdim,idxtag,slength);
  2330     } else if (ident!=iREFARRAY) {
  2331       declared+=(int)size;      /* variables are put on stack, adjust "declared" */
  2332       sym=addvariable2(name,-declared*sizeof(cell),ident,sLOCAL,tag,
  2333                       dim,numdim,idxtag,slength);
  2334       if (ident==iVARIABLE) {
  2335         assert(!staging);
  2336         stgset(TRUE);           /* start stage-buffering */
  2337         assert(stgidx==0);
  2338         staging_start=stgidx;
  2339       } /* if */
  2340       markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
  2341       modstk(-(int)size*sizeof(cell));
  2342       markstack(MEMUSE_STATIC, size);
  2343       assert(curfunc!=NULL);
  2344       assert((curfunc->usage & uNATIVE)==0);
  2345       if (curfunc->x.stacksize<declared+1)
  2346         curfunc->x.stacksize=declared+1;  /* +1 for PROC opcode */
  2347     } else if (ident==iREFARRAY) {
  2348       declared+=1;	/* one cell for address */
  2349       sym=addvariable(name,-declared*sizeof(cell),ident,sLOCAL,tag,dim,numdim,idxtag);
  2350       //markexpr(sLDECL,name,-declared*sizeof(cell)); /* mark for better optimization */
  2351       /* genarray() pushes the address onto the stack, so we don't need to call modstk() here! */
  2352       markheap(MEMUSE_DYNAMIC, 0);
  2353 	  markstack(MEMUSE_STATIC, 1);
  2354       assert(curfunc != NULL && ((curfunc->usage & uNATIVE) == 0));
  2355       if (curfunc->x.stacksize<declared+1)
  2356         curfunc->x.stacksize=declared+1;  /* +1 for PROC opcode */
  2357     } /* if */
  2358     /* now that we have reserved memory for the variable, we can proceed
  2359      * to initialize it */
  2360     assert(sym!=NULL);          /* we declared it, it must be there */
  2361     sym->compound=nestlevel;    /* for multiple declaration/shadowing check */
  2362     if (fconst)
  2363       sym->usage|=uCONST;
  2364     if (!fstatic) {             /* static variables already initialized */
  2365       if (ident==iVARIABLE) {
  2366         /* simple variable, also supports initialization */
  2367         int ctag = tag;         /* set to "tag" by default */
  2368         int explicit_init=FALSE;/* is the variable explicitly initialized? */
  2369         int cident=ident;
  2370         if (matchtoken('=')) {
  2371           if (!autozero)
  2372             error(10);
  2373           cident=doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE);
  2374           explicit_init=TRUE;
  2375         } else {
  2376           if (autozero)
  2377             ldconst(0,sPRI);      /* uninitialized variable, set to zero */
  2378         } /* if */
  2379         if (autozero) {
  2380           /* now try to save the value (still in PRI) in the variable */
  2381           lval.sym=sym;
  2382           lval.ident=iVARIABLE;
  2383           lval.constval=0;
  2384           lval.tag=tag;
  2385           check_userop(NULL,ctag,lval.tag,2,NULL,&ctag);
  2386           store(&lval);
  2387           markexpr(sEXPR,NULL,0); /* full expression ends after the store */
  2388         }
  2389         assert(staging);        /* end staging phase (optimize expression) */
  2390         stgout(staging_start);
  2391         stgset(FALSE);
  2392         if (!matchtag_string(cident, ctag) && !matchtag(tag,ctag,TRUE))
  2393 		{
  2394 		  if (tag & FUNCTAG)
  2395 		    error(100);      /* error - function prototypes do not match */
  2396 		  else
  2397 		    error(213);      /* warning - tag mismatch */
  2398 		}
  2399         /* if the variable was not explicitly initialized, reset the
  2400          * "uWRITTEN" flag that store() set */
  2401         if (!explicit_init)
  2402           sym->usage &= ~uWRITTEN;
  2403       } else if (ident!=iREFARRAY) {
  2404         /* an array */
  2405         assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax);
  2406         assert(size>0 && size>=sym->dim.array.length);
  2407         assert(numdim>1 || size==sym->dim.array.length);
  2408         if (autozero) {
  2409           /* final literal values that are zero make no sense to put in the literal
  2410            * pool, because values get zero-initialized anyway; we check for this,
  2411            * because users often explicitly initialize strings to ""
  2412            */
  2413           while (litidx>cur_lit && litq[litidx-1]==0)
  2414             litidx--;
  2415           /* if the array is not completely filled, set all values to zero first */
  2416           if (litidx-cur_lit<size && (ucell)size<CELL_MAX)
  2417             fillarray(sym,size*sizeof(cell),0);
  2418         }
  2419         if (cur_lit<litidx) {
  2420           /* check whether the complete array is set to a single value; if
  2421            * it is, more compact code can be generated */
  2422           cell first=litq[cur_lit];
  2423           int i;
  2424           for (i=cur_lit; i<litidx && litq[i]==first; i++)
  2425             /* nothing */;
  2426           if (i==litidx) {
  2427             /* all values are the same */
  2428             fillarray(sym,(litidx-cur_lit)*sizeof(cell),first);
  2429             litidx=cur_lit;     /* reset literal table */
  2430           } else {
  2431             /* copy the literals to the array */
  2432             ldconst((cur_lit+glb_declared)*sizeof(cell),sPRI);
  2433             copyarray(sym,(litidx-cur_lit)*sizeof(cell));
  2434           } /* if */
  2435         } /* if */
  2436       } /* if */
  2437     } /* if */
  2438   } while (matchtoken(',')); /* enddo */   /* more? */
  2439   needtoken(tTERM);    /* if not comma, must be semicolumn */
  2440   return ident;
  2441 }
  2442 
  2443 /* this function returns the maximum value for a cell in case of an error
  2444  * (invalid dimension).
  2445  */
  2446 static cell calc_arraysize(int dim[],int numdim,int cur)
  2447 {
  2448   cell subsize;
  2449   ucell newsize;
  2450 
  2451   /* the return value is in cells, not bytes */
  2452   assert(cur>=0 && cur<=numdim);
  2453   if (cur==numdim)
  2454     return 0;
  2455   subsize=calc_arraysize(dim,numdim,cur+1);
  2456   newsize=dim[cur]+dim[cur]*subsize;
  2457   if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize*sizeof(cell)>=CELL_MAX)
  2458     return CELL_MAX;
  2459   return newsize;
  2460 }
  2461 
  2462 static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment,
  2463                                      int startlit,constvalue *lastdim,int *skipdim)
  2464 {
  2465 static int base;
  2466   int d;
  2467   cell accum;
  2468 
  2469   assert(cur>=0 && cur<numdim);
  2470   assert(increment>=0);
  2471   assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx);
  2472   if (cur==0)
  2473     base=startlit;
  2474   if (cur==numdim-1)
  2475     return 0;
  2476   /* 2 or more dimensions left, fill in an indirection vector */
  2477   assert(dim[cur]>0);
  2478   if (dim[cur+1]>0) {
  2479     for (d=0; d<dim[cur]; d++)
  2480       litq[base++]=(dim[cur]+d*(dim[cur+1]-1)+increment) * sizeof(cell);
  2481     accum=dim[cur]*(dim[cur+1]-1);
  2482   } else {
  2483     /* final dimension is variable length */
  2484     constvalue *ld;
  2485     assert(dim[cur+1]==0);
  2486     assert(lastdim!=NULL);
  2487     assert(skipdim!=NULL);
  2488     accum=0;
  2489     /* skip the final dimension sizes for all earlier major dimensions */
  2490     for (d=0,ld=lastdim->next; d<*skipdim; d++,ld=ld->next) {
  2491       assert(ld!=NULL);
  2492     } /* for */
  2493     for (d=0; d<dim[cur]; d++) {
  2494       assert(ld!=NULL);
  2495       assert(strtol(ld->name,NULL,16)==d);
  2496       litq[base++]=(dim[cur]+accum+increment) * sizeof(cell);
  2497       accum+=ld->value-1;
  2498       *skipdim+=1;
  2499       ld=ld->next;
  2500     } /* for */
  2501   } /* if */
  2502   /* create the indirection tables for the lower level */
  2503   if (cur+2<numdim) {   /* are there at least 2 dimensions below this one? */
  2504     increment+=(dim[cur]-1)*dim[cur+1]; /* this many indirection tables follow */
  2505     for (d=0; d<dim[cur]; d++)
  2506       increment+=adjust_indirectiontables(dim,numdim,cur+1,increment,-1,lastdim,skipdim);
  2507   } /* if */
  2508   return accum;
  2509 }
  2510 
  2511 /*  initials
  2512  *
  2513  *  Initialize global objects and local arrays.
  2514  *    size==array cells (count), if 0 on input, the routine counts the number of elements
  2515  *    tag==required tagname id (not the returned tag)
  2516  *
  2517  *  Global references: litidx (altered)
  2518  */
  2519 static void initials(int ident,int tag,cell *size,int dim[],int numdim,
  2520                      constvalue *enumroot)
  2521 {
  2522   int ctag;
  2523   cell tablesize;
  2524   int curlit=litidx;
  2525   int err=0;
  2526 
  2527   if (!matchtoken('=')) {
  2528     assert(ident!=iARRAY || numdim>0);
  2529     if (ident==iARRAY && dim[numdim-1]==0) {
  2530       /* declared as "myvar[];" which is senseless (note: this *does* make
  2531        * sense in the case of a iREFARRAY, which is a function parameter)
  2532        */
  2533       error(9);         /* array has zero length -> invalid size */
  2534     } /* if */
  2535     if (ident==iARRAY) {
  2536       assert(numdim>0 && numdim<=sDIMEN_MAX);
  2537       *size=calc_arraysize(dim,numdim,0);
  2538       if (*size==(cell)CELL_MAX) {
  2539         error(9);       /* array is too big -> invalid size */
  2540         return;
  2541       } /* if */
  2542       /* first reserve space for the indirection vectors of the array, then
  2543        * adjust it to contain the proper values
  2544        * (do not use dumpzero(), as it bypasses the literal queue)
  2545        */
  2546       for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
  2547         litadd(0);
  2548       if (dim[numdim-1]!=0)     /* error 9 has already been given */
  2549         adjust_indirectiontables(dim,numdim,0,0,curlit,NULL,NULL);
  2550     } /* if */
  2551     return;
  2552   } /* if */
  2553 
  2554   if (ident==iVARIABLE) {
  2555     assert(*size==1);
  2556     init(ident,&ctag,NULL);
  2557     if (!matchtag(tag,ctag,TRUE))
  2558 	{
  2559 	  if (tag & FUNCTAG)
  2560 	    error(100);     /* error - function prototypes do not match */
  2561 	  else
  2562         error(213);     /* warning - tag mismatch */
  2563 	}
  2564   } else {
  2565     assert(numdim>0);
  2566     if (numdim==1) {
  2567       *size=initvector(ident,tag,dim[0],FALSE,enumroot,NULL);
  2568     } else {
  2569       int errorfound=FALSE;
  2570       int counteddim[sDIMEN_MAX];
  2571       int idx;
  2572       constvalue lastdim={NULL,"",0,0};     /* sizes of the final dimension */
  2573       int skipdim=0;
  2574 
  2575       if (dim[numdim-1]!=0)
  2576         *size=calc_arraysize(dim,numdim,0); /* calc. full size, if known */
  2577       /* already reserve space for the indirection tables (for an array with
  2578        * known dimensions)
  2579        * (do not use dumpzero(), as it bypasses the literal queue)
  2580        */
  2581       for (tablesize=calc_arraysize(dim,numdim-1,0); tablesize>0; tablesize--)
  2582         litadd(0);
  2583       /* now initialize the sub-arrays */
  2584       memset(counteddim,0,sizeof counteddim);
  2585       initarray(ident,tag,dim,numdim,0,curlit,counteddim,&lastdim,enumroot,&errorfound);
  2586       /* check the specified array dimensions with the initializer counts */
  2587       for (idx=0; idx<numdim-1; idx++) {
  2588         if (dim[idx]==0) {
  2589           dim[idx]=counteddim[idx];
  2590         } else if (counteddim[idx]<dim[idx]) {
  2591           error(52);            /* array is not fully initialized */
  2592           err++;
  2593         } else if (counteddim[idx]>dim[idx]) {
  2594           error(18);            /* initialization data exceeds declared size */
  2595           err++;
  2596         } /* if */
  2597       } /* for */
  2598       if (numdim>1 && dim[numdim-1]==0) {
  2599         /* also look whether, by any chance, all "counted" final dimensions are
  2600          * the same value; if so, we can store this
  2601          */
  2602         constvalue *ld=lastdim.next;
  2603         int d,match;
  2604         for (d=0; d<dim[numdim-2]; d++) {
  2605           assert(ld!=NULL);
  2606           assert(strtol(ld->name,NULL,16)==d);
  2607           if (d==0)
  2608             match=ld->value;
  2609           else if (match!=ld->value)
  2610             break;
  2611           ld=ld->next;
  2612         } /* for */
  2613         if (d==dim[numdim-2])
  2614           dim[numdim-1]=match;
  2615       } /* if */
  2616       /* after all arrays have been initalized, we know the (major) dimensions
  2617        * of the array and we can properly adjust the indirection vectors
  2618        */
  2619       if (err==0)
  2620         adjust_indirectiontables(dim,numdim,0,0,curlit,&lastdim,&skipdim);
  2621       delete_consttable(&lastdim);  /* clear list of minor dimension sizes */
  2622     } /* if */
  2623   } /* if */
  2624 
  2625   if (*size==0)
  2626     *size=litidx-curlit;        /* number of elements defined */
  2627 }
  2628 
  2629 static cell initarray(int ident,int tag,int dim[],int numdim,int cur,
  2630                       int startlit,int counteddim[],constvalue *lastdim,
  2631                       constvalue *enumroot,int *errorfound)
  2632 {
  2633   cell dsize,totalsize;
  2634   int idx,abortparse;
  2635 
  2636   assert(cur>=0 && cur<numdim);
  2637   assert(startlit>=0);
  2638   assert(cur+2<=numdim);/* there must be 2 dimensions or more to do */
  2639   assert(errorfound!=NULL && *errorfound==FALSE);
  2640   totalsize=0;
  2641   needtoken('{');
  2642   for (idx=0,abortparse=FALSE; !abortparse; idx++) {
  2643     /* In case the major dimension is zero, we need to store the offset
  2644      * to the newly detected sub-array into the indirection table; i.e.
  2645      * this table needs to be expanded and updated.
  2646      * In the current design, the indirection vectors for a multi-dimensional
  2647      * array are adjusted after parsing all initializers. Hence, it is only
  2648      * necessary at this point to reserve space for an extra cell in the
  2649      * indirection vector.
  2650      */
  2651     if (dim[cur]==0) {
  2652       litinsert(0,startlit);
  2653     } else if (idx>=dim[cur]) {
  2654       error(18);            /* initialization data exceeds array size */
  2655       break;
  2656     } /* if */
  2657     if (cur+2<numdim) {
  2658       dsize=initarray(ident,tag,dim,numdim,cur+1,startlit,counteddim,
  2659                       lastdim,enumroot,errorfound);
  2660     } else {
  2661       dsize=initvector(ident,tag,dim[cur+1],TRUE,enumroot,errorfound);
  2662       /* The final dimension may be variable length. We need to keep the
  2663        * lengths of the final dimensions in order to set the indirection
  2664        * vectors for the next-to-last dimension.
  2665        */
  2666       append_constval(lastdim,itoh(idx),dsize,0);
  2667     } /* if */
  2668     totalsize+=dsize;
  2669     if (*errorfound || !matchtoken(','))
  2670       abortparse=TRUE;
  2671   } /* for */
  2672   needtoken('}');
  2673   assert(counteddim!=NULL);
  2674   if (counteddim[cur]>0) {
  2675     if (idx<counteddim[cur])
  2676       error(52);                /* array is not fully initialized */
  2677     else if (idx>counteddim[cur])
  2678       error(18);                /* initialization data exceeds declared size */
  2679   } /* if */
  2680   counteddim[cur]=idx;
  2681 
  2682   return totalsize+dim[cur];    /* size of sub-arrays + indirection vector */
  2683 }
  2684 
  2685 /*  initvector
  2686  *  Initialize a single dimensional array
  2687  */
  2688 static cell initvector(int ident,int tag,cell size,int fillzero,
  2689                        constvalue *enumroot,int *errorfound)
  2690 {
  2691   cell prev1=0,prev2=0;
  2692   int ellips=FALSE;
  2693   int curlit=litidx;
  2694   int rtag,ctag;
  2695 
  2696   assert(ident==iARRAY || ident==iREFARRAY);
  2697   if (matchtoken('{')) {
  2698     constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL;
  2699     do {
  2700       int fieldlit=litidx;
  2701       int matchbrace,i;
  2702       if (matchtoken('}')) {    /* to allow for trailing ',' after the initialization */
  2703         lexpush();
  2704         break;
  2705       } /* if */
  2706       if ((ellips=matchtoken(tELLIPS))!=0)
  2707         break;
  2708       /* for enumeration fields, allow another level of braces ("{...}") */
  2709       matchbrace=0;             /* preset */
  2710       ellips=0;
  2711       if (enumfield!=NULL)
  2712         matchbrace=matchtoken('{');
  2713       for ( ;; ) {
  2714         prev2=prev1;
  2715         prev1=init(ident,&ctag,errorfound);
  2716         if (!matchbrace)
  2717           break;
  2718         if ((ellips=matchtoken(tELLIPS))!=0)
  2719           break;
  2720         if (!matchtoken(',')) {
  2721           needtoken('}');
  2722           break;
  2723         } /* for */
  2724       } /* for */
  2725       /* if this array is based on an enumeration, fill the "field" up with
  2726        * zeros, and toggle the tag
  2727        */
  2728       if (enumroot!=NULL && enumfield==NULL)
  2729         error(227);             /* more initializers than enum fields */
  2730       rtag=tag;                 /* preset, may be overridden by enum field tag */
  2731       if (enumfield!=NULL) {
  2732         cell step;
  2733         int cmptag=enumfield->index;
  2734         symbol *symfield=findconst(enumfield->name,&cmptag);
  2735         if (cmptag>1)
  2736           error(91,enumfield->name); /* ambiguous constant, needs tag override */
  2737         assert(symfield!=NULL);
  2738         assert(fieldlit<litidx);
  2739         if (litidx-fieldlit>symfield->dim.array.length)
  2740           error(228);           /* length of initializer exceeds size of the enum field */
  2741         if (ellips) {
  2742           step=prev1-prev2;
  2743         } else {
  2744           step=0;
  2745           prev1=0;
  2746         } /* if */
  2747         for (i=litidx-fieldlit; i<symfield->dim.array.length; i++) {
  2748           prev1+=step;
  2749           litadd(prev1);
  2750         } /* for */
  2751         rtag=symfield->x.tags.index;  /* set the expected tag to the index tag */
  2752         enumfield=enumfield->next;
  2753       } /* if */
  2754       if (!matchtag(rtag,ctag,TRUE))
  2755 	  {
  2756 	    if (rtag & FUNCTAG)
  2757 		  error(100);           /* error - function prototypes do not match */
  2758 		else
  2759 		  error(213);           /* warning - tag mismatch */
  2760 	  }
  2761     } while (matchtoken(',')); /* do */
  2762     needtoken('}');
  2763   } else {
  2764     init(ident,&ctag,errorfound);
  2765     if (!matchtag(tag,ctag,TRUE))
  2766       error(213);               /* tagname mismatch */
  2767   } /* if */
  2768   /* fill up the literal queue with a series */
  2769   if (ellips) {
  2770     cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2;
  2771     if (size==0 || (litidx-curlit)==0)
  2772       error(41);                /* invalid ellipsis, array size unknown */
  2773     else if ((litidx-curlit)==(int)size)
  2774       error(18);                /* initialisation data exceeds declared size */
  2775     while ((litidx-curlit)<(int)size) {
  2776       prev1+=step;
  2777       litadd(prev1);
  2778     } /* while */
  2779   } /* if */
  2780   if (fillzero && size>0) {
  2781     while ((litidx-curlit)<(int)size)
  2782       litadd(0);
  2783   } /* if */
  2784   if (size==0) {
  2785     size=litidx-curlit;                 /* number of elements defined */
  2786   } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */
  2787     error(18);                  /* initialisation data exceeds declared size */
  2788     litidx=(int)size+curlit;    /* avoid overflow in memory moves */
  2789   } /* if */
  2790   return size;
  2791 }
  2792 
  2793 /*  init
  2794  *
  2795  *  Evaluate one initializer.
  2796  */
  2797 static cell init(int ident,int *tag,int *errorfound)
  2798 {
  2799   cell i = 0;
  2800 
  2801   if (matchtoken(tSTRING)){
  2802     /* lex() automatically stores strings in the literal table (and
  2803      * increases "litidx") */
  2804     if (ident==iVARIABLE) {
  2805       error(6);         /* must be assigned to an array */
  2806       litidx=1;         /* reset literal queue */
  2807     } /* if */
  2808     *tag=pc_tag_string;
  2809   } else if (constexpr(&i,tag,NULL)){
  2810     litadd(i);          /* store expression result in literal table */
  2811   } else {
  2812     if (errorfound!=NULL)
  2813       *errorfound=TRUE;
  2814   } /* if */
  2815   return i;
  2816 }
  2817 
  2818 /*  needsub
  2819  *
  2820  *  Get required array size
  2821  */
  2822 static cell needsub(int *tag,constvalue **enumroot)
  2823 {
  2824   cell val;
  2825   symbol *sym;
  2826 
  2827   assert(tag!=NULL);
  2828   *tag=0;
  2829   if (enumroot!=NULL)
  2830     *enumroot=NULL;         /* preset */
  2831   if (matchtoken(']'))      /* we have already seen "[" */
  2832     return 0;               /* zero size (like "char msg[]") */
  2833 
  2834   constexpr(&val,tag,&sym); /* get value (must be constant expression) */
  2835   if (val<0) {
  2836     error(9);               /* negative array size is invalid; assumed zero */
  2837     val=0;
  2838   } /* if */
  2839   needtoken(']');
  2840 
  2841   if (enumroot!=NULL) {
  2842     /* get the field list for an enumeration */
  2843     assert(*enumroot==NULL);/* should have been preset */
  2844     assert(sym==NULL || sym->ident==iCONSTEXPR);
  2845     if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) {
  2846       assert(sym->dim.enumlist!=NULL);
  2847       *enumroot=sym->dim.enumlist;
  2848     } /* if */
  2849   } /* if */
  2850 
  2851   return val;               /* return array size */
  2852 }
  2853 
  2854 /*  decl_const  - declare a single constant
  2855  *
  2856  */
  2857 static void decl_const(int vclass)
  2858 {
  2859   char constname[sNAMEMAX+1];
  2860   cell val;
  2861   char *str;
  2862   int tag,exprtag;
  2863   int symbolline;
  2864   symbol *sym;
  2865 
  2866   insert_docstring_separator();         /* see comment in newfunc() */
  2867   do {
  2868     tag=pc_addtag(NULL);
  2869     if (lex(&val,&str)!=tSYMBOL)        /* read in (new) token */
  2870       error(20,str);                    /* invalid symbol name */
  2871     symbolline=fline;                   /* save line where symbol was found */
  2872     strcpy(constname,str);              /* save symbol name */
  2873     needtoken('=');
  2874     constexpr(&val,&exprtag,NULL);      /* get value */
  2875     /* add_constant() checks for duplicate definitions */
  2876     if (!matchtag(tag,exprtag,FALSE)) {
  2877       /* temporarily reset the line number to where the symbol was defined */
  2878       int orgfline=fline;
  2879       fline=symbolline;
  2880       error(213);                       /* tagname mismatch */
  2881       fline=orgfline;
  2882     } /* if */
  2883     sym=add_constant(constname,val,vclass,tag);
  2884     if (sym!=NULL)
  2885       sc_attachdocumentation(sym);/* attach any documenation to the constant */
  2886   } while (matchtoken(',')); /* enddo */   /* more? */
  2887   needtoken(tTERM);
  2888 }
  2889 
  2890 /*
  2891  * declstruct - declare a struct type
  2892  */
  2893 static void declstruct(void)
  2894 {
  2895 	cell val;
  2896 	char *str;
  2897 	int tok;
  2898 	pstruct_t *pstruct;
  2899 	int size;
  2900 
  2901 	/* get the explicit tag (required!) */
  2902 	tok = lex(&val,&str);
  2903 	if (tok != tSYMBOL)
  2904 	{
  2905 		error(93);
  2906 	}
  2907 
  2908 	if (pstructs_find(str) != NULL)
  2909 	{
  2910 		error(98);
  2911 	}
  2912 
  2913 	pstruct = pstructs_add(str);
  2914 
  2915 	needtoken('{');
  2916 	do
  2917 	{
  2918 		structarg_t arg;
  2919 		if (matchtoken('}'))
  2920 		{
  2921 			/* Quick exit */
  2922 			lexpush();
  2923 			break;
  2924 		}
  2925 		memset(&arg, 0, sizeof(structarg_t));
  2926 		tok = lex(&val,&str);
  2927 		if (tok == tCONST)
  2928 		{
  2929 			arg.fconst = 1;
  2930 			tok = lex(&val,&str);
  2931 		}
  2932 		arg.ident = 0;
  2933 		if (tok == '&')
  2934 		{
  2935 			arg.ident = iREFERENCE;
  2936 			tok = lex(&val,&str);
  2937 		}
  2938 		if (tok == tLABEL)
  2939 		{
  2940 			arg.tag = pc_addtag(str);
  2941 			tok = lex(&val,&str);
  2942 		}
  2943 		if (tok != tSYMBOL)
  2944 		{
  2945 			error(1, "-identifier-", str);
  2946 			continue;
  2947 		}
  2948 		strcpy(arg.name, str);
  2949 		if (matchtoken('['))
  2950 		{
  2951 			if (arg.ident == iREFERENCE)
  2952 			{
  2953 				error(67, arg.name);
  2954 			}
  2955 			arg.ident = iARRAY;
  2956 			do 
  2957 			{
  2958 				constvalue *enumroot;
  2959 				int ignore_tag;
  2960 				if (arg.dimcount == sDIMEN_MAX)
  2961 				{
  2962 					error(53);
  2963 					break;
  2964 				}
  2965 				size = needsub(&ignore_tag, &enumroot);
  2966 				arg.dims[arg.dimcount++] = size;
  2967 			} while (matchtoken('['));
  2968 			/* Handle strings */
  2969 			if (arg.tag == pc_tag_string && arg.dims[arg.dimcount-1])
  2970 			{
  2971 				arg.dims[arg.dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
  2972 			}
  2973 			if (arg.dimcount == 1 && !arg.dims[arg.dimcount-1])
  2974 			{
  2975 				arg.ident = iREFARRAY;
  2976 			}
  2977 		} else if (!arg.ident) {
  2978 			arg.ident = iVARIABLE;
  2979 		}
  2980 		if (pstructs_addarg(pstruct, &arg) == NULL)
  2981 		{
  2982 			error(99, arg.name, pstruct->name);
  2983 		}
  2984 	} while (matchtoken(','));
  2985 	needtoken('}');
  2986 	matchtoken(';');	/* eat up optional semicolon */
  2987 }
  2988 
  2989 
  2990 /**
  2991  * dofuncenum - declare function enumerations
  2992  */
  2993 static void dofuncenum(int listmode)
  2994 {
  2995 	cell val;
  2996 	char *str;
  2997 	// char *ptr;
  2998 	char tagname[sNAMEMAX+1];
  2999 	constvalue *cur;
  3000 	funcenum_t *fenum = NULL;
  3001 	int i;
  3002 
  3003 	/* get the explicit tag (required!) */
  3004 	int l = lex(&val,&str);
  3005 	if (l != tSYMBOL)
  3006 	{
  3007 		error(93);
  3008 	}
  3009 
  3010 	/* This tag can't already exist! */
  3011 	cur=tagname_tab.next;
  3012 	while (cur)
  3013 	{
  3014 		if (strcmp(cur->name, str) == 0)
  3015 		{
  3016 			/* Another bad one... */
  3017 			if (!(cur->value & FUNCTAG))
  3018 			{
  3019 				error(94);
  3020 			}
  3021 			break;
  3022 		}
  3023 		cur = cur->next;
  3024 	}
  3025 	strcpy(tagname, str);
  3026 
  3027 	fenum = funcenums_add(tagname);
  3028 
  3029 	if (listmode)
  3030 	{
  3031 		needtoken('{');
  3032 	}
  3033 	do
  3034 	{
  3035 		functag_t func;
  3036 		if (listmode && matchtoken('}'))
  3037 		{
  3038 			/* Quick exit */
  3039 			lexpush();
  3040 			break;
  3041 		}
  3042 		memset(&func, 0, sizeof(func));
  3043 		func.ret_tag = pc_addtag(NULL);	/* Get the return tag */
  3044 		l = lex(&val, &str);
  3045 		/* :TODO:
  3046 		 * Right now, there is a problem.  We can't pass non-public function pointer addresses around,
  3047 		 * because the address isn't known until the final reparse.  Unfortunately, you can write code
  3048 		 * before the address is known, because Pawn's compiler isn't truly multipass.
  3049 		 *
  3050 		 * When you call a function, there's no problem, because it does not write the address.
  3051 		 * The assembly looks like this:
  3052 		 *   call MyFunction
  3053 		 * Then, only at assembly time (once all passes are done), does it know the address.
  3054 		 * I do not see any solution to this because there is no way I know to inject the function name
  3055 		 * rather than the constant value.  And even if we could, we'd have to change the assembler recognize that.
  3056 		 */
  3057 		/*if (l == tSTOCK)
  3058 		{
  3059 			func.type = uSTOCK;
  3060 		} else */if (l == tPUBLIC) {
  3061 			func.type = uPUBLIC;
  3062 		} else {
  3063 			error(1, "-public-", str);
  3064 		}
  3065 		needtoken('(');
  3066 		do 
  3067 		{
  3068 			funcarg_t *arg = &(func.args[func.argcount]);
  3069 
  3070 			/* Quick exit */
  3071 			if (matchtoken(')'))
  3072 			{
  3073 				lexpush();
  3074 				break;
  3075 			}
  3076 			l = lex(&val, &str);
  3077 			if (l == '&')
  3078 			{
  3079 				if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
  3080 				{
  3081 					error(1, "-identifier-", "&");
  3082 				}
  3083 				arg->ident = iREFERENCE;
  3084 			} else if (l == tCONST) {
  3085 				if ((arg->ident != iVARIABLE && arg->ident != 0) || arg->tagcount > 0)
  3086 				{
  3087 					error(1, "-identifier-", "const");
  3088 				}
  3089 				arg->fconst=TRUE;
  3090 			} else if (l == tLABEL) {
  3091 				if (arg->tagcount > 0)
  3092 				{
  3093 					error(1, "-identifier-", "-tagname-");
  3094 				}
  3095 				arg->tags[arg->tagcount++] = pc_addtag(str);
  3096 #if 0
  3097 				while (arg->tagcount < sTAGS_MAX)
  3098 				{
  3099 					if (!matchtoken('_') && !needtoken(tSYMBOL))
  3100 					{
  3101 						break;
  3102 					}
  3103 					tokeninfo(&val, &ptr);
  3104 					arg->tags[arg->tagcount++] = pc_addtag(ptr);
  3105 					if (matchtoken('}'))
  3106 					{
  3107 						break;
  3108 					}
  3109 					needtoken(',');
  3110 				}
  3111 				needtoken(':');
  3112 #endif
  3113 				l=tLABEL;
  3114 			} else if (l == tSYMBOL) {
  3115 				if (func.argcount >= sARGS_MAX)
  3116 				{
  3117 					error(45);
  3118 				}
  3119 				if (str[0] == PUBLIC_CHAR)
  3120 				{
  3121 					error(56, str);
  3122 				}
  3123 				if (matchtoken('['))
  3124 				{
  3125 					cell size;
  3126 					if (arg->ident == iREFERENCE)
  3127 					{
  3128 						error(67, str);
  3129 					}
  3130 					do 
  3131 					{
  3132 						constvalue *enumroot;
  3133 						int ignore_tag;
  3134 						if (arg->dimcount == sDIMEN_MAX)
  3135 						{
  3136 							error(53);
  3137 							break;
  3138 						}
  3139 						size = needsub(&ignore_tag, &enumroot);
  3140 						arg->dims[arg->dimcount] = size;
  3141 						arg->dimcount += 1;
  3142 					} while (matchtoken('['));
  3143 					/* Handle strings */
  3144 					if ((arg->tagcount == 1 && arg->tags[0] == pc_tag_string)
  3145 						&& arg->dims[arg->dimcount-1])
  3146 					{
  3147 						arg->dims[arg->dimcount-1] = (size + sizeof(cell)-1) / sizeof(cell);
  3148 					}
  3149 					arg->ident=iREFARRAY;
  3150 				} else if (arg->ident == 0) {
  3151 					arg->ident = iVARIABLE;
  3152 				}
  3153 			
  3154 				if (matchtoken('='))
  3155 				{
  3156 					needtoken('0');
  3157 					arg->ommittable = TRUE;
  3158 					func.ommittable = TRUE;
  3159 				} else if (func.ommittable) {
  3160 					error(95);
  3161 				}
  3162 				func.argcount++;
  3163 			} else if (l == tELLIPS) {
  3164 				if (arg->ident == iVARIABLE)
  3165 				{
  3166 					error(10);
  3167 				}
  3168 				arg->ident = iVARARGS;
  3169 				func.argcount++;
  3170 			} else {
  3171 				error(10);
  3172 			}
  3173 		} while (l == '&' || l == tLABEL || l == tCONST || l != tELLIPS && matchtoken(','));
  3174 		needtoken(')');
  3175 		for (i=0; i<func.argcount; i++)
  3176 		{
  3177 			if (func.args[i].tagcount == 0)
  3178 			{
  3179 				func.args[i].tags[0] = 0;
  3180 				func.args[i].tagcount = 1;
  3181 			}
  3182 		}
  3183 		functags_add(fenum, &func);
  3184 		if (!listmode)
  3185 		{
  3186 			break;
  3187 		}
  3188 	} while (matchtoken(','));
  3189 	if (listmode)
  3190 	{
  3191 		needtoken('}');
  3192 	}
  3193 	matchtoken(';'); /* eat an optional semicolon.  nom nom nom */
  3194 	errorset(sRESET, 0);
  3195 }
  3196 
  3197 /*  decl_enum   - declare enumerated constants
  3198  *
  3199  */
  3200 static void decl_enum(int vclass)
  3201 {
  3202   char enumname[sNAMEMAX+1],constname[sNAMEMAX+1];
  3203   cell val,value,size;
  3204   char *str;
  3205   int tag,explicittag;
  3206   cell increment,multiplier;
  3207   constvalue *enumroot;
  3208   symbol *enumsym;
  3209 
  3210   /* get an explicit tag, if any (we need to remember whether an explicit
  3211    * tag was passed, even if that explicit tag was "_:", so we cannot call
  3212    * pc_addtag() here
  3213    */
  3214   if (lex(&val,&str)==tLABEL) {
  3215     tag=pc_addtag(str);
  3216     explicittag=TRUE;
  3217   } else {
  3218     lexpush();
  3219     tag=0;
  3220     explicittag=FALSE;
  3221   } /* if */
  3222 
  3223   /* get optional enum name (also serves as a tag if no explicit tag was set) */
  3224   if (lex(&val,&str)==tSYMBOL) {        /* read in (new) token */
  3225     strcpy(enumname,str);               /* save enum name (last constant) */
  3226     if (!explicittag)
  3227       tag=pc_addtag(enumname);
  3228   } else {
  3229     lexpush();                          /* analyze again */
  3230     enumname[0]='\0';
  3231   } /* if */
  3232 
  3233   /* get increment and multiplier */
  3234   increment=1;
  3235   multiplier=1;
  3236   if (matchtoken('(')) {
  3237     if (matchtoken(taADD)) {
  3238       constexpr(&increment,NULL,NULL);
  3239     } else if (matchtoken(taMULT)) {
  3240       constexpr(&multiplier,NULL,NULL);
  3241     } else if (matchtoken(taSHL)) {
  3242       constexpr(&val,NULL,NULL);
  3243       while (val-->0)
  3244         multiplier*=2;
  3245     } /* if */
  3246     needtoken(')');
  3247   } /* if */
  3248 
  3249   if (strlen(enumname)>0) {
  3250     /* already create the root symbol, so the fields can have it as their "parent" */
  3251     enumsym=add_constant(enumname,0,vclass,tag);
  3252     if (enumsym!=NULL)
  3253       enumsym->usage |= uENUMROOT;
  3254     /* start a new list for the element names */
  3255     if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL)
  3256       error(123);                       /* insufficient memory (fatal error) */
  3257     memset(enumroot,0,sizeof(constvalue));
  3258   } else {
  3259     enumsym=NULL;
  3260     enumroot=NULL;
  3261   } /* if */
  3262 
  3263   needtoken('{');
  3264   /* go through all constants */
  3265   value=0;                              /* default starting value */
  3266   do {
  3267     int idxtag,fieldtag;
  3268     symbol *sym;
  3269     if (matchtoken('}')) {              /* quick exit if '}' follows ',' */
  3270       lexpush();
  3271       break;
  3272     } /* if */
  3273     idxtag=pc_addtag(NULL);             /* optional explicit item tag */
  3274     if (needtoken(tSYMBOL)) {           /* read in (new) token */
  3275       tokeninfo(&val,&str);             /* get the information */
  3276       strcpy(constname,str);            /* save symbol name */
  3277     } else {
  3278       constname[0]='\0';
  3279     } /* if */
  3280     size=increment;                     /* default increment of 'val' */
  3281     fieldtag=0;                         /* default field tag */
  3282     if (matchtoken('[')) {
  3283       constexpr(&size,&fieldtag,NULL);  /* get size */
  3284       needtoken(']');
  3285     } /* if */
  3286     /* :TODO: do we need a size modifier here for pc_tag_string? */
  3287     if (matchtoken('='))
  3288       constexpr(&value,NULL,NULL);      /* get value */
  3289     /* add_constant() checks whether a variable (global or local) or
  3290      * a constant with the same name already exists
  3291      */
  3292     sym=add_constant(constname,value,vclass,tag);
  3293     if (sym==NULL)
  3294       continue;                         /* error message already given */
  3295     /* set the item tag and the item size, for use in indexing arrays */
  3296     sym->x.tags.index=idxtag;
  3297     sym->x.tags.field=fieldtag;
  3298     sym->dim.array.length=size;
  3299     sym->dim.array.level=0;
  3300     sym->dim.array.slength=0;
  3301     sym->parent=enumsym;
  3302     /* add the constant to a separate list as well */
  3303     if (enumroot!=NULL) {
  3304       sym->usage |= uENUMFIELD;
  3305       append_constval(enumroot,constname,value,tag);
  3306     } /* if */
  3307     if (multiplier==1)
  3308       value+=size;
  3309     else
  3310       value*=size*multiplier;
  3311   } while (matchtoken(','));
  3312   needtoken('}');       /* terminates the constant list */
  3313   matchtoken(';');      /* eat an optional ; */
  3314 
  3315   /* set the enum name to the "next" value (typically the last value plus one) */
  3316   if (enumsym!=NULL) {
  3317     assert((enumsym->usage & uENUMROOT)!=0);
  3318     enumsym->addr=value;
  3319     /* assign the constant list */
  3320     assert(enumroot!=NULL);
  3321     enumsym->dim.enumlist=enumroot;
  3322     sc_attachdocumentation(enumsym);  /* attach any documenation to the enumeration */
  3323   } /* if */
  3324 }
  3325 
  3326 static int getstates(const char *funcname)
  3327 {
  3328   char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1];
  3329   cell val;
  3330   char *str;
  3331   constvalue *automaton;
  3332   constvalue *state;
  3333   int fsa,islabel;
  3334   int *list;
  3335   int count,listsize,state_id;
  3336 
  3337   if (!matchtoken('<'))
  3338     return 0;
  3339   if (matchtoken('>'))
  3340     return -1;          /* special construct: all other states (fall-back) */
  3341 
  3342   count=0;
  3343   listsize=0;
  3344   list=NULL;
  3345   fsa=-1;
  3346 
  3347   do {
  3348     if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL))
  3349       break;
  3350     tokeninfo(&val,&str);
  3351     assert(strlen(str)<sizeof fsaname);
  3352     strcpy(fsaname,str);  /* assume this is the name of the automaton */
  3353     if (islabel || matchtoken(':')) {
  3354       /* token is an automaton name, add the name and get a new token */
  3355       if (!needtoken(tSYMBOL))
  3356         break;
  3357       tokeninfo(&val,&str);
  3358       assert(strlen(str)<sizeof statename);
  3359       strcpy(statename,str);
  3360     } else {
  3361       /* the token was the state name (part of an anynymous automaton) */
  3362       assert(strlen(fsaname)<sizeof statename);
  3363       strcpy(statename,fsaname);
  3364       fsaname[0]='\0';
  3365     } /* if */
  3366     if (fsa<0 || fsaname[0]!='\0') {
  3367       automaton=automaton_add(fsaname);
  3368       assert(automaton!=NULL);
  3369       if (fsa>=0 && automaton->index!=fsa)
  3370         error(83,funcname); /* multiple automatons for a single function/variable */
  3371       fsa=automaton->index;
  3372     } /* if */
  3373     state=state_add(statename,fsa);
  3374     /* add this state to the state combination list (it will be attached to the
  3375      * automaton later) */
  3376     state_buildlist(&list,&listsize,&count,(int)state->value);
  3377   } while (matchtoken(','));
  3378   needtoken('>');
  3379 
  3380   if (count>0) {
  3381     assert(automaton!=NULL);
  3382     assert(fsa>=0);
  3383     state_id=state_addlist(list,count,fsa);
  3384     assert(state_id>0);
  3385   } else {
  3386     /* error is already given */
  3387     state_id=0;
  3388   } /* if */
  3389   if (list!=NULL)
  3390     free(list);
  3391 
  3392   return state_id;
  3393 }
  3394 
  3395 static void attachstatelist(symbol *sym, int state_id)
  3396 {
  3397   assert(sym!=NULL);
  3398   if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0))
  3399     error(21,sym->name); /* function already defined, either without states or the current definition has no states */
  3400 
  3401   if (state_id!=0) {
  3402     /* add the state list id */
  3403     constvalue *stateptr;
  3404     if (sym->states==NULL) {
  3405       if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL)
  3406         error(123);             /* insufficient memory (fatal error) */
  3407       memset(sym->states,0,sizeof(constvalue));
  3408     } /* if */
  3409     /* see whether the id already exists (add new state only if it does not
  3410      * yet exist
  3411      */
  3412     assert(sym->states!=NULL);
  3413     for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next)
  3414       /* nothing */;
  3415     assert(state_id<=SHRT_MAX);
  3416     if (stateptr==NULL)
  3417       append_constval(sym->states,"",code_idx,(short)state_id);
  3418     else if (stateptr->value==0)
  3419       stateptr->value=code_idx;
  3420     else
  3421       error(84,sym->name);
  3422     /* also check for another conflicting situation: a fallback function
  3423      * without any states
  3424      */
  3425     if (state_id==-1 && sc_status!=statFIRST) {
  3426       /* in the second round, all states should have been accumulated */
  3427       assert(sym->states!=NULL);
  3428       for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next)
  3429         /* nothing */;
  3430       if (stateptr==NULL)
  3431         error(85,sym->name);      /* no states are defined for this function */
  3432     } /* if */
  3433   } /* if */
  3434 }
  3435 
  3436 /*
  3437  *  Finds a function in the global symbol table or creates a new entry.
  3438  *  It does some basic processing and error checking.
  3439  */
  3440 SC_FUNC symbol *fetchfunc(char *name,int tag)
  3441 {
  3442   symbol *sym;
  3443 
  3444   if ((sym=findglb(name,sGLOBAL))!=0) {   /* already in symbol table? */
  3445     if (sym->ident!=iFUNCTN) {
  3446       error(21,name);                     /* yes, but not as a function */
  3447       return NULL;                        /* make sure the old symbol is not damaged */
  3448     } else if ((sym->usage & uNATIVE)!=0) {
  3449       error(21,name);                     /* yes, and it is a native */
  3450     } /* if */
  3451     assert(sym->vclass==sGLOBAL);
  3452     if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag)
  3453       error(25);                          /* mismatch from earlier prototype */
  3454     if ((sym->usage & uDEFINE)==0) {
  3455       /* as long as the function stays undefined, update the address and the tag */
  3456       if (sym->states==NULL)
  3457         sym->addr=code_idx;
  3458       sym->tag=tag;
  3459     } /* if */
  3460   } else {
  3461     /* don't set the "uDEFINE" flag; it may be a prototype */
  3462     sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0);
  3463     assert(sym!=NULL);          /* fatal error 103 must be given on error */
  3464     /* assume no arguments */
  3465     sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo));
  3466     sym->dim.arglist[0].ident=0;
  3467     /* set library ID to NULL (only for native functions) */
  3468     sym->x.lib=NULL;
  3469     /* set the required stack size to zero (only for non-native functions) */
  3470     sym->x.stacksize=1;         /* 1 for PROC opcode */
  3471   } /* if */
  3472   if (pc_deprecate!=NULL) {
  3473     assert(sym!=NULL);
  3474     sym->flags|=flgDEPRECATED;
  3475     if (sc_status==statWRITE) {
  3476       if (sym->documentation!=NULL) {
  3477         free(sym->documentation);
  3478         sym->documentation=NULL;
  3479       } /* if */
  3480       sym->documentation=pc_deprecate;
  3481     } else {
  3482       free(pc_deprecate);
  3483     } /* if */
  3484     pc_deprecate=NULL;
  3485   } /* if */
  3486 
  3487   return sym;
  3488 }
  3489 
  3490 /* This routine adds symbolic information for each argument.
  3491  */
  3492 static void define_args(void)
  3493 {
  3494   symbol *sym;
  3495 
  3496   /* At this point, no local variables have been declared. All
  3497    * local symbols are function arguments.
  3498    */
  3499   sym=loctab.next;
  3500   while (sym!=NULL) {
  3501     assert(sym->ident!=iLABEL);
  3502     assert(sym->vclass==sLOCAL);
  3503     markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */
  3504     sym=sym->next;
  3505   } /* while */
  3506 }
  3507 
  3508 static int operatorname(char *name)
  3509 {
  3510   int opertok;
  3511   char *str;
  3512   cell val;
  3513 
  3514   assert(name!=NULL);
  3515 
  3516   /* check the operator */
  3517   opertok=lex(&val,&str);
  3518   switch (opertok) {
  3519   case '+':
  3520   case '-':
  3521   case '*':
  3522   case '/':
  3523   case '%':
  3524   case '>':
  3525   case '<':
  3526   case '!':
  3527   case '~':
  3528   case '=':
  3529     name[0]=(char)opertok;
  3530     name[1]='\0';
  3531     break;
  3532   case tINC:
  3533     strcpy(name,"++");
  3534     break;
  3535   case tDEC:
  3536     strcpy(name,"--");
  3537     break;
  3538   case tlEQ:
  3539     strcpy(name,"==");
  3540     break;
  3541   case tlNE:
  3542     strcpy(name,"!=");
  3543     break;
  3544   case tlLE:
  3545     strcpy(name,"<=");
  3546     break;
  3547   case tlGE:
  3548     strcpy(name,">=");
  3549     break;
  3550   default:
  3551     name[0]='\0';
  3552     error(7);           /* operator cannot be redefined (or bad operator name) */
  3553     return 0;
  3554   } /* switch */
  3555 
  3556   return opertok;
  3557 }
  3558 
  3559 static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag)
  3560 {
  3561   int tags[2]={0,0};
  3562   int count=0;
  3563   arginfo *arg;
  3564   char tmpname[sNAMEMAX+1];
  3565   symbol *oldsym;
  3566 
  3567   if (opertok==0)
  3568     return TRUE;
  3569 
  3570   assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL);
  3571   /* count arguments and save (first two) tags */
  3572   while (arg=&sym->dim.arglist[count], arg->ident!=0) {
  3573     if (count<2) {
  3574       if (arg->numtags>1)
  3575         error(65,count+1);  /* function argument may only have a single tag */
  3576       else if (arg->numtags==1)
  3577         tags[count]=arg->tags[0];
  3578     } /* if */
  3579     if (opertok=='~' && count==0) {
  3580       if (arg->ident!=iREFARRAY)
  3581         error(73,arg->name);/* must be an array argument */
  3582     } else {
  3583       //if (arg->ident!=iVARIABLE)
  3584         //error(66,arg->name);/* must be non-reference argument */
  3585     } /* if */
  3586     if (arg->hasdefault)
  3587       error(59,arg->name);  /* arguments of an operator may not have a default value */
  3588     count++;
  3589   } /* while */
  3590 
  3591   /* for '!', '++' and '--', count must be 1
  3592    * for '-', count may be 1 or 2
  3593    * for '=', count must be 1, and the resulttag is also important
  3594    * for all other (binary) operators and the special '~' operator, count must be 2
  3595    */
  3596   switch (opertok) {
  3597   case '!':
  3598   case '=':
  3599   case tINC:
  3600   case tDEC:
  3601     if (count!=1)
  3602       error(62);      /* number or placement of the operands does not fit the operator */
  3603     break;
  3604   case '-':
  3605     if (count!=1 && count!=2)
  3606       error(62);      /* number or placement of the operands does not fit the operator */
  3607     break;
  3608   default:
  3609     if (count!=2)
  3610       error(62);      /* number or placement of the operands does not fit the operator */
  3611   } /* switch */
  3612 
  3613   if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0))
  3614     error(64);        /* cannot change predefined operators */
  3615 
  3616   /* change the operator name */
  3617   assert(strlen(opername)>0);
  3618   operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag);
  3619   if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) {
  3620     int i;
  3621     if ((oldsym->usage & uDEFINE)!=0) {
  3622       char errname[2*sNAMEMAX+16];
  3623       funcdisplayname(errname,tmpname);
  3624       error(21,errname);        /* symbol already defined */
  3625     } /* if */
  3626     sym->usage|=oldsym->usage;  /* copy flags from the previous definition */
  3627     for (i=0; i<oldsym->numrefers; i++)
  3628       if (oldsym->refer[i]!=NULL)
  3629         refer_symbol(sym,oldsym->refer[i]);
  3630     delete_symbol(&glbtab,oldsym);
  3631   } /* if */
  3632   strcpy(sym->name,tmpname);
  3633   sym->hash=namehash(sym->name);/* calculate new hash */
  3634 
  3635   /* operators should return a value, except the '~' operator */
  3636   if (opertok!='~')
  3637     sym->usage |= uRETVALUE;
  3638 
  3639   return TRUE;
  3640 }
  3641 
  3642 static int check_operatortag(int opertok,int resulttag,char *opername)
  3643 {
  3644   assert(opername!=NULL && strlen(opername)>0);
  3645   switch (opertok) {
  3646   case '!':
  3647   case '<':
  3648   case '>':
  3649   case tlEQ:
  3650   case tlNE:
  3651   case tlLE:
  3652   case tlGE:
  3653     if (resulttag!=pc_addtag("bool")) {
  3654       error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */
  3655       return FALSE;
  3656     } /* if */
  3657     break;
  3658   case '~':
  3659     if (resulttag!=0) {
  3660       error(63,opername,"_:");    /* operator "~" requires a "_:" result tag */
  3661       return FALSE;
  3662     } /* if */
  3663     break;
  3664   } /* switch */
  3665   return TRUE;
  3666 }
  3667 
  3668 static char *tag2str(char *dest,int tag)
  3669 {
  3670   tag &= TAGMASK;
  3671   assert(tag>=0);
  3672   sprintf(dest,"0%x",tag);
  3673   return isdigit(dest[1]) ? &dest[1] : dest;
  3674 }
  3675 
  3676 SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag)
  3677 {
  3678   char tagstr1[10], tagstr2[10];
  3679   int opertok;
  3680 
  3681   assert(numtags>=1 && numtags<=2);
  3682   opertok= (opername[1]=='\0') ? opername[0] : 0;
  3683   if (opertok=='=')
  3684     sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1));
  3685   else if (numtags==1 || opertok=='~')
  3686     sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1));
  3687   else
  3688     sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2));
  3689   return symname;
  3690 }
  3691 
  3692 static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname)
  3693 {
  3694   char *ptr,*name;
  3695   int unary;
  3696 
  3697   /* tags are only positive, so if the function name starts with a '-',
  3698    * the operator is an unary '-' or '--' operator.
  3699    */
  3700   if (*fname=='-') {
  3701     *tag1=0;
  3702     unary=TRUE;
  3703     ptr=fname;
  3704   } else {
  3705     *tag1=(int)strtol(fname,&ptr,16);
  3706     unary= ptr==fname;  /* unary operator if it doesn't start with a tag name */
  3707   } /* if */
  3708   assert(!unary || *tag1==0);
  3709   assert(*ptr!='\0');
  3710   for (name=opname; !isdigit(*ptr); )
  3711     *name++ = *ptr++;
  3712   *name='\0';
  3713   *tag2=(int)strtol(ptr,NULL,16);
  3714   return unary;
  3715 }
  3716 
  3717 constvalue *find_tag_byval(int tag)
  3718 {
  3719   constvalue *tagsym;
  3720   tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG);
  3721   if (tagsym==NULL)
  3722     tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);
  3723   return tagsym;
  3724 }
  3725 
  3726 SC_FUNC char *funcdisplayname(char *dest,char *funcname)
  3727 {
  3728   int tags[2];
  3729   char opname[10];
  3730   constvalue *tagsym[2];
  3731   int unary;
  3732 
  3733   if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') {
  3734     if (dest!=funcname)
  3735       strcpy(dest,funcname);
  3736     return dest;
  3737   } /* if */
  3738 
  3739   unary=parse_funcname(funcname,&tags[0],&tags[1],opname);
  3740   tagsym[1]=find_tag_byval(tags[1]);
  3741   assert(tagsym[1]!=NULL);
  3742   if (unary) {
  3743     sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name);
  3744   } else {
  3745     tagsym[0]=find_tag_byval(tags[0]);
  3746     assert(tagsym[0]!=NULL);
  3747     /* special case: the assignment operator has the return value as the 2nd tag */
  3748     if (opname[0]=='=' && opname[1]=='\0')
  3749       sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name);
  3750     else
  3751       sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name);
  3752   } /* if */
  3753   return dest;
  3754 }
  3755 
  3756 static void funcstub(int fnative)
  3757 {
  3758   int tok,tag,fpublic;
  3759   char *str;
  3760   cell val,size;
  3761   char symbolname[sNAMEMAX+1];
  3762   int idxtag[sDIMEN_MAX];
  3763   int dim[sDIMEN_MAX];
  3764   int numdim;
  3765   symbol *sym,*sub;
  3766   int opertok;
  3767 
  3768   opertok=0;
  3769   lastst=0;
  3770   litidx=0;                     /* clear the literal pool */
  3771   assert(loctab.next==NULL);    /* local symbol table should be empty */
  3772 
  3773   tag=pc_addtag(NULL);			/* get the tag of the return value */
  3774   numdim=0;
  3775   while (matchtoken('[')) {
  3776     /* the function returns an array, get this tag for the index and the array
  3777      * dimensions
  3778      */
  3779     if (numdim == sDIMEN_MAX) {
  3780       error(53);                /* exceeding maximum number of dimensions */
  3781       return;
  3782     } /* if */
  3783     size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
  3784     if (size==0)
  3785       error(9);                 /* invalid array size */
  3786     #if INT_MAX < LONG_MAX
  3787       if (size > INT_MAX)
  3788         error(125);             /* overflow, exceeding capacity */
  3789     #endif
  3790     dim[numdim++]=(int)size;
  3791   } /* while */
  3792 
  3793   if (tag == pc_tag_string && numdim && dim[numdim-1])
  3794     dim[numdim-1] = (size + sizeof(cell)-1) / sizeof(cell);
  3795 
  3796   tok=lex(&val,&str);
  3797   fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR);
  3798   if (fnative) {
  3799     if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR)
  3800       error(42);                /* invalid combination of class specifiers */
  3801   } else {
  3802     if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC)
  3803       tok=lex(&val,&str);
  3804   } /* if */
  3805 
  3806   if (tok==tOPERATOR) {
  3807     opertok=operatorname(symbolname);
  3808     if (opertok==0)
  3809       return;                   /* error message already given */
  3810     check_operatortag(opertok,tag,symbolname);
  3811   } else {
  3812     if (tok!=tSYMBOL && freading) {
  3813       error(10);                /* illegal function or declaration */
  3814       return;
  3815     } /* if */
  3816     strcpy(symbolname,str);
  3817   } /* if */
  3818   needtoken('(');               /* only functions may be native/forward */
  3819 
  3820   sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
  3821   if (sym==NULL)
  3822     return;
  3823   if (fnative) {
  3824     sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
  3825     sym->x.lib=curlibrary;
  3826   } else if (fpublic) {
  3827     sym->usage|=uPUBLIC;
  3828   } /* if */
  3829   sym->usage|=uFORWARD;
  3830 
  3831   declargs(sym,FALSE);
  3832   /* "declargs()" found the ")" */
  3833   sc_attachdocumentation(sym);  /* attach any documenation to the function */
  3834   if (!operatoradjust(opertok,sym,symbolname,tag))
  3835     sym->usage &= ~uDEFINE;
  3836 
  3837   if (getstates(symbolname)!=0) {
  3838     if (fnative || opertok!=0)
  3839       error(82);                /* native functions and operators may not have states */
  3840     else
  3841       error(231);               /* ignoring state specifications on forward declarations */
  3842   } /* if */
  3843 
  3844   /* for a native operator, also need to specify an "exported" function name;
  3845    * for a native function, this is optional
  3846    */
  3847   if (fnative) {
  3848     if (opertok!=0) {
  3849 		if (matchtoken('['))
  3850 		{
  3851 			printf("Hrm!\n");
  3852 		}
  3853       needtoken('=');
  3854       lexpush();        /* push back, for matchtoken() to retrieve again */
  3855     } /* if */
  3856     if (matchtoken('=')) {
  3857       /* allow number or symbol */
  3858       if (matchtoken(tSYMBOL)) {
  3859         tokeninfo(&val,&str);
  3860         insert_alias(sym->name,str);
  3861       } else {
  3862         constexpr(&val,NULL,NULL);
  3863         sym->addr=val;
  3864         /* At the moment, I have assumed that this syntax is only valid if
  3865          * val < 0. To properly mix "normal" native functions and indexed
  3866          * native functions, one should use negative indices anyway.
  3867          * Special code for a negative index in sym->addr exists in SC4.C
  3868          * (ffcall()) and in SC6.C (the loops for counting the number of native
  3869          * variables and for writing them).
  3870          */
  3871       } /* if */
  3872     } /* if */
  3873   } /* if */
  3874   needtoken(tTERM);
  3875 
  3876   /* attach the array to the function symbol */
  3877   if (numdim>0) {
  3878     assert(sym!=NULL);
  3879     sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag);
  3880     sub->parent=sym;
  3881   } /* if */
  3882 
  3883   litidx=0;                     /* clear the literal pool */
  3884   delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
  3885 }
  3886 
  3887 /*  newfunc    - begin a function
  3888  *
  3889  *  This routine is called from "parse" and tries to make a function
  3890  *  out of the following text
  3891  *
  3892  *  Global references: funcstatus,lastst,litidx
  3893  *                     rettype  (altered)
  3894  *                     curfunc  (altered)
  3895  *                     declared (altered)
  3896  *                     glb_declared (altered)
  3897  *                     sc_alignnext (altered)
  3898  */
  3899 static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock)
  3900 {
  3901   symbol *sym;
  3902   int argcnt,tok,tag,funcline;
  3903   int opertok,opererror;
  3904   char symbolname[sNAMEMAX+1];
  3905   char *str;
  3906   cell val,cidx,glbdecl;
  3907   short filenum;
  3908   int state_id;
  3909 
  3910   assert(litidx==0);    /* literal queue should be empty */
  3911   litidx=0;             /* clear the literal pool (should already be empty) */
  3912   opertok=0;
  3913   lastst=0;             /* no statement yet */
  3914   cidx=0;               /* just to avoid compiler warnings */
  3915   glbdecl=0;
  3916   assert(loctab.next==NULL);    /* local symbol table should be empty */
  3917   filenum=fcurrent;     /* save file number at the start of the declaration */
  3918 
  3919   if (firstname!=NULL) {
  3920     assert(strlen(firstname)<=sNAMEMAX);
  3921     strcpy(symbolname,firstname);       /* save symbol name */
  3922     tag=firsttag;
  3923   } else {
  3924     tag= (firsttag>=0) ? firsttag : pc_addtag(NULL);
  3925     tok=lex(&val,&str);
  3926     assert(!fpublic);
  3927     if (tok==tNATIVE || tok==tPUBLIC && stock)
  3928       error(42);                /* invalid combination of class specifiers */
  3929     if (tok==tOPERATOR) {
  3930       opertok=operatorname(symbolname);
  3931       if (opertok==0)
  3932         return TRUE;            /* error message already given */
  3933       check_operatortag(opertok,tag,symbolname);
  3934     } else {
  3935       if (tok!=tSYMBOL && freading) {
  3936         error(20,str);          /* invalid symbol name */
  3937         return FALSE;
  3938       } /* if */
  3939       assert(strlen(str)<=sNAMEMAX);
  3940       strcpy(symbolname,str);
  3941     } /* if */
  3942   } /* if */
  3943   /* check whether this is a function or a variable declaration */
  3944   if (!matchtoken('('))
  3945     return FALSE;
  3946   /* so it is a function, proceed */
  3947   funcline=fline;               /* save line at which the function is defined */
  3948   if (symbolname[0]==PUBLIC_CHAR) {
  3949     fpublic=TRUE;               /* implicitly public function */
  3950     if (stock)
  3951       error(42);                /* invalid combination of class specifiers */
  3952   } /* if */
  3953   sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
  3954   if (sym==NULL || (sym->usage & uNATIVE)!=0)
  3955     return TRUE;                /* it was recognized as a function declaration, but not as a valid one */
  3956   if (fpublic)
  3957     sym->usage|=uPUBLIC;
  3958   if (fstatic)
  3959     sym->fnumber=filenum;
  3960   /* if the function was used before being declared, and it has a tag for the
  3961    * result, add a third pass (as second "skimming" parse) because the function
  3962    * result may have been used with user-defined operators, which have now
  3963    * been incorrectly flagged (as the return tag was unknown at the time of
  3964    * the call)
  3965    */
  3966   if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) {
  3967     int curstatus=sc_status;
  3968     sc_status=statWRITE;  /* temporarily set status to WRITE, so the warning isn't blocked */
  3969 #if 0 /* SourceMod - silly, should be removed in first pass, so removed */
  3970     error(208);
  3971 #endif
  3972     sc_status=curstatus;
  3973     sc_reparse=TRUE;      /* must add another pass to "initial scan" phase */
  3974   } /* if */
  3975 #if 0	/* Not used for SourceMod */
  3976   /* we want public functions to be explicitly prototyped, as they are called
  3977    * from the outside
  3978    */
  3979   if (fpublic && (sym->usage & uFORWARD)==0)
  3980     error(235,symbolname);
  3981 #endif
  3982   /* declare all arguments */
  3983   argcnt=declargs(sym,TRUE);
  3984   opererror=!operatoradjust(opertok,sym,symbolname,tag);
  3985   if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) {
  3986     if (argcnt>0)
  3987       error(5);         /* "main()" and "entry()" functions may not have any arguments */
  3988     sym->usage|=uREAD;  /* "main()" is the program's entry point: always used */
  3989   } /* if */
  3990   state_id=getstates(symbolname);
  3991   if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0))
  3992     error(82);          /* operators may not have states, main() may neither */
  3993   attachstatelist(sym,state_id);
  3994   /* "declargs()" found the ")"; if a ";" appears after this, it was a
  3995    * prototype */
  3996   if (matchtoken(';')) {
  3997     sym->usage|=uFORWARD;
  3998     if (!sc_needsemicolon)
  3999       error(10);       /* old style prototypes used with optional semicolumns */
  4000     delete_symbols(&loctab,0,TRUE,TRUE);  /* prototype is done; forget everything */
  4001     return TRUE;
  4002   } /* if */
  4003   /* so it is not a prototype, proceed */
  4004   /* if this is a function that is not referred to (this can only be detected
  4005    * in the second stage), shut code generation off */
  4006   if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) {
  4007     sc_status=statSKIP;
  4008     cidx=code_idx;
  4009     glbdecl=glb_declared;
  4010   } /* if */
  4011   if ((sym->flags & flgDEPRECATED)!=0) {
  4012     char *ptr= (sym->documentation!=NULL) ? sym->documentation : "";
  4013     error(234,symbolname,ptr);  /* deprecated (probably a public function) */
  4014   } /* if */
  4015   begcseg();
  4016   sym->usage|=uDEFINE;  /* set the definition flag */
  4017   if (stock)
  4018     sym->usage|=uSTOCK;
  4019   if (opertok!=0 && opererror)
  4020     sym->usage &= ~uDEFINE;
  4021   /* if the function has states, dump the label to the start of the function */
  4022   if (state_id!=0) {
  4023     constvalue *ptr=sym->states->next;
  4024     while (ptr!=NULL) {
  4025       assert(sc_status!=statWRITE || strlen(ptr->name)>0);
  4026       if (ptr->index==state_id) {
  4027         setlabel((int)strtol(ptr->name,NULL,16));
  4028         break;
  4029       } /* if */
  4030       ptr=ptr->next;
  4031     } /* while */
  4032   } /* if */
  4033   startfunc(sym->name); /* creates stack frame */
  4034   insert_dbgline(funcline);
  4035   setline(FALSE);
  4036   if (sc_alignnext) {
  4037     alignframe(sc_dataalign);
  4038     sc_alignnext=FALSE;
  4039   } /* if */
  4040   declared=0;           /* number of local cells */
  4041   resetstacklist();
  4042   resetheaplist();
  4043   rettype=(sym->usage & uRETVALUE);      /* set "return type" variable */
  4044   curfunc=sym;
  4045   define_args();        /* add the symbolic info for the function arguments */
  4046   #if !defined SC_LIGHT
  4047     if (matchtoken('{')) {
  4048       lexpush();
  4049     } else {
  4050       /* Insert a separator so that comments following the statement will not
  4051        * be attached to this function; they should be attached to the next
  4052        * function. This is not a problem for functions having a compound block,
  4053        * because the closing brace is an explicit "end token" for the function.
  4054        * With single statement functions, the preprocessor may overread the
  4055        * source code before the parser determines an "end of statement".
  4056        */
  4057       insert_docstring_separator();
  4058     } /* if */
  4059   #endif
  4060   sc_curstates=state_id;/* set state id, for accessing global state variables */
  4061   statement(NULL,FALSE);
  4062   sc_curstates=0;
  4063   if ((rettype & uRETVALUE)!=0)
  4064     sym->usage|=uRETVALUE;
  4065   if (declared!=0) {
  4066     /* This happens only in a very special (and useless) case, where a function
  4067      * has only a single statement in its body (no compound block) and that
  4068      * statement declares a new variable
  4069      */
  4070     popstacklist(1);
  4071     declared=0;
  4072   } /* if */
  4073   if ((lastst!=tRETURN) && (lastst!=tGOTO)){
  4074     ldconst(0,sPRI);
  4075     ffret(strcmp(sym->name,uENTRYFUNC)!=0);
  4076     if ((sym->usage & uRETVALUE)!=0) {
  4077       char symname[2*sNAMEMAX+16];  /* allow space for user defined operators */
  4078       funcdisplayname(symname,sym->name);
  4079       error(209,symname);       /* function should return a value */
  4080     } /* if */
  4081   } /* if */
  4082   endfunc();
  4083   sym->codeaddr=code_idx;
  4084   sc_attachdocumentation(sym);  /* attach collected documenation to the function */
  4085   if (litidx) {                 /* if there are literals defined */
  4086     glb_declared+=litidx;
  4087     begdseg();                  /* flip to DATA segment */
  4088     dumplits();                 /* dump literal strings */
  4089     litidx=0;
  4090   } /* if */
  4091   testsymbols(&loctab,0,TRUE,TRUE);     /* test for unused arguments and labels */
  4092   delete_symbols(&loctab,0,TRUE,TRUE);  /* clear local variables queue */
  4093   assert(loctab.next==NULL);
  4094   curfunc=NULL;
  4095   if (sc_status==statSKIP) {
  4096     sc_status=statWRITE;
  4097     code_idx=cidx;
  4098     glb_declared=glbdecl;
  4099   } /* if */
  4100   return TRUE;
  4101 }
  4102 
  4103 static int argcompare(arginfo *a1,arginfo *a2)
  4104 {
  4105   int result,level,i;
  4106 
  4107 #if 0	/* SourceMod uses case insensitive args for forwards */
  4108   result= strcmp(a1->name,a2->name)==0;     /* name */
  4109 #else
  4110   result=1;
  4111 #endif
  4112   if (result)
  4113     result= a1->ident==a2->ident;           /* type/class */
  4114   if (result)
  4115     result= a1->usage==a2->usage;           /* "const" flag */
  4116   if (result)
  4117     result= a1->numtags==a2->numtags;       /* tags (number and names) */
  4118   for (i=0; result && i<a1->numtags; i++)
  4119     result= a1->tags[i]==a2->tags[i];
  4120   if (result)
  4121     result= a1->numdim==a2->numdim;         /* array dimensions & index tags */
  4122   for (level=0; result && level<a1->numdim; level++)
  4123     result= a1->dim[level]==a2->dim[level];
  4124   for (level=0; result && level<a1->numdim; level++)
  4125     result= a1->idxtag[level]==a2->idxtag[level];
  4126   if (result)
  4127     result= a1->hasdefault==a2->hasdefault; /* availability of default value */
  4128   if (a1->hasdefault) {
  4129     if (a1->ident==iREFARRAY) {
  4130       if (result)
  4131         result= a1->defvalue.array.size==a2->defvalue.array.size;
  4132       if (result)
  4133         result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize;
  4134       /* ??? should also check contents of the default array (these troubles
  4135        * go away in a 2-pass compiler that forbids double declarations, but
  4136        * Pawn currently does not forbid them) */
  4137     } else {
  4138       if (result) {
  4139         if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0 || (a1->hasdefault & uCOUNTOF)!=0)
  4140           result= a1->hasdefault==a2->hasdefault
  4141                   && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0
  4142                   && a1->defvalue.size.level==a2->defvalue.size.level;
  4143         else
  4144           result= a1->defvalue.val==a2->defvalue.val;
  4145       } /* if */
  4146     } /* if */
  4147     if (result)
  4148       result= a1->defvalue_tag==a2->defvalue_tag;
  4149   } /* if */
  4150   return result;
  4151 }
  4152 
  4153 /*  declargs()
  4154  *
  4155  *  This routine adds an entry in the local symbol table for each argument
  4156  *  found in the argument list. It returns the number of arguments.
  4157  */
  4158 static int declargs(symbol *sym,int chkshadow)
  4159 {
  4160   #define MAXTAGS 16
  4161   char *ptr;
  4162   int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags;
  4163   cell val;
  4164   arginfo arg, *arglist;
  4165   char name[sNAMEMAX+1];
  4166   int ident,fpublic,fconst;
  4167   int idx;
  4168 
  4169   /* if the function is already defined earlier, get the number of arguments
  4170    * of the existing definition
  4171    */
  4172   oldargcnt=0;
  4173   if ((sym->usage & uPROTOTYPED)!=0)
  4174     while (sym->dim.arglist[oldargcnt].ident!=0)
  4175       oldargcnt++;
  4176   argcnt=0;                             /* zero aruments up to now */
  4177   ident=iVARIABLE;
  4178   numtags=0;
  4179   fconst=FALSE;
  4180   fpublic= (sym->usage & (uPUBLIC|uSTOCK))!=0;
  4181   /* the '(' parantheses has already been parsed */
  4182   if (!matchtoken(')')){
  4183     do {                                /* there are arguments; process them */
  4184       /* any legal name increases argument count (and stack offset) */
  4185       tok=lex(&val,&ptr);
  4186       switch (tok) {
  4187       case 0:
  4188         /* nothing */
  4189         break;
  4190       case '&':
  4191         if (ident!=iVARIABLE || numtags>0)
  4192           error(1,"-identifier-","&");
  4193         ident=iREFERENCE;
  4194         break;
  4195       case tCONST:
  4196         if (ident!=iVARIABLE || numtags>0)
  4197           error(1,"-identifier-","const");
  4198         fconst=TRUE;
  4199         break;
  4200       case tLABEL:
  4201         if (numtags>0)
  4202           error(1,"-identifier-","-tagname-");
  4203         tags[0]=pc_addtag(ptr);
  4204         numtags=1;
  4205         break;
  4206       case '{':
  4207         if (numtags>0)
  4208           error(1,"-identifier-","-tagname-");
  4209         numtags=0;
  4210         while (numtags<MAXTAGS) {
  4211           if (!matchtoken('_') && !needtoken(tSYMBOL))
  4212             break;
  4213           tokeninfo(&val,&ptr);
  4214           tags[numtags++]=pc_addtag(ptr);
  4215           if (matchtoken('}'))
  4216             break;
  4217           needtoken(',');
  4218         } /* for */
  4219         needtoken(':');
  4220         tok=tLABEL;     /* for outer loop: flag that we have seen a tagname */
  4221         break;
  4222       case tSYMBOL:
  4223         if (argcnt>=sMAXARGS)
  4224           error(45);                    /* too many function arguments */
  4225         strcpy(name,ptr);               /* save symbol name */
  4226         if (name[0]==PUBLIC_CHAR)
  4227           error(56,name);               /* function arguments cannot be public */
  4228         if (numtags==0)
  4229           tags[numtags++]=0;            /* default tag */
  4230         /* Stack layout:
  4231          *   base + 0*sizeof(cell)  == previous "base"
  4232          *   base + 1*sizeof(cell)  == function return address
  4233          *   base + 2*sizeof(cell)  == number of arguments
  4234          *   base + 3*sizeof(cell)  == first argument of the function
  4235          * So the offset of each argument is "(argcnt+3) * sizeof(cell)".
  4236          */
  4237         doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg);
  4238         /* :TODO: fix this so stocks that are func pointers can't have default arguments? */
  4239         if ((sym->usage & uPUBLIC) && arg.hasdefault)
  4240           error(59,name);       /* arguments of a public function may not have a default value */
  4241         if ((sym->usage & uPROTOTYPED)==0) {
  4242           /* redimension the argument list, add the entry */
  4243           sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
  4244           if (sym->dim.arglist==0)
  4245             error(123);                 /* insufficient memory */
  4246           memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo));  /* keep the list terminated */
  4247           sym->dim.arglist[argcnt]=arg;
  4248         } else {
  4249           /* check the argument with the earlier definition */
  4250           if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg))
  4251             error(25);          /* function definition does not match prototype */
  4252           /* may need to free default array argument and the tag list */
  4253           if (arg.ident==iREFARRAY && arg.hasdefault)
  4254             free(arg.defvalue.array.data);
  4255           else if (arg.ident==iVARIABLE
  4256                    && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0) || (arg.hasdefault & uCOUNTOF)!=0)
  4257             free(arg.defvalue.size.symname);
  4258           free(arg.tags);
  4259         } /* if */
  4260         argcnt++;
  4261         ident=iVARIABLE;
  4262         numtags=0;
  4263         fconst=FALSE;
  4264         break;
  4265       case tELLIPS:
  4266         if (ident!=iVARIABLE)
  4267           error(10);                    /* illegal function or declaration */
  4268         if (numtags==0)
  4269           tags[numtags++]=0;            /* default tag */
  4270         if ((sym->usage & uPROTOTYPED)==0) {
  4271           /* redimension the argument list, add the entry iVARARGS */
  4272           sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo));
  4273           if (sym->dim.arglist==0)
  4274             error(123);                 /* insufficient memory */
  4275           memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo));  /* keep the list terminated */
  4276           sym->dim.arglist[argcnt].ident=iVARARGS;
  4277           sym->dim.arglist[argcnt].hasdefault=FALSE;
  4278           sym->dim.arglist[argcnt].defvalue.val=0;
  4279           sym->dim.arglist[argcnt].defvalue_tag=0;
  4280           sym->dim.arglist[argcnt].numtags=numtags;
  4281           sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]);
  4282           if (sym->dim.arglist[argcnt].tags==NULL)
  4283             error(123);                 /* insufficient memory */
  4284           memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]);
  4285         } else {
  4286           if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS)
  4287             error(25);          /* function definition does not match prototype */
  4288         } /* if */
  4289         argcnt++;
  4290         break;
  4291       default:
  4292         error(10);                      /* illegal function or declaration */
  4293       } /* switch */
  4294     } while (tok=='&' || tok==tLABEL || tok==tCONST
  4295              || tok!=tELLIPS && matchtoken(',')); /* more? */
  4296     /* if the next token is not ",", it should be ")" */
  4297     needtoken(')');
  4298   } /* if */
  4299   /* resolve any "sizeof" arguments (now that all arguments are known) */
  4300   assert(sym->dim.arglist!=NULL);
  4301   arglist=sym->dim.arglist;
  4302   for (idx=0; idx<argcnt && arglist[idx].ident!=0; idx++) {
  4303     if ((arglist[idx].hasdefault & uSIZEOF)!=0 
  4304          || (arglist[idx].hasdefault & uTAGOF)!=0 
  4305          || (arglist[idx].hasdefault & uCOUNTOF)!=0) {
  4306       int altidx;
  4307       /* Find the argument with the name mentioned after the "sizeof". Note
  4308        * that we cannot use findloc here because we need the arginfo struct,
  4309        * not the symbol.
  4310        */
  4311       ptr=arglist[idx].defvalue.size.symname;
  4312       assert(ptr!=NULL);
  4313       for (altidx=0; altidx<argcnt && strcmp(ptr,arglist[altidx].name)!=0; altidx++)
  4314         /* nothing */;
  4315       if (altidx>=argcnt) {
  4316         error(17,ptr);                  /* undefined symbol */
  4317       } else {
  4318         assert(arglist[idx].defvalue.size.symname!=NULL);
  4319         /* check the level against the number of dimensions */
  4320         if (arglist[idx].defvalue.size.level>0
  4321             && arglist[idx].defvalue.size.level>=arglist[altidx].numdim)
  4322           error(28,arglist[idx].name);  /* invalid subscript */
  4323         /* check the type of the argument whose size to take; for a iVARIABLE
  4324          * or a iREFERENCE, this is always 1 (so the code is redundant)
  4325          */
  4326         assert(arglist[altidx].ident!=iVARARGS);
  4327         if (arglist[altidx].ident!=iREFARRAY 
  4328             && (((arglist[idx].hasdefault & uSIZEOF)!=0)
  4329                   || (arglist[idx].hasdefault & uCOUNTOF)!=0)) {
  4330           if ((arglist[idx].hasdefault & uTAGOF)!=0) {
  4331             error(81,arglist[idx].name);  /* cannot take "tagof" an indexed array */
  4332           } else {
  4333             assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE);
  4334             error(223,ptr);             /* redundant sizeof */
  4335           } /* if */
  4336         } /* if */
  4337       } /* if */
  4338     } /* if */
  4339   } /* for */
  4340 
  4341   sym->usage|=uPROTOTYPED;
  4342   errorset(sRESET,0);           /* reset error flag (clear the "panic mode")*/
  4343   return argcnt;
  4344 }
  4345 
  4346 /*  doarg       - declare one argument type
  4347  *
  4348  *  this routine is called from "declargs()" and adds an entry in the local
  4349  *  symbol table for one argument.
  4350  *
  4351  *  "fpublic" indicates whether the function for this argument list is public.
  4352  *  The arguments themselves are never public.
  4353  */
  4354 static void doarg(char *name,int ident,int offset,int tags[],int numtags,
  4355                   int fpublic,int fconst,int chkshadow,arginfo *arg)
  4356 {
  4357   symbol *argsym;
  4358   constvalue *enumroot;
  4359   cell size;
  4360   int slength=0;
  4361 
  4362   strcpy(arg->name,name);
  4363   arg->hasdefault=FALSE;        /* preset (most common case) */
  4364   arg->defvalue.val=0;          /* clear */
  4365   arg->defvalue_tag=0;
  4366   arg->numdim=0;
  4367   if (matchtoken('[')) {
  4368     if (ident==iREFERENCE)
  4369       error(67,name);           /* illegal declaration ("&name[]" is unsupported) */
  4370     do {
  4371       if (arg->numdim == sDIMEN_MAX) {
  4372         error(53);              /* exceeding maximum number of dimensions */
  4373         return;
  4374       } /* if */
  4375       size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */
  4376       #if INT_MAX < LONG_MAX
  4377         if (size > INT_MAX)
  4378           error(125);           /* overflow, exceeding capacity */
  4379       #endif
  4380       arg->dim[arg->numdim]=(int)size;
  4381       arg->numdim+=1;
  4382     } while (matchtoken('['));
  4383     ident=iREFARRAY;            /* "reference to array" (is a pointer) */
  4384     if (checktag(tags, numtags, pc_tag_string)) {
  4385       slength = arg->dim[arg->numdim - 1];
  4386       arg->dim[arg->numdim - 1] = (size + sizeof(cell) - 1) / sizeof(cell);
  4387     }
  4388     if (matchtoken('=')) {
  4389       lexpush();                /* initials() needs the "=" token again */
  4390       assert(litidx==0);        /* at the start of a function, this is reset */
  4391       assert(numtags>0);
  4392       initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot);
  4393       assert(size>=litidx);
  4394       /* allocate memory to hold the initial values */
  4395       arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell));
  4396       if (arg->defvalue.array.data!=NULL) {
  4397         int i;
  4398         memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell));
  4399         arg->hasdefault=TRUE;   /* argument has default value */
  4400         arg->defvalue.array.size=litidx;
  4401         arg->defvalue.array.addr=-1;
  4402         /* calulate size to reserve on the heap */
  4403         arg->defvalue.array.arraysize=1;
  4404         for (i=0; i<arg->numdim; i++)
  4405           arg->defvalue.array.arraysize*=arg->dim[i];
  4406         if (arg->defvalue.array.arraysize < arg->defvalue.array.size)
  4407           arg->defvalue.array.arraysize = arg->defvalue.array.size;
  4408       } /* if */
  4409       litidx=0;                 /* reset */
  4410     } /* if */
  4411   } else {
  4412     if (matchtoken('=')) {
  4413       unsigned char size_tag_token;
  4414       assert(ident==iVARIABLE || ident==iREFERENCE);
  4415       arg->hasdefault=TRUE;     /* argument has a default value */
  4416       size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0);
  4417       if (size_tag_token==0)
  4418         size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0);
  4419       if (size_tag_token==0)
  4420         size_tag_token=(unsigned char)(matchtoken(tCELLSOF) ? uCOUNTOF : 0);
  4421       if (size_tag_token!=0) {
  4422         int paranthese;
  4423         if (ident==iREFERENCE)
  4424           error(66,name);       /* argument may not be a reference */
  4425         paranthese=0;
  4426         while (matchtoken('('))
  4427           paranthese++;
  4428         if (needtoken(tSYMBOL)) {
  4429           /* save the name of the argument whose size id to take */
  4430           char *name;
  4431           cell val;
  4432           tokeninfo(&val,&name);
  4433           if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL)
  4434             error(123);         /* insufficient memory */
  4435           arg->defvalue.size.level=0;
  4436           if (size_tag_token==uSIZEOF || size_tag_token==uCOUNTOF) {
  4437             while (matchtoken('[')) {
  4438               arg->defvalue.size.level+=(short)1;
  4439               needtoken(']');
  4440             } /* while */
  4441           } /* if */
  4442           if (ident==iVARIABLE) /* make sure we set this only if not a reference */
  4443             arg->hasdefault |= size_tag_token;  /* uSIZEOF or uTAGOF */
  4444         } /* if */
  4445         while (paranthese--)
  4446           needtoken(')');
  4447       } else {
  4448         constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL);
  4449         assert(numtags>0);
  4450         if (!matchtag(tags[0],arg->defvalue_tag,TRUE))
  4451           error(213);           /* tagname mismatch */
  4452       } /* if */
  4453     } /* if */
  4454   } /* if */
  4455   arg->ident=(char)ident;
  4456   arg->usage=(char)(fconst ? uCONST : 0);
  4457   arg->numtags=numtags;
  4458   arg->tags=(int*)malloc(numtags*sizeof tags[0]);
  4459   if (arg->tags==NULL)
  4460     error(123);                 /* insufficient memory */
  4461   memcpy(arg->tags,tags,numtags*sizeof tags[0]);
  4462   argsym=findloc(name);
  4463   if (argsym!=NULL) {
  4464     error(21,name);             /* symbol already defined */
  4465   } else {
  4466     if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN)
  4467       error(219,name);          /* variable shadows another symbol */
  4468     /* add details of type and address */
  4469     assert(numtags>0);
  4470     argsym=addvariable2(name,offset,ident,sLOCAL,tags[0],
  4471                        arg->dim,arg->numdim,arg->idxtag,slength);
  4472     argsym->compound=0;
  4473     if (ident==iREFERENCE)
  4474       argsym->usage|=uREAD;     /* because references are passed back */
  4475     if (fpublic)
  4476       argsym->usage|=uREAD;     /* arguments of public functions are always "used" */
  4477     if (fconst)
  4478       argsym->usage|=uCONST;
  4479   } /* if */
  4480 }
  4481 
  4482 static int count_referrers(symbol *entry)
  4483 {
  4484   int i,count;
  4485 
  4486   count=0;
  4487   for (i=0; i<entry->numrefers; i++)
  4488     if (entry->refer[i]!=NULL)
  4489       count++;
  4490   return count;
  4491 }
  4492 
  4493 #if !defined SC_LIGHT
  4494 static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue,
  4495                        char **outer_start,int *outer_length,
  4496                        char **inner_start,int *inner_length)
  4497 {
  4498   char *ptr,*inner_end;
  4499   int xmltag_len,xmlparam_len,xmlvalue_len;
  4500   int match;
  4501 
  4502   assert(source!=NULL);
  4503   assert(xmltag!=NULL);
  4504   assert(outer_start!=NULL);
  4505   assert(outer_length!=NULL);
  4506   assert(inner_start!=NULL);
  4507   assert(inner_length!=NULL);
  4508 
  4509   /* both NULL or both non-NULL */
  4510   assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL);
  4511 
  4512   xmltag_len=strlen(xmltag);
  4513   xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0;
  4514   xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0;
  4515   ptr=source;
  4516   /* find an opening '<' */
  4517   while ((ptr=strchr(ptr,'<'))!=NULL) {
  4518     *outer_start=ptr;           /* be optimistic... */
  4519     match=FALSE;                /* ...and pessimistic at the same time */
  4520     ptr++;                      /* skip '<' */
  4521     while (*ptr!='\0' && *ptr<=' ')
  4522       ptr++;                    /* skip white space */
  4523     if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
  4524       /* xml tag found, optionally check the parameter */
  4525       ptr+=xmltag_len;
  4526       while (*ptr!='\0' && *ptr<=' ')
  4527         ptr++;                  /* skip white space */
  4528       if (xmlparam!=NULL) {
  4529         if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) {
  4530           ptr+=xmlparam_len;
  4531           while (*ptr!='\0' && *ptr<=' ')
  4532             ptr++;              /* skip white space */
  4533           if (*ptr=='=') {
  4534             ptr++;              /* skip '=' */
  4535             while (*ptr!='\0' && *ptr<=' ')
  4536               ptr++;            /* skip white space */
  4537             if (*ptr=='"' || *ptr=='\'')
  4538               ptr++;            /* skip " or ' */
  4539             assert(xmlvalue!=NULL);
  4540             if (strncmp(ptr,xmlvalue,xmlvalue_len)==0
  4541                 && (*(ptr+xmlvalue_len)<=' '
  4542                     || *(ptr+xmlvalue_len)=='>'
  4543                     || *(ptr+xmlvalue_len)=='"'
  4544                     || *(ptr+xmlvalue_len)=='\''))
  4545               match=TRUE;       /* found it */
  4546           } /* if */
  4547         } /* if */
  4548       } else {
  4549         match=TRUE;             /* don't check the parameter */
  4550       } /* if */
  4551     } /* if */
  4552     if (match) {
  4553       /* now find the end of the opening tag */
  4554       while (*ptr!='\0' && *ptr!='>')
  4555         ptr++;
  4556       if (*ptr=='>')
  4557         ptr++;
  4558       while (*ptr!='\0' && *ptr<=' ')
  4559         ptr++;                  /* skip white space */
  4560       *inner_start=ptr;
  4561       /* find the start of the closing tag (assume no nesting) */
  4562       while ((ptr=strchr(ptr,'<'))!=NULL) {
  4563         inner_end=ptr;
  4564         ptr++;                  /* skip '<' */
  4565         while (*ptr!='\0' && *ptr<=' ')
  4566           ptr++;                /* skip white space */
  4567         if (*ptr=='/') {
  4568           ptr++;                /* skip / */
  4569           while (*ptr!='\0' && *ptr<=' ')
  4570             ptr++;              /* skip white space */
  4571           if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) {
  4572             /* find the end of the closing tag */
  4573             while (*ptr!='\0' && *ptr!='>')
  4574               ptr++;
  4575             if (*ptr=='>')
  4576               ptr++;
  4577             /* set the lengths of the inner and outer segment */
  4578             assert(*inner_start!=NULL);
  4579             *inner_length=(int)(inner_end-*inner_start);
  4580             assert(*outer_start!=NULL);
  4581             *outer_length=(int)(ptr-*outer_start);
  4582             break;              /* break out of the loop */
  4583           } /* if */
  4584         } /* if */
  4585       } /* while */
  4586       return TRUE;
  4587     } /* if */
  4588   } /* while */
  4589   return FALSE; /* not found */
  4590 }
  4591 
  4592 static char *xmlencode(char *dest,char *source)
  4593 {
  4594   char temp[2*sNAMEMAX+20],*ptr;
  4595 
  4596   /* replace < by &lt; and such; normally, such a symbol occurs at most once in
  4597    * a symbol name (e.g. "operator<")
  4598    */
  4599   ptr=temp;
  4600   while (*source!='\0') {
  4601     switch (*source) {
  4602     case '<':
  4603       strcpy(ptr,"&lt;");
  4604       ptr+=4;
  4605       break;
  4606     case '>':
  4607       strcpy(ptr,"&gt;");
  4608       ptr+=4;
  4609       break;
  4610     case '&':
  4611       strcpy(ptr,"&amp;");
  4612       ptr+=5;
  4613       break;
  4614     default:
  4615       *ptr++=*source;
  4616     } /* switch */
  4617     source++;
  4618   } /* while */
  4619   *ptr='\0';
  4620   strcpy(dest,temp);
  4621   return dest;
  4622 }
  4623 
  4624 static void make_report(symbol *root,FILE *log,char *sourcefile)
  4625 {
  4626   char symname[_MAX_PATH];
  4627   int i,arg;
  4628   symbol *sym,*ref;
  4629   constvalue *tagsym;
  4630   constvalue *enumroot;
  4631   char *ptr;
  4632 
  4633   /* adapt the installation directory */
  4634   strcpy(symname,sc_rootpath);
  4635   #if DIRSEP_CHAR=='\\'
  4636     while ((ptr=strchr(symname,':'))!=NULL)
  4637       *ptr='|';
  4638     while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL)
  4639       *ptr='/';
  4640   #endif
  4641 
  4642   /* the XML header */
  4643   fprintf(log,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
  4644   fprintf(log,"<?xml-stylesheet href=\"file:///%s/xml/pawndoc.xsl\" type=\"text/xsl\"?>\n",symname);
  4645   fprintf(log,"<doc source=\"%s\">\n",sourcefile);
  4646   ptr=strrchr(sourcefile,DIRSEP_CHAR);
  4647   if (ptr!=NULL)
  4648     ptr++;
  4649   else
  4650     ptr=sourcefile;
  4651   fprintf(log,"\t<assembly>\n\t\t<name>%s</name>\n\t</assembly>\n",ptr);
  4652 
  4653   /* attach the global documentation, if any */
  4654   if (sc_documentation!=NULL) {
  4655     fprintf(log,"\n\t<!-- general -->\n");
  4656     fprintf(log,"\t<general>\n\t\t");
  4657     fputs(sc_documentation,log);
  4658     fprintf(log,"\n\t</general>\n\n");
  4659   } /* if */
  4660 
  4661   /* use multiple passes to print constants variables and functions in
  4662    * separate sections
  4663    */
  4664   fprintf(log,"\t<members>\n");
  4665 
  4666   fprintf(log,"\n\t\t<!-- enumerations -->\n");
  4667   for (sym=root->next; sym!=NULL; sym=sym->next) {
  4668     if (sym->parent!=NULL)
  4669       continue;                 /* hierarchical data type */
  4670     assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
  4671            || sym->ident==iARRAY || sym->ident==iFUNCTN);
  4672     if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0)
  4673       continue;
  4674     if ((sym->usage & uREAD)==0)
  4675       continue;
  4676     fprintf(log,"\t\t<member name=\"T:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
  4677     if (sym->tag!=0) {
  4678       tagsym=find_tag_byval(sym->tag);
  4679       assert(tagsym!=NULL);
  4680       fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
  4681     } /* if */
  4682     /* browse through all fields */
  4683     if ((enumroot=sym->dim.enumlist)!=NULL) {
  4684       enumroot=enumroot->next;  /* skip root */
  4685       while (enumroot!=NULL) {
  4686         fprintf(log,"\t\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,enumroot->name),enumroot->value);
  4687         /* find the constant with this name and get the tag */
  4688         ref=findglb(enumroot->name,sGLOBAL);
  4689         if (ref!=NULL) {
  4690           if (ref->x.tags.index!=0) {
  4691             tagsym=find_tag_byval(ref->x.tags.index);
  4692             assert(tagsym!=NULL);
  4693             fprintf(log,"\t\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
  4694           } /* if */
  4695           if (ref->dim.array.length!=1)
  4696             fprintf(log,"\t\t\t\t<size value=\"%ld\"/>\n",(long)ref->dim.array.length);
  4697         } /* if */
  4698         fprintf(log,"\t\t\t</member>\n");
  4699         enumroot=enumroot->next;
  4700       } /* while */
  4701     } /* if */
  4702     assert(sym->refer!=NULL);
  4703     for (i=0; i<sym->numrefers; i++) {
  4704       if ((ref=sym->refer[i])!=NULL)
  4705         fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
  4706     } /* for */
  4707     if (sym->documentation!=NULL)
  4708       fprintf(log,"\t\t\t%s\n",sym->documentation);
  4709     fprintf(log,"\t\t</member>\n");
  4710   } /* for */
  4711 
  4712   fprintf(log,"\n\t\t<!-- constants -->\n");
  4713   for (sym=root->next; sym!=NULL; sym=sym->next) {
  4714     if (sym->parent!=NULL)
  4715       continue;                 /* hierarchical data type */
  4716     assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE
  4717            || sym->ident==iARRAY || sym->ident==iFUNCTN);
  4718     if (sym->ident!=iCONSTEXPR)
  4719       continue;
  4720     if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0)
  4721       continue;
  4722     fprintf(log,"\t\t<member name=\"C:%s\" value=\"%ld\">\n",funcdisplayname(symname,sym->name),sym->addr);
  4723     if (sym->tag!=0) {
  4724       tagsym=find_tag_byval(sym->tag);
  4725       assert(tagsym!=NULL);
  4726       fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
  4727     } /* if */
  4728     assert(sym->refer!=NULL);
  4729     for (i=0; i<sym->numrefers; i++) {
  4730       if ((ref=sym->refer[i])!=NULL)
  4731         fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
  4732     } /* for */
  4733     if (sym->documentation!=NULL)
  4734       fprintf(log,"\t\t\t%s\n",sym->documentation);
  4735     fprintf(log,"\t\t</member>\n");
  4736   } /* for */
  4737 
  4738   fprintf(log,"\n\t\t<!-- variables -->\n");
  4739   for (sym=root->next; sym!=NULL; sym=sym->next) {
  4740     if (sym->parent!=NULL)
  4741       continue;                 /* hierarchical data type */
  4742     if (sym->ident!=iVARIABLE && sym->ident!=iARRAY)
  4743       continue;
  4744     fprintf(log,"\t\t<member name=\"F:%s\">\n",funcdisplayname(symname,sym->name));
  4745     if (sym->tag!=0) {
  4746       tagsym=find_tag_byval(sym->tag);
  4747       assert(tagsym!=NULL);
  4748       fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
  4749     } /* if */
  4750     assert(sym->refer!=NULL);
  4751     if ((sym->usage & uPUBLIC)!=0)
  4752       fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
  4753     for (i=0; i<sym->numrefers; i++) {
  4754       if ((ref=sym->refer[i])!=NULL)
  4755         fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
  4756     } /* for */
  4757     if (sym->documentation!=NULL)
  4758       fprintf(log,"\t\t\t%s\n",sym->documentation);
  4759     fprintf(log,"\t\t</member>\n");
  4760   } /* for */
  4761 
  4762   fprintf(log,"\n\t\t<!-- functions -->\n");
  4763   for (sym=root->next; sym!=NULL; sym=sym->next) {
  4764     if (sym->parent!=NULL)
  4765       continue;                 /* hierarchical data type */
  4766     if (sym->ident!=iFUNCTN)
  4767       continue;
  4768     if ((sym->usage & (uREAD | uNATIVE))==uNATIVE)
  4769       continue;                 /* unused native function */
  4770     funcdisplayname(symname,sym->name);
  4771     xmlencode(symname,symname);
  4772     fprintf(log,"\t\t<member name=\"M:%s\" syntax=\"%s(",symname,symname);
  4773     /* print only the names of the parameters between the parentheses */
  4774     assert(sym->dim.arglist!=NULL);
  4775     for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
  4776       int dim;
  4777       if (arg>0)
  4778         fprintf(log,", ");
  4779       switch (sym->dim.arglist[arg].ident) {
  4780       case iVARIABLE:
  4781         fprintf(log,"%s",sym->dim.arglist[arg].name);
  4782         break;
  4783       case iREFERENCE:
  4784         fprintf(log,"&amp;%s",sym->dim.arglist[arg].name);
  4785         break;
  4786       case iREFARRAY:
  4787         fprintf(log,"%s",sym->dim.arglist[arg].name);
  4788         for (dim=0; dim<sym->dim.arglist[arg].numdim;dim++)
  4789           fprintf(log,"[]");
  4790         break;
  4791       case iVARARGS:
  4792         fprintf(log,"...");
  4793         break;
  4794       } /* switch */
  4795     } /* for */
  4796     /* ??? should also print an "array return" size */
  4797     fprintf(log,")\">\n");
  4798     if (sym->tag!=0) {
  4799       tagsym=find_tag_byval(sym->tag);
  4800       assert(tagsym!=NULL);
  4801       fprintf(log,"\t\t\t<tagname value=\"%s\"/>\n",tagsym->name);
  4802     } /* if */
  4803     /* check whether this function is called from the outside */
  4804     if ((sym->usage & uNATIVE)!=0)
  4805       fprintf(log,"\t\t\t<attribute name=\"native\"/>\n");
  4806     if ((sym->usage & uPUBLIC)!=0)
  4807       fprintf(log,"\t\t\t<attribute name=\"public\"/>\n");
  4808     if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0)
  4809       fprintf(log,"\t\t\t<attribute name=\"entry\"/>\n");
  4810     if ((sym->usage & uNATIVE)==0)
  4811       fprintf(log,"\t\t\t<stacksize value=\"%ld\"/>\n",(long)sym->x.stacksize);
  4812     if (sym->states!=NULL) {
  4813       constvalue *stlist=sym->states->next;
  4814       assert(stlist!=NULL);     /* there should be at least one state item */
  4815       while (stlist!=NULL && stlist->index==-1)
  4816         stlist=stlist->next;
  4817       assert(stlist!=NULL);     /* state id should be found */
  4818       i=state_getfsa(stlist->index);
  4819       assert(i>=0);             /* automaton 0 exists */
  4820       stlist=automaton_findid(i);
  4821       assert(stlist!=NULL);     /* automaton should be found */
  4822       fprintf(log,"\t\t\t<automaton name=\"%s\"/>\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)");
  4823       //??? dump state decision table
  4824     } /* if */
  4825     assert(sym->refer!=NULL);
  4826     for (i=0; i<sym->numrefers; i++)
  4827       if ((ref=sym->refer[i])!=NULL)
  4828         fprintf(log,"\t\t\t<referrer name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
  4829     /* print all symbols that are required for this function to compile */
  4830     for (ref=root->next; ref!=NULL; ref=ref->next) {
  4831       if (ref==sym)
  4832         continue;
  4833       for (i=0; i<ref->numrefers; i++)
  4834         if (ref->refer[i]==sym)
  4835           fprintf(log,"\t\t\t<dependency name=\"%s\"/>\n",xmlencode(symname,funcdisplayname(symname,ref->name)));
  4836     } /* for */
  4837     /* print parameter list, with tag & const information, plus descriptions */
  4838     assert(sym->dim.arglist!=NULL);
  4839     for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) {
  4840       int dim,paraminfo;
  4841       char *outer_start,*inner_start;
  4842       int outer_length,inner_length;
  4843       if (sym->dim.arglist[arg].ident==iVARARGS)
  4844         fprintf(log,"\t\t\t<param name=\"...\">\n");
  4845       else
  4846         fprintf(log,"\t\t\t<param name=\"%s\">\n",sym->dim.arglist[arg].name);
  4847       /* print the tag name(s) for each parameter */
  4848       assert(sym->dim.arglist[arg].numtags>0);
  4849       assert(sym->dim.arglist[arg].tags!=NULL);
  4850       paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0)
  4851                 || sym->dim.arglist[arg].ident==iREFERENCE
  4852                 || sym->dim.arglist[arg].ident==iREFARRAY;
  4853       if (paraminfo)
  4854         fprintf(log,"\t\t\t\t<paraminfo>");
  4855       if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) {
  4856         assert(paraminfo);
  4857         if (sym->dim.arglist[arg].numtags>1)
  4858           fprintf(log," {");
  4859         for (i=0; i<sym->dim.arglist[arg].numtags; i++) {
  4860           if (i>0)
  4861             fprintf(log,",");
  4862           tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]);
  4863           assert(tagsym!=NULL);
  4864           fprintf(log,"%s",tagsym->name);
  4865         } /* for */
  4866         if (sym->dim.arglist[arg].numtags>1)
  4867           fprintf(log,"}");
  4868       } /* if */
  4869       switch (sym->dim.arglist[arg].ident) {
  4870       case iREFERENCE:
  4871         fprintf(log," &amp;");
  4872         break;
  4873       case iREFARRAY:
  4874         fprintf(log," ");
  4875         for (dim=0; dim<sym->dim.arglist[arg].numdim; dim++) {
  4876           if (sym->dim.arglist[arg].dim[dim]==0) {
  4877             fprintf(log,"[]");
  4878           } else {
  4879             //??? find index tag
  4880             fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]);
  4881           } /* if */
  4882         } /* for */
  4883         break;
  4884       } /* switch */
  4885       if (paraminfo)
  4886         fprintf(log," </paraminfo>\n");
  4887       /* print the user description of the parameter (parse through
  4888        * sym->documentation)
  4889        */
  4890       if (sym->documentation!=NULL
  4891           && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name,
  4892                          &outer_start, &outer_length, &inner_start, &inner_length))
  4893       {
  4894         char *tail;
  4895         fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start);
  4896         /* delete from documentation string */
  4897         tail=outer_start+outer_length;
  4898         memmove(outer_start,tail,strlen(tail)+1);
  4899       } /* if */
  4900       fprintf(log,"\t\t\t</param>\n");
  4901     } /* for */
  4902     if (sym->documentation!=NULL)
  4903       fprintf(log,"\t\t\t%s\n",sym->documentation);
  4904     fprintf(log,"\t\t</member>\n");
  4905   } /* for */
  4906 
  4907   fprintf(log,"\n\t</members>\n");
  4908   fprintf(log,"</doc>\n");
  4909 }
  4910 #endif
  4911 
  4912 /* Every symbol has a referrer list, that contains the functions that use
  4913  * the symbol. Now, if function "apple" is accessed by functions "banana" and
  4914  * "citron", but neither function "banana" nor "citron" are used by anyone
  4915  * else, then, by inference, function "apple" is not used either.
  4916  */
  4917 static void reduce_referrers(symbol *root)
  4918 {
  4919   int i,restart;
  4920   symbol *sym,*ref;
  4921 
  4922   do {
  4923     restart=0;
  4924     for (sym=root->next; sym!=NULL; sym=sym->next) {
  4925       if (sym->parent!=NULL)
  4926         continue;                 /* hierarchical data type */
  4927       if (sym->ident==iFUNCTN
  4928           && (sym->usage & uNATIVE)==0
  4929           && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0
  4930           && count_referrers(sym)==0)
  4931       {
  4932         sym->usage&=~(uREAD | uWRITTEN);  /* erase usage bits if there is no referrer */
  4933         /* find all symbols that are referred by this symbol */
  4934         for (ref=root->next; ref!=NULL; ref=ref->next) {
  4935           if (ref->parent!=NULL)
  4936             continue;             /* hierarchical data type */
  4937           assert(ref->refer!=NULL);
  4938           for (i=0; i<ref->numrefers && ref->refer[i]!=sym; i++)
  4939             /* nothing */;
  4940           if (i<ref->numrefers) {
  4941             assert(ref->refer[i]==sym);
  4942             ref->refer[i]=NULL;
  4943             restart++;
  4944           } /* if */
  4945         } /* for */
  4946       } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY)
  4947                  && (sym->usage & uPUBLIC)==0
  4948                  && sym->parent==NULL
  4949                  && count_referrers(sym)==0)
  4950       {
  4951         sym->usage&=~(uREAD | uWRITTEN);  /* erase usage bits if there is no referrer */
  4952       } /* if */
  4953     } /* for */
  4954     /* after removing a symbol, check whether more can be removed */
  4955   } while (restart>0);
  4956 }
  4957 
  4958 #if !defined SC_LIGHT
  4959 static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion)
  4960 {
  4961   long size,maxsize;
  4962   int i;
  4963 
  4964   assert(sym!=NULL);
  4965   assert(sym->ident==iFUNCTN);
  4966   assert((sym->usage & uNATIVE)==0);
  4967   assert(recursion!=NULL);
  4968 
  4969   maxsize=sym->x.stacksize;
  4970   for (i=0; i<sym->numrefers; i++) {
  4971     if (sym->refer[i]!=NULL) {
  4972       assert(sym->refer[i]->ident==iFUNCTN);
  4973       assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */
  4974       if (sym->refer[i]==sourcesym) {   /* recursion detection */
  4975         *recursion=1;
  4976         break;                          /* recursion was detected, quit loop */
  4977       } /* if */
  4978       size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion);
  4979       if (maxsize<size)
  4980         maxsize=size;
  4981     } /* if */
  4982   } /* for */
  4983 
  4984   if ((sym->usage & uPUBLIC)!=0) {
  4985     /* Find out how many parameters a public function has, then see if this
  4986      * is bigger than some maximum
  4987      */
  4988     arginfo *arg=sym->dim.arglist;
  4989     int count=0;
  4990     assert(arg!=0);
  4991     while (arg->ident!=0) {
  4992       count++;
  4993       arg++;
  4994     } /* while */
  4995     assert(pubfuncparams!=0);
  4996     if (count>*pubfuncparams)
  4997       *pubfuncparams=count;
  4998   } /* if */
  4999 
  5000   return maxsize+basesize;
  5001 }
  5002 
  5003 static long max_stacksize(symbol *root,int *recursion)
  5004 {
  5005   /* Loop over all non-native functions. For each function, loop
  5006    * over all of its referrers, accumulating the stack requirements.
  5007    * Detect (indirect) recursion with a "mark-and-sweep" algorithm.
  5008    * I (mis-)use the "compound" field of the symbol structure for
  5009    * the marker, as this field is unused for functions.
  5010    *
  5011    * Note that the stack is shared with the heap. A host application
  5012    * may "eat" cells from the heap as well, through amx_Allot(). The
  5013    * stack requirements are thus only an estimate.
  5014    */
  5015   long size,maxsize;
  5016   int maxparams;
  5017   symbol *sym;
  5018 
  5019   assert(root!=NULL);
  5020   assert(recursion!=NULL);
  5021   #if !defined NDEBUG
  5022     for (sym=root->next; sym!=NULL; sym=sym->next)
  5023       if (sym->ident==iFUNCTN)
  5024         assert(sym->compound==0);
  5025   #endif
  5026 
  5027   maxsize=0;
  5028   maxparams=0;
  5029   *recursion=0;         /* assume no recursion */
  5030   for (sym=root->next; sym!=NULL; sym=sym->next) {
  5031     /* drop out if this is not a user-implemented function */
  5032     if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0)
  5033       continue;
  5034     /* accumulate stack size for this symbol */
  5035     size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion);
  5036     assert(size>=0);
  5037     if (maxsize<size)
  5038       maxsize=size;
  5039   } /* for */
  5040 
  5041   maxsize++;                  /* +1 because a zero cell is always pushed on top
  5042                                * of the stack to catch stack overwrites */
  5043   return maxsize+(maxparams+1);/* +1 because # of parameters is always pushed on entry */
  5044 }
  5045 #endif
  5046 
  5047 /*  testsymbols - test for unused local or global variables
  5048  *
  5049  *  "Public" functions are excluded from the check, since these
  5050  *  may be exported to other object modules.
  5051  *  Labels are excluded from the check if the argument 'testlabs'
  5052  *  is 0. Thus, labels are not tested until the end of the function.
  5053  *  Constants may also be excluded (convenient for global constants).
  5054  *
  5055  *  When the nesting level drops below "level", the check stops.
  5056  *
  5057  *  The function returns whether there is an "entry" point for the file.
  5058  *  This flag will only be 1 when browsing the global symbol table.
  5059  */
  5060 static int testsymbols(symbol *root,int level,int testlabs,int testconst)
  5061 {
  5062   char symname[2*sNAMEMAX+16];
  5063   int entry=FALSE;
  5064 
  5065   symbol *sym=root->next;
  5066   while (sym!=NULL && sym->compound>=level) {
  5067     switch (sym->ident) {
  5068     case iLABEL:
  5069       if (testlabs) {
  5070         if ((sym->usage & uDEFINE)==0) {
  5071           error(19,sym->name);      /* not a label: ... */
  5072         } else if ((sym->usage & uREAD)==0) {
  5073           errorset(sSETPOS,sym->lnumber);
  5074           error(203,sym->name);     /* symbol isn't used: ... */
  5075         } /* if */
  5076       } /* if */
  5077       break;
  5078     case iFUNCTN:
  5079       if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) {
  5080         funcdisplayname(symname,sym->name);
  5081         if (strlen(symname)>0)
  5082           error(203,symname);       /* symbol isn't used ... (and not public/native/stock) */
  5083       } /* if */
  5084       if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0)
  5085         entry=TRUE;                 /* there is an entry point */
  5086       /* also mark the function to the debug information */
  5087       if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0)
  5088         insert_dbgsymbol(sym);
  5089       break;
  5090     case iCONSTEXPR:
  5091       if (testconst && (sym->usage & uREAD)==0) {
  5092         errorset(sSETPOS,sym->lnumber);
  5093         error(203,sym->name);       /* symbol isn't used: ... */
  5094       } /* if */
  5095       break;
  5096     default:
  5097       /* a variable */
  5098       if (sym->parent!=NULL)
  5099         break;                      /* hierarchical data type */
  5100       if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) {
  5101         errorset(sSETPOS,sym->lnumber);
  5102         error(203,sym->name);  /* symbol isn't used (and not stock) */
  5103       } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) {
  5104         errorset(sSETPOS,sym->lnumber);
  5105         error(204,sym->name);       /* value assigned to symbol is never used */
  5106 #if 0 // ??? not sure whether it is a good idea to force people use "const"
  5107       } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) {
  5108         errorset(sSETPOS,sym->lnumber);
  5109         error(214,sym->name);       /* make array argument "const" */
  5110 #endif
  5111       } /* if */
  5112       /* also mark the variable (local or global) to the debug information */
  5113       if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0)
  5114         insert_dbgsymbol(sym);
  5115     } /* if */
  5116     sym=sym->next;
  5117   } /* while */
  5118 
  5119   return entry;
  5120 }
  5121 
  5122 static cell calc_array_datasize(symbol *sym, cell *offset)
  5123 {
  5124   cell length;
  5125 
  5126   assert(sym!=NULL);
  5127   assert(sym->ident==iARRAY || sym->ident==iREFARRAY);
  5128   length=sym->dim.array.length;
  5129   if (sym->dim.array.level > 0) {
  5130     cell sublength=calc_array_datasize(finddepend(sym),offset);
  5131     if (offset!=NULL)
  5132       *offset=length*(*offset+sizeof(cell));
  5133     if (sublength>0)
  5134       length*=length*sublength;
  5135     else
  5136       length=0;
  5137   } else {
  5138     if (offset!=NULL)
  5139       *offset=0;
  5140   } /* if */
  5141   return length;
  5142 }
  5143 
  5144 static void destructsymbols(symbol *root,int level)
  5145 {
  5146   cell offset=0;
  5147   int savepri=FALSE;
  5148   symbol *sym=root->next;
  5149   while (sym!=NULL && sym->compound>=level) {
  5150     if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
  5151       char symbolname[16];
  5152       symbol *opsym;
  5153       cell elements;
  5154       /* check that the '~' operator is defined for this tag */
  5155       operator_symname(symbolname,"~",sym->tag,0,1,0);
  5156       if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) {
  5157         /* save PRI, in case of a return statment */
  5158         if (!savepri) {
  5159           pushreg(sPRI);        /* right-hand operand is in PRI */
  5160           savepri=TRUE;
  5161         } /* if */
  5162         /* if the variable is an array, get the number of elements */
  5163         if (sym->ident==iARRAY) {
  5164           elements=calc_array_datasize(sym,&offset);
  5165           /* "elements" can be zero when the variable is declared like
  5166            *    new mytag: myvar[2][] = { {1, 2}, {3, 4} }
  5167            * one should declare all dimensions!
  5168            */
  5169           if (elements==0)
  5170             error(46,sym->name);        /* array size is unknown */
  5171         } else {
  5172           elements=1;
  5173           offset=0;
  5174         } /* if */
  5175         pushval(elements);
  5176         /* call the '~' operator */
  5177         address(sym,sPRI);
  5178         addconst(offset);       /* add offset to array data to the address */
  5179         pushreg(sPRI);
  5180         pushval(2 /* *sizeof(cell)*/ );/* 2 parameters */
  5181         assert(opsym->ident==iFUNCTN);
  5182         ffcall(opsym,NULL,1);
  5183         if (sc_status!=statSKIP)
  5184           markusage(opsym,uREAD);   /* do not mark as "used" when this call itself is skipped */
  5185         if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL)
  5186           opsym->x.lib->value += 1; /* increment "usage count" of the library */
  5187       } /* if */
  5188     } /* if */
  5189     sym=sym->next;
  5190   } /* while */
  5191   /* restore PRI, if it was saved */
  5192   if (savepri)
  5193     popreg(sPRI);
  5194 }
  5195 
  5196 static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val,
  5197                                    int index)
  5198 {
  5199   constvalue *cur;
  5200 
  5201   if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL)
  5202     error(123);       /* insufficient memory (fatal error) */
  5203   memset(cur,0,sizeof(constvalue));
  5204   if (name!=NULL) {
  5205     assert(strlen(name)<sNAMEMAX);
  5206     strcpy(cur->name,name);
  5207   } /* if */
  5208   cur->value=val;
  5209   cur->index=index;
  5210   cur->next=next;
  5211   prev->next=cur;
  5212   return cur;
  5213 }
  5214 
  5215 SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index)
  5216 {
  5217   constvalue *cur,*prev;
  5218 
  5219   /* find the end of the constant table */
  5220   for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next)
  5221     /* nothing */;
  5222   return insert_constval(prev,NULL,name,val,index);
  5223 }
  5224 
  5225 SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index)
  5226 {
  5227   constvalue *ptr = table->next;
  5228 
  5229   while (ptr!=NULL) {
  5230     if (strcmp(name,ptr->name)==0 && ptr->index==index)
  5231       return ptr;
  5232     ptr=ptr->next;
  5233   } /* while */
  5234   return NULL;
  5235 }
  5236 
  5237 static constvalue *find_constval_byval(constvalue *table,cell val)
  5238 {
  5239   constvalue *ptr = table->next;
  5240 
  5241   while (ptr!=NULL) {
  5242     if (ptr->value==val)
  5243       return ptr;
  5244     ptr=ptr->next;
  5245   } /* while */
  5246   return NULL;
  5247 }
  5248 
  5249 #if 0   /* never used */
  5250 static int delete_constval(constvalue *table,char *name)
  5251 {
  5252   constvalue *prev = table;
  5253   constvalue *cur = prev->next;
  5254 
  5255   while (cur!=NULL) {
  5256     if (strcmp(name,cur->name)==0) {
  5257       prev->next=cur->next;
  5258       free(cur);
  5259       return TRUE;
  5260     } /* if */
  5261     prev=cur;
  5262     cur=cur->next;
  5263   } /* while */
  5264   return FALSE;
  5265 }
  5266 #endif
  5267 
  5268 SC_FUNC void delete_consttable(constvalue *table)
  5269 {
  5270   constvalue *cur=table->next, *next;
  5271 
  5272   while (cur!=NULL) {
  5273     next=cur->next;
  5274     free(cur);
  5275     cur=next;
  5276   } /* while */
  5277   memset(table,0,sizeof(constvalue));
  5278 }
  5279 
  5280 /*  add_constant
  5281  *
  5282  *  Adds a symbol to the symbol table. Returns NULL on failure.
  5283  */
  5284 SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag)
  5285 {
  5286   symbol *sym;
  5287 
  5288   /* Test whether a global or local symbol with the same name exists. Since
  5289    * constants are stored in the symbols table, this also finds previously
  5290    * defind constants. */
  5291   sym=findglb(name,sSTATEVAR);
  5292   if (!sym)
  5293     sym=findloc(name);
  5294   if (sym) {
  5295     int redef=0;
  5296     if (sym->ident!=iCONSTEXPR)
  5297       redef=1;                  /* redefinition a function/variable to a constant is not allowed */
  5298     if ((sym->usage & uENUMFIELD)!=0) {
  5299       /* enum field, special case if it has a different tag and the new symbol is also an enum field */
  5300       constvalue *tagid;
  5301       symbol *tagsym;
  5302       if (sym->tag==tag)
  5303         redef=1;                /* enumeration field is redefined (same tag) */
  5304       tagid=find_tag_byval(tag);
  5305       if (tagid==NULL) {
  5306         redef=1;                /* new constant does not have a tag */
  5307       } else {
  5308         tagsym=findconst(tagid->name,NULL);
  5309         if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0)
  5310           redef=1;              /* new constant is not an enumeration field */
  5311       } /* if */
  5312       /* in this particular case (enumeration field that is part of a different
  5313        * enum, and non-conflicting with plain constants) we want to be able to
  5314        * redefine it
  5315        */
  5316       if (!redef)
  5317         goto redef_enumfield;
  5318     } else if (sym->tag!=tag) {
  5319       redef=1;                  /* redefinition of a constant (non-enum) to a different tag is not allowed */
  5320     } /* if */
  5321     if (redef) {
  5322       error(21,name);           /* symbol already defined */
  5323       return NULL;
  5324     } else if (sym->addr!=val) {
  5325       error(201,name);          /* redefinition of constant (different value) */
  5326       sym->addr=val;            /* set new value */
  5327     } /* if */
  5328     /* silently ignore redefinitions of constants with the same value & tag */
  5329     return sym;
  5330   } /* if */
  5331 
  5332   /* constant doesn't exist yet (or is allowed to be redefined) */
  5333 redef_enumfield:
  5334   sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE);
  5335   assert(sym!=NULL);            /* fatal error 103 must be given on error */
  5336   if (sc_status == statIDLE)
  5337     sym->usage |= uPREDEF;
  5338   return sym;
  5339 }
  5340 
  5341 /*  statement           - The Statement Parser
  5342  *
  5343  *  This routine is called whenever the parser needs to know what statement
  5344  *  it encounters (i.e. whenever program syntax requires a statement).
  5345  */
  5346 static void statement(int *lastindent,int allow_decl)
  5347 {
  5348   int tok,save;
  5349   cell val;
  5350   char *st;
  5351 
  5352   if (!freading) {
  5353     error(36);                  /* empty statement */
  5354     return;
  5355   } /* if */
  5356   errorset(sRESET,0);
  5357 
  5358   tok=lex(&val,&st);
  5359   if (tok!='{') {
  5360     insert_dbgline(fline);
  5361     setline(TRUE);
  5362   } /* if */
  5363   /* lex() has set stmtindent */
  5364   if (lastindent!=NULL && tok!=tLABEL) {
  5365     if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0)
  5366       error(217);               /* loose indentation */
  5367     *lastindent=stmtindent;
  5368     indent_nowarn=FALSE;        /* if warning was blocked, re-enable it */
  5369   } /* if */
  5370   switch (tok) {
  5371   case 0:
  5372     /* nothing */
  5373     break;
  5374   case tNEW:
  5375     if (allow_decl) {
  5376       autozero=1;
  5377       declloc(FALSE);
  5378       lastst=tNEW;
  5379     } else {
  5380       error(3);                 /* declaration only valid in a block */
  5381     } /* if */
  5382     break;
  5383   case tDECL:
  5384     if (allow_decl) {
  5385       autozero=0;
  5386       declloc(FALSE);
  5387       lastst=tDECL;
  5388     } else {
  5389       error(3);                 /* declaration only valid in a block */
  5390     } /* if */
  5391     break;
  5392   case tSTATIC:
  5393     if (allow_decl) {
  5394       declloc(TRUE);
  5395       lastst=tNEW;
  5396     } else {
  5397       error(3);                 /* declaration only valid in a block */
  5398     } /* if */
  5399     break;
  5400   case '{':
  5401   case tBEGIN:
  5402     save=fline;
  5403     if (!matchtoken('}'))       /* {} is the empty statement */
  5404       compound(save==fline,tok);
  5405     /* lastst (for "last statement") does not change */
  5406     break;
  5407   case ';':
  5408     error(36);                  /* empty statement */
  5409     break;
  5410   case tIF:
  5411     lastst=doif();
  5412     break;
  5413   case tWHILE:
  5414     lastst=dowhile();
  5415     break;
  5416   case tDO:
  5417     lastst=dodo();
  5418     break;
  5419   case tFOR:
  5420     lastst=dofor();
  5421     break;
  5422   case tSWITCH:
  5423     doswitch();
  5424     lastst=tSWITCH;
  5425     break;
  5426   case tCASE:
  5427   case tDEFAULT:
  5428     error(14);     /* not in switch */
  5429     break;
  5430   case tGOTO:
  5431     dogoto();
  5432     lastst=tGOTO;
  5433     break;
  5434   case tLABEL:
  5435     dolabel();
  5436     lastst=tLABEL;
  5437     break;
  5438   case tRETURN:
  5439     doreturn();
  5440     lastst=tRETURN;
  5441     break;
  5442   case tBREAK:
  5443     dobreak();
  5444     lastst=tBREAK;
  5445     break;
  5446   case tCONTINUE:
  5447     docont();
  5448     lastst=tCONTINUE;
  5449     break;
  5450   case tEXIT:
  5451     doexit();
  5452     lastst=tEXIT;
  5453     break;
  5454   case tASSERT:
  5455     doassert();
  5456     lastst=tASSERT;
  5457     break;
  5458   case tSLEEP:
  5459     dosleep();
  5460     lastst=tSLEEP;
  5461     break;
  5462   case tSTATE:
  5463     dostate();
  5464     lastst=tSTATE;
  5465     break;
  5466   case tCONST:
  5467     decl_const(sLOCAL);
  5468     break;
  5469   case tENUM:
  5470     decl_enum(sLOCAL);
  5471     break;
  5472   default:          /* non-empty expression */
  5473     sc_allowproccall=optproccall;
  5474     lexpush();      /* analyze token later */
  5475     doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);
  5476     needtoken(tTERM);
  5477     lastst=tEXPR;
  5478     sc_allowproccall=FALSE;
  5479   } /* switch */
  5480 }
  5481 
  5482 static void compound(int stmt_sameline,int starttok)
  5483 {
  5484   int indent=-1;
  5485   cell save_decl=declared;
  5486   int count_stmt=0;
  5487   int block_start=fline;  /* save line where the compound block started */
  5488   int endtok;
  5489 
  5490   pushstacklist();
  5491   pushheaplist();
  5492   /* if there is more text on this line, we should adjust the statement indent */
  5493   if (stmt_sameline) {
  5494     int i;
  5495     const unsigned char *p=lptr;
  5496     /* go back to the opening brace */
  5497     while (*p!=starttok) {
  5498       assert(p>pline);
  5499       p--;
  5500     } /* while */
  5501     assert(*p==starttok);  /* it should be found */
  5502     /* go forward, skipping white-space */
  5503     p++;
  5504     while (*p<=' ' && *p!='\0')
  5505       p++;
  5506     assert(*p!='\0'); /* a token should be found */
  5507     stmtindent=0;
  5508     for (i=0; i<(int)(p-pline); i++)
  5509       if (pline[i]=='\t' && sc_tabsize>0)
  5510         stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
  5511       else
  5512         stmtindent++;
  5513   } /* if */
  5514 
  5515   endtok=(starttok=='{') ? '}' : tEND;
  5516   nestlevel+=1;                 /* increase compound statement level */
  5517   while (matchtoken(endtok)==0){/* repeat until compound statement is closed */
  5518     if (!freading){
  5519       error(30,block_start);    /* compound block not closed at end of file */
  5520       break;
  5521     } else {
  5522       if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE || lastst==tENDLESS))
  5523         error(225);             /* unreachable code */
  5524       statement(&indent,TRUE);  /* do a statement */
  5525       count_stmt++;
  5526     } /* if */
  5527   } /* while */
  5528   if (lastst!=tRETURN)
  5529     destructsymbols(&loctab,nestlevel);
  5530   if (lastst!=tRETURN && lastst!=tGOTO) {
  5531     popheaplist(1);
  5532     popstacklist(1);
  5533   } else {
  5534     popheaplist(0);
  5535     popstacklist(0);
  5536   }
  5537   testsymbols(&loctab,nestlevel,FALSE,TRUE);        /* look for unused block locals */
  5538   declared=save_decl;
  5539   delete_symbols(&loctab,nestlevel,FALSE,TRUE);     /* erase local symbols, but
  5540                                                      * retain block local labels
  5541                                                      * (within the function) */
  5542   nestlevel-=1;                 /* decrease compound statement level */
  5543 }
  5544 
  5545 /*  doexpr
  5546  *
  5547  *  Global references: stgidx   (referred to only)
  5548  */
  5549 static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr,
  5550                   int *tag,symbol **symptr,int chkfuncresult)
  5551 {
  5552   return doexpr2(comma,chkeffect,allowarray,mark_endexpr,tag,symptr,chkfuncresult,NULL);
  5553 }
  5554 
  5555 /*  doexpr2
  5556  *
  5557  *  Global references: stgidx   (referred to only)
  5558  */
  5559 static int doexpr2(int comma,int chkeffect,int allowarray,int mark_endexpr,
  5560                   int *tag,symbol **symptr,int chkfuncresult,value *lval)
  5561 {
  5562   int index,ident;
  5563   int localstaging=FALSE;
  5564   cell val;
  5565 
  5566   if (!staging) {
  5567     stgset(TRUE);               /* start stage-buffering */
  5568     localstaging=TRUE;
  5569     assert(stgidx==0);
  5570   } /* if */
  5571   index=stgidx;
  5572   errorset(sEXPRMARK,0);
  5573   do {
  5574     /* on second round through, mark the end of the previous expression */
  5575     if (index!=stgidx)
  5576       markexpr(sEXPR,NULL,0);
  5577     sideeffect=FALSE;
  5578     ident=expression(&val,tag,symptr,chkfuncresult,lval);
  5579     if (!allowarray && (ident==iARRAY || ident==iREFARRAY))
  5580       error(33,"-unknown-");    /* array must be indexed */
  5581     if (chkeffect && !sideeffect)
  5582       error(215);               /* expression has no effect */
  5583     sc_allowproccall=FALSE;     /* cannot use "procedure call" syntax anymore */
  5584   } while (comma && matchtoken(',')); /* more? */
  5585   if (mark_endexpr)
  5586     markexpr(sEXPR,NULL,0);     /* optionally, mark the end of the expression */
  5587   errorset(sEXPRRELEASE,0);
  5588   if (localstaging) {
  5589     stgout(index);
  5590     stgset(FALSE);              /* stop staging */
  5591   } /* if */
  5592   return ident;
  5593 }
  5594 
  5595 /*  constexpr
  5596  */
  5597 SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr)
  5598 {
  5599   int ident,index;
  5600   cell cidx;
  5601 
  5602   stgset(TRUE);         /* start stage-buffering */
  5603   stgget(&index,&cidx); /* mark position in code generator */
  5604   errorset(sEXPRMARK,0);
  5605   ident=expression(val,tag,symptr,FALSE,NULL);
  5606   stgdel(index,cidx);   /* scratch generated code */
  5607   stgset(FALSE);        /* stop stage-buffering */
  5608   if (ident!=iCONSTEXPR) {
  5609     error(8);           /* must be constant expression */
  5610     if (val!=NULL)
  5611       *val=0;
  5612     if (tag!=NULL)
  5613       *tag=0;
  5614     if (symptr!=NULL)
  5615       *symptr=NULL;
  5616   } /* if */
  5617   errorset(sEXPRRELEASE,0);
  5618   return (ident==iCONSTEXPR);
  5619 }
  5620 
  5621 /*  test
  5622  *
  5623  *  In the case a "simple assignment" operator ("=") is used within a test,
  5624  *  the warning "possibly unintended assignment" is displayed. This routine
  5625  *  sets the global variable "sc_intest" to true, it is restored upon termination.
  5626  *  In the case the assignment was intended, use parantheses around the
  5627  *  expression to avoid the warning; primary() sets "sc_intest" to 0.
  5628  *
  5629  *  Global references: sc_intest (altered, but restored upon termination)
  5630  */
  5631 static int test(int label,int parens,int invert)
  5632 {
  5633   int index,tok;
  5634   cell cidx;
  5635   int ident,tag;
  5636   int endtok;
  5637   cell constval;
  5638   symbol *sym;
  5639   int localstaging=FALSE;
  5640 
  5641   if (!staging) {
  5642     stgset(TRUE);               /* start staging */
  5643     localstaging=TRUE;
  5644     #if !defined NDEBUG
  5645       stgget(&index,&cidx);     /* should start at zero if started locally */
  5646       assert(index==0);
  5647     #endif
  5648   } /* if */
  5649 
  5650   PUSHSTK_I(sc_intest);
  5651   sc_intest=TRUE;
  5652   endtok=0;
  5653   if (parens!=TEST_PLAIN) {
  5654     if (matchtoken('('))
  5655       endtok=')';
  5656     else if (parens==TEST_THEN)
  5657       endtok=tTHEN;
  5658     else if (parens==TEST_DO)
  5659       endtok=tDO;
  5660   } /* if */
  5661   do {
  5662     stgget(&index,&cidx);       /* mark position (of last expression) in
  5663                                  * code generator */
  5664     ident=expression(&constval,&tag,&sym,TRUE,NULL);
  5665     tok=matchtoken(',');
  5666     if (tok)
  5667       markexpr(sEXPR,NULL,0);
  5668   } while (tok); /* do */
  5669   if (endtok!=0)
  5670     needtoken(endtok);
  5671   if (ident==iARRAY || ident==iREFARRAY) {
  5672     char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-";
  5673     error(33,ptr);              /* array must be indexed */
  5674   } /* if */
  5675   if (ident==iCONSTEXPR) {      /* constant expression */
  5676     int testtype=0;
  5677     sc_intest=(short)POPSTK_I();/* restore stack */
  5678     stgdel(index,cidx);
  5679     if (constval) {             /* code always executed */
  5680       error(206);               /* redundant test: always non-zero */
  5681       testtype=tENDLESS;
  5682     } else {
  5683       error(205);               /* redundant code: never executed */
  5684       jumplabel(label);
  5685     } /* if */
  5686     if (localstaging) {
  5687       stgout(0);                /* write "jumplabel" code */
  5688       stgset(FALSE);            /* stop staging */
  5689     } /* if */
  5690     return testtype;
  5691   } /* if */
  5692   if (tag!=0 && tag!=pc_addtag("bool"))
  5693     if (check_userop(lneg,tag,0,1,NULL,&tag))
  5694       invert= !invert;          /* user-defined ! operator inverted result */
  5695   if (invert)
  5696     jmp_ne0(label);             /* jump to label if true (different from 0) */
  5697   else
  5698     jmp_eq0(label);             /* jump to label if false (equal to 0) */
  5699   markexpr(sEXPR,NULL,0);       /* end expression (give optimizer a chance) */
  5700   sc_intest=(short)POPSTK_I();  /* double typecast to avoid warning with Microsoft C */
  5701   if (localstaging) {
  5702     stgout(0);                  /* output queue from the very beginning (see
  5703                                  * assert() when localstaging is set to TRUE) */
  5704     stgset(FALSE);              /* stop staging */
  5705   } /* if */
  5706   return 0;
  5707 }
  5708 
  5709 static int doif(void)
  5710 {
  5711   int flab1,flab2;
  5712   int ifindent;
  5713   int lastst_true;
  5714 
  5715   ifindent=stmtindent;          /* save the indent of the "if" instruction */
  5716   flab1=getlabel();             /* get label number for false branch */
  5717   test(flab1,TEST_THEN,FALSE);  /* get expression, branch to flab1 if false */
  5718   statement(NULL,FALSE);        /* if true, do a statement */
  5719   if (!matchtoken(tELSE)) {     /* if...else ? */
  5720     setlabel(flab1);            /* no, simple if..., print false label */
  5721   } else {
  5722     lastst_true=lastst;         /* save last statement of the "true" branch */
  5723     /* to avoid the "dangling else" error, we want a warning if the "else"
  5724      * has a lower indent than the matching "if" */
  5725     if (stmtindent<ifindent && sc_tabsize>0)
  5726       error(217);               /* loose indentation */
  5727     flab2=getlabel();
  5728     if ((lastst!=tRETURN) && (lastst!=tGOTO))
  5729       jumplabel(flab2);         /* "true" branch jumps around "else" clause, unless the "true" branch statement already jumped */
  5730     setlabel(flab1);            /* print false label */
  5731     statement(NULL,FALSE);      /* do "else" clause */
  5732     setlabel(flab2);            /* print true label */
  5733     /* if both the "true" branch and the "false" branch ended with the same
  5734      * kind of statement, set the last statement id to that kind, rather than
  5735      * to the generic tIF; this allows for better "unreachable code" checking
  5736      */
  5737     if (lastst==lastst_true)
  5738       return lastst;
  5739   } /* if */
  5740   return tIF;
  5741 }
  5742 
  5743 static int dowhile(void)
  5744 {
  5745   int wq[wqSIZE];               /* allocate local queue */
  5746   int save_endlessloop,retcode;
  5747 
  5748   save_endlessloop=endlessloop;
  5749   addwhile(wq);                 /* add entry to queue for "break" */
  5750   setlabel(wq[wqLOOP]);         /* loop label */
  5751   /* The debugger uses the "break" opcode to be able to "break" out of
  5752    * a loop. To make sure that each loop has a break opcode, even for the
  5753    * tiniest loop, set it below the top of the loop
  5754    */
  5755   setline(TRUE);
  5756   endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
  5757   statement(NULL,FALSE);        /* if so, do a statement */
  5758   jumplabel(wq[wqLOOP]);        /* and loop to "while" start */
  5759   setlabel(wq[wqEXIT]);         /* exit label */
  5760   delwhile();                   /* delete queue entry */
  5761 
  5762   retcode=endlessloop ? tENDLESS : tWHILE;
  5763   endlessloop=save_endlessloop;
  5764   return retcode;
  5765 }
  5766 
  5767 /*
  5768  *  Note that "continue" will in this case not jump to the top of the loop, but
  5769  *  to the end: just before the TRUE-or-FALSE testing code.
  5770  */
  5771 static int dodo(void)
  5772 {
  5773   int wq[wqSIZE],top;
  5774   int save_endlessloop,retcode;
  5775 
  5776   save_endlessloop=endlessloop;
  5777   addwhile(wq);           /* see "dowhile" for more info */
  5778   top=getlabel();         /* make a label first */
  5779   setlabel(top);          /* loop label */
  5780   statement(NULL,FALSE);
  5781   needtoken(tWHILE);
  5782   setlabel(wq[wqLOOP]);   /* "continue" always jumps to WQLOOP. */
  5783   setline(TRUE);
  5784   endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
  5785   jumplabel(top);
  5786   setlabel(wq[wqEXIT]);
  5787   delwhile();
  5788   needtoken(tTERM);
  5789 
  5790   retcode=endlessloop ? tENDLESS : tDO;
  5791   endlessloop=save_endlessloop;
  5792   return retcode;
  5793 }
  5794 
  5795 static int dofor(void)
  5796 {
  5797   int wq[wqSIZE],skiplab;
  5798   cell save_decl;
  5799   int save_nestlevel,save_endlessloop;
  5800   int index,endtok;
  5801   int *ptr;
  5802 
  5803   save_decl=declared;
  5804   save_nestlevel=nestlevel;
  5805   save_endlessloop=endlessloop;
  5806   pushstacklist();
  5807   
  5808   addwhile(wq);
  5809   skiplab=getlabel();
  5810   endtok= matchtoken('(') ? ')' : tDO;
  5811   if (matchtoken(';')==0) {
  5812     /* new variable declarations are allowed here */
  5813     if (matchtoken(tNEW)) {
  5814       /* The variable in expr1 of the for loop is at a
  5815        * 'compound statement' level of it own.
  5816        */
  5817       nestlevel++;
  5818       autozero=1;
  5819       declloc(FALSE); /* declare local variable */
  5820     } else {
  5821       doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);  /* expression 1 */
  5822       needtoken(';');
  5823     } /* if */
  5824   } /* if */
  5825   /* Adjust the "declared" field in the "while queue", in case that
  5826    * local variables were declared in the first expression of the
  5827    * "for" loop. These are deleted in separately, so a "break" or a "continue"
  5828    * must ignore these fields.
  5829    */
  5830   ptr=readwhile();
  5831   assert(ptr!=NULL);
  5832   /*ptr[wqBRK]=(int)declared;
  5833    *ptr[wqCONT]=(int)declared;
  5834    */
  5835   ptr[wqBRK] = stackusage->list_id;
  5836   ptr[wqCONT] = stackusage->list_id;
  5837   jumplabel(skiplab);               /* skip expression 3 1st time */
  5838   setlabel(wq[wqLOOP]);             /* "continue" goes to this label: expr3 */
  5839   setline(TRUE);
  5840   /* Expressions 2 and 3 are reversed in the generated code: expression 3
  5841    * precedes expression 2. When parsing, the code is buffered and marks for
  5842    * the start of each expression are insterted in the buffer.
  5843    */
  5844   assert(!staging);
  5845   stgset(TRUE);                     /* start staging */
  5846   assert(stgidx==0);
  5847   index=stgidx;
  5848   stgmark(sSTARTREORDER);
  5849   stgmark((char)(sEXPRSTART+0));    /* mark start of 2nd expression in stage */
  5850   setlabel(skiplab);                /* jump to this point after 1st expression */
  5851   if (matchtoken(';')) {
  5852     endlessloop=1;
  5853   } else {
  5854     endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
  5855     needtoken(';');
  5856   } /* if */
  5857   stgmark((char)(sEXPRSTART+1));    /* mark start of 3th expression in stage */
  5858   if (!matchtoken(endtok)) {
  5859     doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE);    /* expression 3 */
  5860     needtoken(endtok);
  5861   } /* if */
  5862   stgmark(sENDREORDER);             /* mark end of reversed evaluation */
  5863   stgout(index);
  5864   stgset(FALSE);                    /* stop staging */
  5865   statement(NULL,FALSE);
  5866   jumplabel(wq[wqLOOP]);
  5867   setlabel(wq[wqEXIT]);
  5868   delwhile();
  5869 
  5870   assert(nestlevel>=save_nestlevel);
  5871   if (nestlevel>save_nestlevel) {
  5872     /* Clean up the space and the symbol table for the local
  5873      * variable in "expr1".
  5874      */
  5875     destructsymbols(&loctab,nestlevel);
  5876     popstacklist(1);
  5877     testsymbols(&loctab,nestlevel,FALSE,TRUE);  /* look for unused block locals */
  5878     declared=save_decl;
  5879     delete_symbols(&loctab,nestlevel,FALSE,TRUE);
  5880     nestlevel=save_nestlevel;     /* reset 'compound statement' nesting level */
  5881   } else {
  5882     popstacklist(0);
  5883   } /* if */
  5884 
  5885   index=endlessloop ? tENDLESS : tFOR;
  5886   endlessloop=save_endlessloop;
  5887   return index;
  5888 }
  5889 
  5890 /* The switch statement is incompatible with its C sibling:
  5891  * 1. the cases are not drop through
  5892  * 2. only one instruction may appear below each case, use a compound
  5893  *    instruction to execute multiple instructions
  5894  * 3. the "case" keyword accepts a comma separated list of values to
  5895  *    match
  5896  *
  5897  * SWITCH param
  5898  *   PRI = expression result
  5899  *   param = table offset (code segment)
  5900  *
  5901  */
  5902 static void doswitch(void)
  5903 {
  5904   int lbl_table,lbl_exit,lbl_case;
  5905   int swdefault,casecount;
  5906   int tok,endtok;
  5907   cell val;
  5908   char *str;
  5909   constvalue caselist = { NULL, "", 0, 0};   /* case list starts empty */
  5910   constvalue *cse,*csp;
  5911   char labelname[sNAMEMAX+1];
  5912 
  5913   endtok= matchtoken('(') ? ')' : tDO;
  5914   doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */
  5915   needtoken(endtok);
  5916   /* generate the code for the switch statement, the label is the address
  5917    * of the case table (to be generated later).
  5918    */
  5919   lbl_table=getlabel();
  5920   lbl_case=0;                   /* just to avoid a compiler warning */
  5921   ffswitch(lbl_table);
  5922 
  5923   if (matchtoken(tBEGIN)) {
  5924     endtok=tEND;
  5925   } else {
  5926     endtok='}';
  5927     needtoken('{');
  5928   } /* if */
  5929   lbl_exit=getlabel();          /* get label number for jumping out of switch */
  5930   swdefault=FALSE;
  5931   casecount=0;
  5932   do {
  5933     tok=lex(&val,&str);         /* read in (new) token */
  5934     switch (tok) {
  5935     case tCASE:
  5936       if (swdefault!=FALSE)
  5937         error(15);        /* "default" case must be last in switch statement */
  5938       lbl_case=getlabel();
  5939       PUSHSTK_I(sc_allowtags);
  5940       sc_allowtags=FALSE; /* do not allow tagnames here */
  5941       do {
  5942         casecount++;
  5943 
  5944         /* ??? enforce/document that, in a switch, a statement cannot start
  5945          *     with a label. Then, you can search for:
  5946          *     * the first semicolon (marks the end of a statement)
  5947          *     * an opening brace (marks the start of a compound statement)
  5948          *     and search for the right-most colon before that statement
  5949          *     Now, by replacing the ':' by a special COLON token, you can
  5950          *     parse all expressions until that special token.
  5951          */
  5952 
  5953         constexpr(&val,NULL,NULL);
  5954         /* Search the insertion point (the table is kept in sorted order, so
  5955          * that advanced abstract machines can sift the case table with a
  5956          * binary search). Check for duplicate case values at the same time.
  5957          */
  5958         for (csp=&caselist, cse=caselist.next;
  5959              cse!=NULL && cse->value<val;
  5960              csp=cse, cse=cse->next)
  5961           /* nothing */;
  5962         if (cse!=NULL && cse->value==val)
  5963           error(40,val);                /* duplicate "case" label */
  5964         /* Since the label is stored as a string in the "constvalue", the
  5965          * size of an identifier must be at least 8, as there are 8
  5966          * hexadecimal digits in a 32-bit number.
  5967          */
  5968         #if sNAMEMAX < 8
  5969           #error Length of identifier (sNAMEMAX) too small.
  5970         #endif
  5971         assert(csp!=NULL);
  5972         assert(csp->next==cse);
  5973         insert_constval(csp,cse,itoh(lbl_case),val,0);
  5974         if (matchtoken(tDBLDOT)) {
  5975           error(1, ":", "..");
  5976         } /* if */
  5977       } while (matchtoken(','));
  5978       needtoken(':');                   /* ':' ends the case */
  5979       sc_allowtags=(short)POPSTK_I();   /* reset */
  5980       setlabel(lbl_case);
  5981       statement(NULL,FALSE);
  5982       jumplabel(lbl_exit);
  5983       break;
  5984     case tDEFAULT:
  5985       if (swdefault!=FALSE)
  5986         error(16);         /* multiple defaults in switch */
  5987       lbl_case=getlabel();
  5988       setlabel(lbl_case);
  5989       needtoken(':');
  5990       swdefault=TRUE;
  5991       statement(NULL,FALSE);
  5992       /* Jump to lbl_exit, even thouh this is the last clause in the
  5993        * switch, because the jump table is generated between the last
  5994        * clause of the switch and the exit label.
  5995        */
  5996       jumplabel(lbl_exit);
  5997       break;
  5998     default:
  5999       if (tok!=endtok) {
  6000         error(2);
  6001         indent_nowarn=TRUE; /* disable this check */
  6002         tok=endtok;     /* break out of the loop after an error */
  6003       } /* if */
  6004     } /* switch */
  6005   } while (tok!=endtok);
  6006 
  6007   #if !defined NDEBUG
  6008     /* verify that the case table is sorted (unfortunatly, duplicates can
  6009      * occur; there really shouldn't be duplicate cases, but the compiler
  6010      * may not crash or drop into an assertion for a user error). */
  6011     for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next)
  6012       assert(cse->value <= cse->next->value);
  6013   #endif
  6014   /* generate the table here, before lbl_exit (general jump target) */
  6015   setlabel(lbl_table);
  6016   assert(swdefault==FALSE || swdefault==TRUE);
  6017   if (swdefault==FALSE) {
  6018     /* store lbl_exit as the "none-matched" label in the switch table */
  6019     strcpy(labelname,itoh(lbl_exit));
  6020   } else {
  6021     /* lbl_case holds the label of the "default" clause */
  6022     strcpy(labelname,itoh(lbl_case));
  6023   } /* if */
  6024   ffcase(casecount,labelname,TRUE);
  6025   /* generate the rest of the table */
  6026   for (cse=caselist.next; cse!=NULL; cse=cse->next)
  6027     ffcase(cse->value,cse->name,FALSE);
  6028 
  6029   setlabel(lbl_exit);
  6030   delete_consttable(&caselist); /* clear list of case labels */
  6031 }
  6032 
  6033 static void doassert(void)
  6034 {
  6035   int flab1,index;
  6036   cell cidx;
  6037 
  6038   if ((sc_debug & sCHKBOUNDS)!=0) {
  6039     flab1=getlabel();           /* get label number for "OK" branch */
  6040     test(flab1,TEST_PLAIN,TRUE);/* get expression and branch to flab1 if true */
  6041     insert_dbgline(fline);      /* make sure we can find the correct line number */
  6042     ffabort(xASSERTION);
  6043     setlabel(flab1);
  6044   } else {
  6045     stgset(TRUE);               /* start staging */
  6046     stgget(&index,&cidx);       /* mark position in code generator */
  6047     do {
  6048       expression(NULL,NULL,NULL,FALSE,NULL);
  6049       stgdel(index,cidx);       /* just scrap the code */
  6050     } while (matchtoken(','));
  6051     stgset(FALSE);              /* stop staging */
  6052   } /* if */
  6053   needtoken(tTERM);
  6054 }
  6055 
  6056 static void dogoto(void)
  6057 {
  6058   char *st;
  6059   cell val;
  6060   symbol *sym;
  6061 
  6062   /* if we were inside an endless loop, assume that we jump out of it */
  6063   endlessloop=0;
  6064 
  6065   if (lex(&val,&st)==tSYMBOL) {
  6066     sym=fetchlab(st);
  6067     jumplabel((int)sym->addr);
  6068     sym->usage|=uREAD;  /* set "uREAD" bit */
  6069     // ??? if the label is defined (check sym->usage & uDEFINE), check
  6070     //     sym->compound (nesting level of the label) against nestlevel;
  6071     //     if sym->compound < nestlevel, call the destructor operator
  6072   } else {
  6073     error(20,st);       /* illegal symbol name */
  6074   } /* if */
  6075   needtoken(tTERM);
  6076 }
  6077 
  6078 static void dolabel(void)
  6079 {
  6080   char *st;
  6081   cell val;
  6082   symbol *sym;
  6083 
  6084   tokeninfo(&val,&st);  /* retrieve label name again */
  6085   if (find_constval(&tagname_tab,st,0)!=NULL)
  6086     error(221,st);      /* label name shadows tagname */
  6087   sym=fetchlab(st);
  6088   setlabel((int)sym->addr);
  6089   /* since one can jump around variable declarations or out of compound
  6090    * blocks, the stack must be manually adjusted
  6091    */
  6092   //:TODO: This is actually generated, egads!
  6093   //We have to support this and LCTRL/SCTRL
  6094   setstk(-declared*sizeof(cell));
  6095   sym->usage|=uDEFINE;  /* label is now defined */
  6096 }
  6097 
  6098 /*  fetchlab
  6099  *
  6100  *  Finds a label from the (local) symbol table or adds one to it.
  6101  *  Labels are local in scope.
  6102  *
  6103  *  Note: The "_usage" bit is set to zero. The routines that call "fetchlab()"
  6104  *        must set this bit accordingly.
  6105  */
  6106 static symbol *fetchlab(char *name)
  6107 {
  6108   symbol *sym;
  6109 
  6110   sym=findloc(name);            /* labels are local in scope */
  6111   if (sym){
  6112     if (sym->ident!=iLABEL)
  6113       error(19,sym->name);      /* not a label: ... */
  6114   } else {
  6115     sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0);
  6116     assert(sym!=NULL);          /* fatal error 103 must be given on error */
  6117     sym->x.declared=(int)declared;
  6118     sym->compound=nestlevel;
  6119   } /* if */
  6120   return sym;
  6121 }
  6122 
  6123 /*  doreturn
  6124  *
  6125  *  Global references: rettype  (altered)
  6126  */
  6127 static void doreturn(void)
  6128 {
  6129   int tag,ident;
  6130   int level;
  6131   symbol *sym,*sub;
  6132 
  6133   if (!matchtoken(tTERM)) {
  6134     /* "return <value>" */
  6135     if ((rettype & uRETNONE)!=0)
  6136       error(78);                        /* mix "return;" and "return value;" */
  6137     ident=doexpr(TRUE,FALSE,TRUE,FALSE,&tag,&sym,TRUE);
  6138     needtoken(tTERM);
  6139     /* see if this function already has a sub type (an array attached) */
  6140     sub=finddepend(curfunc);
  6141     assert(sub==NULL || sub->ident==iREFARRAY);
  6142     if ((rettype & uRETVALUE)!=0) {
  6143       int retarray=(ident==iARRAY || ident==iREFARRAY);
  6144       /* there was an earlier "return" statement in this function */
  6145       if (sub==NULL && retarray || sub!=NULL && !retarray)
  6146         error(79);                      /* mixing "return array;" and "return value;" */
  6147       if (retarray && (curfunc->usage & uPUBLIC)!=0)
  6148         error(90,curfunc->name);        /* public function may not return array */
  6149     } /* if */
  6150     rettype|=uRETVALUE;                 /* function returns a value */
  6151     /* check tagname with function tagname */
  6152     assert(curfunc!=NULL);
  6153     if (!matchtag_string(ident, tag) && !matchtag(curfunc->tag,tag,TRUE))
  6154       error(213);                       /* tagname mismatch */
  6155     if (ident==iARRAY || ident==iREFARRAY) {
  6156       int dim[sDIMEN_MAX],numdim;
  6157       cell arraysize;
  6158       assert(sym!=NULL);
  6159       if (sub!=NULL) {
  6160         assert(sub->ident==iREFARRAY);
  6161         /* this function has an array attached already; check that the current
  6162          * "return" statement returns exactly the same array
  6163          */
  6164         level=sym->dim.array.level;
  6165         if (sub->dim.array.level!=level) {
  6166           error(48);                    /* array dimensions must match */
  6167         } else {
  6168           for (numdim=0; numdim<=level; numdim++) {
  6169             dim[numdim]=(int)sub->dim.array.length;
  6170             if (sym->dim.array.length!=dim[numdim])
  6171               error(47);    /* array sizes must match */
  6172             if (numdim<level) {
  6173               sym=finddepend(sym);
  6174               sub=finddepend(sub);
  6175               assert(sym!=NULL && sub!=NULL);
  6176               /* ^^^ both arrays have the same dimensions (this was checked
  6177                *     earlier) so the dependend should always be found
  6178                */
  6179             } /* if */
  6180           } /* for */
  6181         } /* if */
  6182       } else {
  6183         int idxtag[sDIMEN_MAX];
  6184         int argcount;
  6185         /* this function does not yet have an array attached; clone the
  6186          * returned symbol beneath the current function
  6187          */
  6188         sub=sym;
  6189         assert(sub!=NULL);
  6190         level=sub->dim.array.level;
  6191         for (numdim=0; numdim<=level; numdim++) {
  6192           dim[numdim]=(int)sub->dim.array.length;
  6193           idxtag[numdim]=sub->x.tags.index;
  6194           if (numdim<level) {
  6195             sub=finddepend(sub);
  6196             assert(sub!=NULL);
  6197           } /* if */
  6198           /* check that all dimensions are known */
  6199           if (dim[numdim]<=0)
  6200             error(46,sym->name);
  6201         } /* for */
  6202         /* the address of the array is stored in a hidden parameter; the address
  6203          * of this parameter is 1 + the number of parameters (times the size of
  6204          * a cell) + the size of the stack frame and the return address
  6205          *   base + 0*sizeof(cell)         == previous "base"
  6206          *   base + 1*sizeof(cell)         == function return address
  6207          *   base + 2*sizeof(cell)         == number of arguments
  6208          *   base + 3*sizeof(cell)         == first argument of the function
  6209          *   ...
  6210          *   base + ((n-1)+3)*sizeof(cell) == last argument of the function
  6211          *   base + (n+3)*sizeof(cell)     == hidden parameter with array address
  6212          */
  6213         assert(curfunc!=NULL);
  6214         assert(curfunc->dim.arglist!=NULL);
  6215         for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++)
  6216           /* nothing */;
  6217         sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag);
  6218         sub->parent=curfunc;
  6219       } /* if */
  6220       /* get the hidden parameter, copy the array (the array is on the heap;
  6221        * it stays on the heap for the moment, and it is removed -usually- at
  6222        * the end of the expression/statement, see expression() in SC3.C)
  6223        */
  6224       address(sub,sALT);                /* ALT = destination */
  6225       arraysize=calc_arraysize(dim,numdim,0);
  6226       memcopy(arraysize*sizeof(cell));  /* source already in PRI */
  6227       /* moveto1(); is not necessary, callfunction() does a popreg() */
  6228     } /* if */
  6229   } else {
  6230     /* this return statement contains no expression */
  6231     ldconst(0,sPRI);
  6232     if ((rettype & uRETVALUE)!=0) {
  6233       char symname[2*sNAMEMAX+16];      /* allow space for user defined operators */
  6234       assert(curfunc!=NULL);
  6235       funcdisplayname(symname,curfunc->name);
  6236       error(209,symname);               /* function should return a value */
  6237     } /* if */
  6238     rettype|=uRETNONE;                  /* function does not return anything */
  6239   } /* if */
  6240   destructsymbols(&loctab,0);           /* call destructor for *all* locals */
  6241   genheapfree(-1);
  6242   genstackfree(-1);						/* free everything on the stack */
  6243   ffret(strcmp(curfunc->name,uENTRYFUNC)!=0);
  6244 }
  6245 
  6246 static void dobreak(void)
  6247 {
  6248   int *ptr;
  6249 
  6250   endlessloop=0;      /* if we were inside an endless loop, we just jumped out */
  6251   ptr=readwhile();      /* readwhile() gives an error if not in loop */
  6252   needtoken(tTERM);
  6253   if (ptr==NULL)
  6254     return;
  6255   destructsymbols(&loctab,nestlevel);
  6256   genstackfree(ptr[wqBRK]);
  6257   jumplabel(ptr[wqEXIT]);
  6258 }
  6259 
  6260 static void docont(void)
  6261 {
  6262   int *ptr;
  6263 
  6264   ptr=readwhile();      /* readwhile() gives an error if not in loop */
  6265   needtoken(tTERM);
  6266   if (ptr==NULL)
  6267     return;
  6268   destructsymbols(&loctab,nestlevel);
  6269   genstackfree(ptr[wqCONT]);
  6270   genheapfree(ptr[wqCONT]);
  6271   jumplabel(ptr[wqLOOP]);
  6272 }
  6273 
  6274 SC_FUNC void exporttag(int tag)
  6275 {
  6276   /* find the tag by value in the table, then set the top bit to mark it
  6277    * "public"
  6278    */
  6279   if (tag!=0 && (tag & PUBLICTAG)==0) {
  6280     constvalue *ptr;
  6281     for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next)
  6282       /* nothing */;
  6283     if (ptr!=NULL)
  6284       ptr->value |= PUBLICTAG;
  6285   } /* if */
  6286 }
  6287 
  6288 static void doexit(void)
  6289 {
  6290   int tag=0;
  6291 
  6292   if (matchtoken(tTERM)==0){
  6293     doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
  6294     needtoken(tTERM);
  6295   } else {
  6296     ldconst(0,sPRI);
  6297   } /* if */
  6298   ldconst(tag,sALT);
  6299   exporttag(tag);
  6300   destructsymbols(&loctab,0);           /* call destructor for *all* locals */
  6301   ffabort(xEXIT);
  6302 }
  6303 
  6304 static void dosleep(void)
  6305 {
  6306   int tag=0;
  6307 
  6308   if (matchtoken(tTERM)==0){
  6309     doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE);
  6310     needtoken(tTERM);
  6311   } else {
  6312     ldconst(0,sPRI);
  6313   } /* if */
  6314   ldconst(tag,sALT);
  6315   exporttag(tag);
  6316   ffabort(xSLEEP);
  6317 
  6318   /* for stack usage checking, mark the use of the sleep instruction */
  6319   pc_memflags |= suSLEEP_INSTR;
  6320 }
  6321 
  6322 static void dostate(void)
  6323 {
  6324   char name[sNAMEMAX+1];
  6325   constvalue *automaton;
  6326   constvalue *state;
  6327   constvalue *stlist;
  6328   int flabel;
  6329   symbol *sym;
  6330   #if !defined SC_LIGHT
  6331     int length,index,listid,listindex,stateindex;
  6332     char *doc;
  6333   #endif
  6334 
  6335   /* check for an optional condition */
  6336   if (matchtoken('(')) {
  6337     flabel=getlabel();          /* get label number for "false" branch */
  6338     pc_docexpr=TRUE;            /* attach expression as a documentation string */
  6339     test(flabel,TEST_PLAIN,FALSE);/* get expression, branch to flabel if false */
  6340     pc_docexpr=FALSE;
  6341     needtoken(')');
  6342   } else {
  6343     flabel=-1;
  6344   } /* if */
  6345 
  6346   if (!sc_getstateid(&automaton,&state)) {
  6347     delete_autolisttable();
  6348     return;
  6349   } /* if */
  6350   needtoken(tTERM);
  6351 
  6352   /* store the new state id */
  6353   assert(state!=NULL);
  6354   ldconst(state->value,sPRI);
  6355   assert(automaton!=NULL);
  6356   assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0);
  6357   storereg(automaton->value,sPRI);
  6358 
  6359   /* find the optional entry() function for the state */
  6360   sym=findglb(uENTRYFUNC,sGLOBAL);
  6361   if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) {
  6362     for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) {
  6363       assert(strlen(stlist->name)!=0);
  6364       if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value))
  6365         break;      /* found! */
  6366     } /* for */
  6367     assert(stlist==NULL || state_inlist(stlist->index,state->value));
  6368     if (stlist!=NULL) {
  6369       /* the label to jump to is in stlist->name */
  6370       ffcall(sym,stlist->name,0);
  6371     } /* if */
  6372   } /* if */
  6373 
  6374   if (flabel>=0)
  6375     setlabel(flabel);           /* condition was false, jump around the state switch */
  6376 
  6377   #if !defined SC_LIGHT
  6378     /* mark for documentation */
  6379     if (sc_status==statFIRST) {
  6380       char *str;
  6381       /* get the last list id attached to the function, this contains the source states */
  6382       assert(curfunc!=NULL);
  6383       if (curfunc->states!=NULL) {
  6384         stlist=curfunc->states->next;
  6385         assert(stlist!=NULL);
  6386         while (stlist->next!=NULL)
  6387           stlist=stlist->next;
  6388         listid=stlist->index;
  6389       } else {
  6390         listid=-1;
  6391       } /* if */
  6392       listindex=0;
  6393       length=strlen(name)+70; /* +70 for the fixed part "<transition ... />\n" */
  6394       /* see if there are any condition strings to attach */
  6395       for (index=0; (str=get_autolist(index))!=NULL; index++)
  6396         length+=strlen(str);
  6397       if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) {
  6398         do {
  6399           sprintf(doc,"<transition target=\"%s\"",name);
  6400           if (listid>=0) {
  6401             /* get the source state */
  6402             stateindex=state_listitem(listid,listindex);
  6403             state=state_findid(stateindex);
  6404             assert(state!=NULL);
  6405             sprintf(doc+strlen(doc)," source=\"%s\"",state->name);
  6406           } /* if */
  6407           if (get_autolist(0)!=NULL) {
  6408             /* add the condition */
  6409             strcat(doc," condition=\"");
  6410             for (index=0; (str=get_autolist(index))!=NULL; index++) {
  6411               /* remove the ')' token that may be appended before detecting that the expression has ended */
  6412               if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL)
  6413                 strcat(doc,str);
  6414             } /* for */
  6415             strcat(doc,"\"");
  6416           } /* if */
  6417           strcat(doc,"/>\n");
  6418           insert_docstring(doc);
  6419         } while (listid>=0 && ++listindex<state_count(listid));
  6420         free(doc);
  6421       } /* if */
  6422     } /* if */
  6423   #endif
  6424   delete_autolisttable();
  6425 }
  6426 
  6427 
  6428 static void addwhile(int *ptr)
  6429 {
  6430   int k;
  6431 
  6432   ptr[wqBRK]=stackusage->list_id;     /* stack pointer (for "break") */
  6433   ptr[wqCONT]=stackusage->list_id;    /* for "continue", possibly adjusted later */
  6434   ptr[wqLOOP]=getlabel();
  6435   ptr[wqEXIT]=getlabel();
  6436   if (wqptr>=(wq+wqTABSZ-wqSIZE))
  6437     error(122,"loop table");    /* loop table overflow (too many active loops)*/
  6438   k=0;
  6439   while (k<wqSIZE){     /* copy "ptr" to while queue table */
  6440     *wqptr=*ptr;
  6441     wqptr+=1;
  6442     ptr+=1;
  6443     k+=1;
  6444   } /* while */
  6445 }
  6446 
  6447 static void delwhile(void)
  6448 {
  6449   if (wqptr>wq)
  6450     wqptr-=wqSIZE;
  6451 }
  6452 
  6453 static int *readwhile(void)
  6454 {
  6455   if (wqptr<=wq){
  6456     error(24);          /* out of context */
  6457     return NULL;
  6458   } else {
  6459     return (wqptr-wqSIZE);
  6460   } /* if */
  6461 }
  6462