sourcepawn/compiler/sc2.c
changeset 27 764c6dd3dc25
child 52 97bdc78041aa
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/sourcepawn/compiler/sc2.c	Sat Jul 15 05:15:15 2006 +0000
     1.3 @@ -0,0 +1,2817 @@
     1.4 +/*  Pawn compiler - File input, preprocessing and lexical analysis functions
     1.5 + *
     1.6 + *  Copyright (c) ITB CompuPhase, 1997-2006
     1.7 + *
     1.8 + *  This software is provided "as-is", without any express or implied warranty.
     1.9 + *  In no event will the authors be held liable for any damages arising from
    1.10 + *  the use of this software.
    1.11 + *
    1.12 + *  Permission is granted to anyone to use this software for any purpose,
    1.13 + *  including commercial applications, and to alter it and redistribute it
    1.14 + *  freely, subject to the following restrictions:
    1.15 + *
    1.16 + *  1.  The origin of this software must not be misrepresented; you must not
    1.17 + *      claim that you wrote the original software. If you use this software in
    1.18 + *      a product, an acknowledgment in the product documentation would be
    1.19 + *      appreciated but is not required.
    1.20 + *  2.  Altered source versions must be plainly marked as such, and must not be
    1.21 + *      misrepresented as being the original software.
    1.22 + *  3.  This notice may not be removed or altered from any source distribution.
    1.23 + *
    1.24 + *  Version: $Id: sc2.c 3590 2006-06-24 14:16:39Z thiadmer $
    1.25 + */
    1.26 +#include <assert.h>
    1.27 +#include <stdio.h>
    1.28 +#include <stdlib.h>
    1.29 +#include <string.h>
    1.30 +#include <ctype.h>
    1.31 +#include <math.h>
    1.32 +#include "lstring.h"
    1.33 +#include "sc.h"
    1.34 +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
    1.35 +  #include <sclinux.h>
    1.36 +#endif
    1.37 +
    1.38 +#if defined FORTIFY
    1.39 +  #include <alloc/fortify.h>
    1.40 +#endif
    1.41 +
    1.42 +/* flags for litchar() */
    1.43 +#define RAWMODE         1
    1.44 +#define UTF8MODE        2
    1.45 +static cell litchar(const unsigned char **lptr,int flags);
    1.46 +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag);
    1.47 +
    1.48 +static void substallpatterns(unsigned char *line,int buffersize);
    1.49 +static int match(char *st,int end);
    1.50 +static int alpha(char c);
    1.51 +
    1.52 +#define SKIPMODE      1 /* bit field in "#if" stack */
    1.53 +#define PARSEMODE     2 /* bit field in "#if" stack */
    1.54 +#define HANDLED_ELSE  4 /* bit field in "#if" stack */
    1.55 +#define SKIPPING      (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE)
    1.56 +
    1.57 +static short icomment;  /* currently in multiline comment? */
    1.58 +static char ifstack[sCOMP_STACK]; /* "#if" stack */
    1.59 +static short iflevel;   /* nesting level if #if/#else/#endif */
    1.60 +static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */
    1.61 +static unsigned char term_expr[] = "";
    1.62 +static int listline=-1; /* "current line" for the list file */
    1.63 +
    1.64 +
    1.65 +/*  pushstk & popstk
    1.66 + *
    1.67 + *  Uses a LIFO stack to store information. The stack is used by doinclude(),
    1.68 + *  doswitch() (to hold the state of "swactive") and some other routines.
    1.69 + *
    1.70 + *  Porting note: I made the bold assumption that an integer will not be
    1.71 + *  larger than a pointer (it may be smaller). That is, the stack element
    1.72 + *  is typedef'ed as a pointer type, but I also store integers on it. See
    1.73 + *  SC.H for "stkitem"
    1.74 + *
    1.75 + *  Global references: stack,stkidx,stktop (private to pushstk(), popstk()
    1.76 + *                     and clearstk())
    1.77 + */
    1.78 +static stkitem *stack=NULL;
    1.79 +static int stkidx=0,stktop=0;
    1.80 +
    1.81 +SC_FUNC void pushstk(stkitem val)
    1.82 +{
    1.83 +  assert(stkidx<=stktop);
    1.84 +  if (stkidx==stktop) {
    1.85 +    stkitem *newstack;
    1.86 +    int newsize= (stktop==0) ? 16 : 2*stktop;
    1.87 +    /* try to resize the stack */
    1.88 +    assert(newsize>stktop);
    1.89 +    newstack=(stkitem*)malloc(newsize*sizeof(stkitem));
    1.90 +    if (newstack==NULL)
    1.91 +      error(102,"parser stack");  /* stack overflow (recursive include?) */
    1.92 +    /* swap the stacks */
    1.93 +    memcpy(newstack,stack,stkidx*sizeof(stkitem));
    1.94 +    if (stack!=NULL)
    1.95 +      free(stack);
    1.96 +    stack=newstack;
    1.97 +    stktop=newsize;
    1.98 +  } /* if */
    1.99 +  assert(stkidx<stktop);
   1.100 +  stack[stkidx]=val;
   1.101 +  stkidx+=1;
   1.102 +}
   1.103 +
   1.104 +SC_FUNC stkitem popstk(void)
   1.105 +{
   1.106 +  if (stkidx==0) {
   1.107 +    stkitem s;
   1.108 +    s.i=-1;             /* stack is empty */
   1.109 +    return s;
   1.110 +  } /* if */
   1.111 +  stkidx--;
   1.112 +  assert(stack!=NULL);
   1.113 +  return stack[stkidx];
   1.114 +}
   1.115 +
   1.116 +SC_FUNC void clearstk(void)
   1.117 +{
   1.118 +  assert(stack!=NULL || stktop==0);
   1.119 +  if (stack!=NULL) {
   1.120 +    free(stack);
   1.121 +    stack=NULL;
   1.122 +    stktop=0;
   1.123 +  } /* if */
   1.124 +  assert(stktop==0);
   1.125 +}
   1.126 +
   1.127 +SC_FUNC int plungequalifiedfile(char *name)
   1.128 +{
   1.129 +static char *extensions[] = { ".inc", ".p", ".pawn" };
   1.130 +  FILE *fp;
   1.131 +  char *ext;
   1.132 +  int ext_idx;
   1.133 +
   1.134 +  ext_idx=0;
   1.135 +  do {
   1.136 +    fp=(FILE*)pc_opensrc(name);
   1.137 +    ext=strchr(name,'\0');      /* save position */
   1.138 +    if (fp==NULL) {
   1.139 +      /* try to append an extension */
   1.140 +      strcpy(ext,extensions[ext_idx]);
   1.141 +      fp=(FILE*)pc_opensrc(name);
   1.142 +      if (fp==NULL)
   1.143 +        *ext='\0';              /* on failure, restore filename */
   1.144 +    } /* if */
   1.145 +    ext_idx++;
   1.146 +  } while (fp==NULL && ext_idx<(sizeof extensions / sizeof extensions[0]));
   1.147 +  if (fp==NULL) {
   1.148 +    *ext='\0';                  /* restore filename */
   1.149 +    return FALSE;
   1.150 +  } /* if */
   1.151 +  PUSHSTK_P(inpf);
   1.152 +  PUSHSTK_P(inpfname);          /* pointer to current file name */
   1.153 +  PUSHSTK_P(curlibrary);
   1.154 +  PUSHSTK_I(iflevel);
   1.155 +  assert(!SKIPPING);
   1.156 +  assert(skiplevel==iflevel);   /* these two are always the same when "parsing" */
   1.157 +  PUSHSTK_I(sc_is_utf8);
   1.158 +  PUSHSTK_I(icomment);
   1.159 +  PUSHSTK_I(fcurrent);
   1.160 +  PUSHSTK_I(fline);
   1.161 +  inpfname=duplicatestring(name);/* set name of include file */
   1.162 +  if (inpfname==NULL)
   1.163 +    error(103);             /* insufficient memory */
   1.164 +  inpf=fp;                  /* set input file pointer to include file */
   1.165 +  fnumber++;
   1.166 +  fline=0;                  /* set current line number to 0 */
   1.167 +  fcurrent=fnumber;
   1.168 +  icomment=0;               /* not in a comment */
   1.169 +  insert_dbgfile(inpfname);
   1.170 +  setfiledirect(inpfname);
   1.171 +  listline=-1;              /* force a #line directive when changing the file */
   1.172 +  sc_is_utf8=(short)scan_utf8(inpf,name);
   1.173 +  return TRUE;
   1.174 +}
   1.175 +
   1.176 +SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths)
   1.177 +{
   1.178 +  int result=FALSE;
   1.179 +
   1.180 +  if (try_currentpath) {
   1.181 +    result=plungequalifiedfile(name);
   1.182 +    if (!result) {
   1.183 +      /* failed to open the file in the active directory, try to open the file
   1.184 +       * in the same directory as the current file --but first check whether
   1.185 +       * there is a (relative) path for the current file
   1.186 +       */
   1.187 +      char *ptr;
   1.188 +      if ((ptr=strrchr(inpfname,DIRSEP_CHAR))!=0) {
   1.189 +        int len=(int)(ptr-inpfname)+1;
   1.190 +        if (len+strlen(name)<_MAX_PATH) {
   1.191 +          char path[_MAX_PATH];
   1.192 +          strlcpy(path,inpfname,len+1);
   1.193 +          strlcat(path,name,sizeof path);
   1.194 +          result=plungequalifiedfile(path);
   1.195 +        } /* if */
   1.196 +      } /* if */
   1.197 +    } /* if */
   1.198 +  } /* if */
   1.199 +
   1.200 +  if (try_includepaths && name[0]!=DIRSEP_CHAR) {
   1.201 +    int i;
   1.202 +    char *ptr;
   1.203 +    for (i=0; !result && (ptr=get_path(i))!=NULL; i++) {
   1.204 +      char path[_MAX_PATH];
   1.205 +      strlcpy(path,ptr,sizeof path);
   1.206 +      strlcat(path,name,sizeof path);
   1.207 +      result=plungequalifiedfile(path);
   1.208 +    } /* while */
   1.209 +  } /* if */
   1.210 +  return result;
   1.211 +}
   1.212 +
   1.213 +static void check_empty(const unsigned char *lptr)
   1.214 +{
   1.215 +  /* verifies that the string contains only whitespace */
   1.216 +  while (*lptr<=' ' && *lptr!='\0')
   1.217 +    lptr++;
   1.218 +  if (*lptr!='\0')
   1.219 +    error(38);          /* extra characters on line */
   1.220 +}
   1.221 +
   1.222 +/*  doinclude
   1.223 + *
   1.224 + *  Gets the name of an include file, pushes the old file on the stack and
   1.225 + *  sets some options. This routine doesn't use lex(), since lex() doesn't
   1.226 + *  recognize file names (and directories).
   1.227 + *
   1.228 + *  Global references: inpf     (altered)
   1.229 + *                     inpfname (altered)
   1.230 + *                     fline    (altered)
   1.231 + *                     lptr     (altered)
   1.232 + */
   1.233 +static void doinclude(int silent)
   1.234 +{
   1.235 +  char name[_MAX_PATH];
   1.236 +  char symname[sNAMEMAX];
   1.237 +  char *ptr;
   1.238 +  char c;
   1.239 +  int i, result;
   1.240 +
   1.241 +  while (*lptr<=' ' && *lptr!='\0')         /* skip leading whitespace */
   1.242 +    lptr++;
   1.243 +  if (*lptr=='<' || *lptr=='\"'){
   1.244 +    c=(char)((*lptr=='\"') ? '\"' : '>');   /* termination character */
   1.245 +    lptr++;
   1.246 +    while (*lptr<=' ' && *lptr!='\0')       /* skip whitespace after quote */
   1.247 +      lptr++;
   1.248 +  } else {
   1.249 +    c='\0';
   1.250 +  } /* if */
   1.251 +
   1.252 +  i=0;
   1.253 +  while (*lptr!=c && *lptr!='\0' && i<sizeof name - 1)  /* find the end of the string */
   1.254 +    name[i++]=*lptr++;
   1.255 +  while (i>0 && name[i-1]<=' ')
   1.256 +    i--;                        /* strip trailing whitespace */
   1.257 +  assert(i>=0 && i<sizeof name);
   1.258 +  name[i]='\0';                 /* zero-terminate the string */
   1.259 +
   1.260 +  if (*lptr!=c) {               /* verify correct string termination */
   1.261 +    error(37);                  /* invalid string */
   1.262 +    return;
   1.263 +  } /* if */
   1.264 +  if (c!='\0')
   1.265 +    check_empty(lptr+1);        /* verify that the rest of the line is whitespace */
   1.266 +
   1.267 +  /* create a symbol from the name of the include file; this allows the system
   1.268 +   * to test for multiple inclusions
   1.269 +   */
   1.270 +  strcpy(symname,"_inc_");
   1.271 +  if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL)
   1.272 +    strlcat(symname,ptr+1,sizeof symname);
   1.273 +  else
   1.274 +    strlcat(symname,name,sizeof symname);
   1.275 +  if (find_symbol(&glbtab,symname,fcurrent,-1,NULL)==NULL) {
   1.276 +    /* constant is not present, so this file has not been included yet */
   1.277 +
   1.278 +    /* Include files between "..." or without quotes are read from the current
   1.279 +     * directory, or from a list of "include directories". Include files
   1.280 +     * between <...> are only read from the list of include directories.
   1.281 +     */
   1.282 +    result=plungefile(name,(c!='>'),TRUE);
   1.283 +    if (result)
   1.284 +      add_constant(symname,1,sGLOBAL,0);
   1.285 +    else if (!silent)
   1.286 +      error(100,name);            /* cannot read from ... (fatal error) */
   1.287 +  } /* if */
   1.288 +}
   1.289 +
   1.290 +/*  readline
   1.291 + *
   1.292 + *  Reads in a new line from the input file pointed to by "inpf". readline()
   1.293 + *  concatenates lines that end with a \ with the next line. If no more data
   1.294 + *  can be read from the file, readline() attempts to pop off the previous file
   1.295 + *  from the stack. If that fails too, it sets "freading" to 0.
   1.296 + *
   1.297 + *  Global references: inpf,fline,inpfname,freading,icomment (altered)
   1.298 + */
   1.299 +static void readline(unsigned char *line)
   1.300 +{
   1.301 +  int i,num,cont;
   1.302 +  unsigned char *ptr;
   1.303 +
   1.304 +  if (lptr==term_expr)
   1.305 +    return;
   1.306 +  num=sLINEMAX;
   1.307 +  cont=FALSE;
   1.308 +  do {
   1.309 +    if (inpf==NULL || pc_eofsrc(inpf)) {
   1.310 +      if (cont)
   1.311 +        error(49);        /* invalid line continuation */
   1.312 +      if (inpf!=NULL && inpf!=inpf_org)
   1.313 +        pc_closesrc(inpf);
   1.314 +      i=POPSTK_I();
   1.315 +      if (i==-1) {        /* All's done; popstk() returns "stack is empty" */
   1.316 +        freading=FALSE;
   1.317 +        *line='\0';
   1.318 +        /* when there is nothing more to read, the #if/#else stack should
   1.319 +         * be empty and we should not be in a comment
   1.320 +         */
   1.321 +        assert(iflevel>=0);
   1.322 +        if (iflevel>0)
   1.323 +          error(1,"#endif","-end of file-");
   1.324 +        else if (icomment!=0)
   1.325 +          error(1,"*/","-end of file-");
   1.326 +        return;
   1.327 +      } /* if */
   1.328 +      fline=i;
   1.329 +      fcurrent=(short)POPSTK_I();
   1.330 +      icomment=(short)POPSTK_I();
   1.331 +      sc_is_utf8=(short)POPSTK_I();
   1.332 +      iflevel=(short)POPSTK_I();
   1.333 +      skiplevel=iflevel;        /* this condition held before including the file */
   1.334 +      assert(!SKIPPING);        /* idem ditto */
   1.335 +      curlibrary=(constvalue *)POPSTK_P();
   1.336 +      free(inpfname);           /* return memory allocated for the include file name */
   1.337 +      inpfname=(char *)POPSTK_P();
   1.338 +      inpf=(FILE *)POPSTK_P();
   1.339 +      insert_dbgfile(inpfname);
   1.340 +      setfiledirect(inpfname);
   1.341 +      listline=-1;              /* force a #line directive when changing the file */
   1.342 +    } /* if */
   1.343 +
   1.344 +    if (pc_readsrc(inpf,line,num)==NULL) {
   1.345 +      *line='\0';     /* delete line */
   1.346 +      cont=FALSE;
   1.347 +    } else {
   1.348 +      /* check whether to erase leading spaces */
   1.349 +      if (cont) {
   1.350 +        unsigned char *ptr=line;
   1.351 +        while (*ptr<=' ' && *ptr!='\0')
   1.352 +          ptr++;
   1.353 +        if (ptr!=line)
   1.354 +          memmove(line,ptr,strlen((char*)ptr)+1);
   1.355 +      } /* if */
   1.356 +      cont=FALSE;
   1.357 +      /* check whether a full line was read */
   1.358 +      if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf))
   1.359 +        error(75);      /* line too long */
   1.360 +      /* check if the next line must be concatenated to this line */
   1.361 +      if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL)
   1.362 +        ptr=(unsigned char*)strchr((char*)line,'\r');
   1.363 +      if (ptr!=NULL && ptr>line) {
   1.364 +        assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */
   1.365 +        while (ptr>line && *ptr<=' ')
   1.366 +          ptr--;        /* skip trailing whitespace */
   1.367 +        if (*ptr=='\\') {
   1.368 +          cont=TRUE;
   1.369 +          /* set '\a' at the position of '\\' to make it possible to check
   1.370 +           * for a line continuation in a single line comment (error 49)
   1.371 +           */
   1.372 +          *ptr++='\a';
   1.373 +          *ptr='\0';    /* erase '\n' (and any trailing whitespace) */
   1.374 +        } /* if */
   1.375 +      } /* if */
   1.376 +      num-=strlen((char*)line);
   1.377 +      line+=strlen((char*)line);
   1.378 +    } /* if */
   1.379 +    fline+=1;
   1.380 +  } while (num>=0 && cont);
   1.381 +}
   1.382 +
   1.383 +/*  stripcom
   1.384 + *
   1.385 + *  Replaces all comments from the line by space characters. It updates
   1.386 + *  a global variable ("icomment") for multiline comments.
   1.387 + *
   1.388 + *  This routine also supports the C++ extension for single line comments.
   1.389 + *  These comments are started with "//" and end at the end of the line.
   1.390 + *
   1.391 + *  The function also detects (and manages) "documentation comments". The
   1.392 + *  global variable "icomment" is set to 2 for documentation comments.
   1.393 + *
   1.394 + *  Global references: icomment  (private to "stripcom")
   1.395 + */
   1.396 +static void stripcom(unsigned char *line)
   1.397 +{
   1.398 +  char c;
   1.399 +  #if !defined SC_LIGHT
   1.400 +    #define COMMENT_LIMIT 100
   1.401 +    #define COMMENT_MARGIN 40   /* length of the longest word */
   1.402 +    char comment[COMMENT_LIMIT+COMMENT_MARGIN];
   1.403 +    int commentidx=0;
   1.404 +    int skipstar=TRUE;
   1.405 +    static int prev_singleline=FALSE;
   1.406 +    int singleline=prev_singleline;
   1.407 +
   1.408 +    prev_singleline=FALSE;  /* preset */
   1.409 +  #endif
   1.410 +
   1.411 +  while (*line){
   1.412 +    if (icomment!=0) {
   1.413 +      if (*line=='*' && *(line+1)=='/') {
   1.414 +        #if !defined SC_LIGHT
   1.415 +          if (icomment==2) {
   1.416 +            assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
   1.417 +            comment[commentidx]='\0';
   1.418 +            if (strlen(comment)>0)
   1.419 +              insert_docstring(comment);
   1.420 +          } /* if */
   1.421 +        #endif
   1.422 +        icomment=0;     /* comment has ended */
   1.423 +        *line=' ';      /* replace '*' and '/' characters by spaces */
   1.424 +        *(line+1)=' ';
   1.425 +        line+=2;
   1.426 +      } else {
   1.427 +        if (*line=='/' && *(line+1)=='*')
   1.428 +          error(216);   /* nested comment */
   1.429 +        #if !defined SC_LIGHT
   1.430 +          /* collect the comment characters in a string */
   1.431 +          if (icomment==2) {
   1.432 +            if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) {
   1.433 +              /* ignore leading whitespace and '*' characters */
   1.434 +            } else if (commentidx<COMMENT_LIMIT+COMMENT_MARGIN-1) {
   1.435 +              comment[commentidx++]=(char)((*line!='\n') ? *line : ' ');
   1.436 +              if (commentidx>COMMENT_LIMIT && *line!='\0' && *line<=' ') {
   1.437 +                comment[commentidx]='\0';
   1.438 +                insert_docstring(comment);
   1.439 +                commentidx=0;
   1.440 +              } /* if */
   1.441 +              skipstar=FALSE;
   1.442 +            } /* if */
   1.443 +          } /* if */
   1.444 +        #endif
   1.445 +        *line=' ';      /* replace comments by spaces */
   1.446 +        line+=1;
   1.447 +      } /* if */
   1.448 +    } else {
   1.449 +      if (*line=='/' && *(line+1)=='*'){
   1.450 +        icomment=1;     /* start comment */
   1.451 +        #if !defined SC_LIGHT
   1.452 +          /* there must be two "*" behind the slash and then white space */
   1.453 +          if (*(line+2)=='*' && *(line+3)<=' ') {
   1.454 +            /* if we are not in a function, we must attach the previous block
   1.455 +             * to the global documentation
   1.456 +             */
   1.457 +            if (curfunc==NULL && get_docstring(0)!=NULL)
   1.458 +              sc_attachdocumentation(NULL);
   1.459 +            icomment=2; /* documentation comment */
   1.460 +          } /* if */
   1.461 +          commentidx=0;
   1.462 +          skipstar=TRUE;
   1.463 +        #endif
   1.464 +        *line=' ';      /* replace '/' and '*' characters by spaces */
   1.465 +        *(line+1)=' ';
   1.466 +        line+=2;
   1.467 +        if (icomment==2)
   1.468 +          *line++=' ';
   1.469 +      } else if (*line=='/' && *(line+1)=='/'){  /* comment to end of line */
   1.470 +        if (strchr((char*)line,'\a')!=NULL)
   1.471 +          error(49);    /* invalid line continuation */
   1.472 +        #if !defined SC_LIGHT
   1.473 +          if (*(line+2)=='/' && *(line+3)<=' ') {
   1.474 +            /* documentation comment */
   1.475 +            char *str=(char*)line+3;
   1.476 +            char *end;
   1.477 +            while (*str<=' ' && *str!='\0')
   1.478 +              str++;    /* skip leading whitespace */
   1.479 +            if ((end=strrchr(str,'\n'))!=NULL)
   1.480 +              *end='\0';/* erase trailing '\n' */
   1.481 +            /* if there is a disjunct block, we may need to attach the previous
   1.482 +             * block to the global documentation
   1.483 +             */
   1.484 +            if (!singleline && curfunc==NULL && get_docstring(0)!=NULL)
   1.485 +              sc_attachdocumentation(NULL);
   1.486 +            insert_docstring(str);
   1.487 +            prev_singleline=TRUE;
   1.488 +          } /* if */
   1.489 +        #endif
   1.490 +        *line++='\n';   /* put "newline" at first slash */
   1.491 +        *line='\0';     /* put "zero-terminator" at second slash */
   1.492 +      } else {
   1.493 +        if (*line=='\"' || *line=='\''){        /* leave literals unaltered */
   1.494 +          c=*line;      /* ending quote, single or double */
   1.495 +          line+=1;
   1.496 +          while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0')
   1.497 +            line+=1;
   1.498 +          line+=1;      /* skip final quote */
   1.499 +        } else {
   1.500 +          line+=1;
   1.501 +        } /* if */
   1.502 +      } /* if */
   1.503 +    } /* if */
   1.504 +  } /* while */
   1.505 +  #if !defined SC_LIGHT
   1.506 +    if (icomment==2) {
   1.507 +      assert(commentidx<COMMENT_LIMIT+COMMENT_MARGIN);
   1.508 +      comment[commentidx]='\0';
   1.509 +      if (strlen(comment)>0)
   1.510 +        insert_docstring(comment);
   1.511 +    } /* if */
   1.512 +  #endif
   1.513 +}
   1.514 +
   1.515 +/*  btoi
   1.516 + *
   1.517 + *  Attempts to interpret a numeric symbol as a boolean value. On success
   1.518 + *  it returns the number of characters processed (so the line pointer can be
   1.519 + *  adjusted) and the value is stored in "val". Otherwise it returns 0 and
   1.520 + *  "val" is garbage.
   1.521 + *
   1.522 + *  A boolean value must start with "0b"
   1.523 + */
   1.524 +static int btoi(cell *val,const unsigned char *curptr)
   1.525 +{
   1.526 +  const unsigned char *ptr;
   1.527 +
   1.528 +  *val=0;
   1.529 +  ptr=curptr;
   1.530 +  if (*ptr=='0' && *(ptr+1)=='b') {
   1.531 +    ptr+=2;
   1.532 +    while (*ptr=='0' || *ptr=='1' || *ptr=='_') {
   1.533 +      if (*ptr!='_')
   1.534 +        *val=(*val<<1) | (*ptr-'0');
   1.535 +      ptr++;
   1.536 +    } /* while */
   1.537 +  } else {
   1.538 +    return 0;
   1.539 +  } /* if */
   1.540 +  if (alphanum(*ptr))   /* number must be delimited by non-alphanumeric char */
   1.541 +    return 0;
   1.542 +  else
   1.543 +    return (int)(ptr-curptr);
   1.544 +}
   1.545 +
   1.546 +/*  dtoi
   1.547 + *
   1.548 + *  Attempts to interpret a numeric symbol as a decimal value. On success
   1.549 + *  it returns the number of characters processed and the value is stored in
   1.550 + *  "val". Otherwise it returns 0 and "val" is garbage.
   1.551 + */
   1.552 +static int dtoi(cell *val,const unsigned char *curptr)
   1.553 +{
   1.554 +  const unsigned char *ptr;
   1.555 +
   1.556 +  *val=0;
   1.557 +  ptr=curptr;
   1.558 +  if (!isdigit(*ptr))   /* should start with digit */
   1.559 +    return 0;
   1.560 +  while (isdigit(*ptr) || *ptr=='_') {
   1.561 +    if (*ptr!='_')
   1.562 +      *val=(*val*10)+(*ptr-'0');
   1.563 +    ptr++;
   1.564 +  } /* while */
   1.565 +  if (alphanum(*ptr))   /* number must be delimited by non-alphanumerical */
   1.566 +    return 0;
   1.567 +  if (*ptr=='.' && isdigit(*(ptr+1)))
   1.568 +    return 0;           /* but a fractional part must not be present */
   1.569 +  return (int)(ptr-curptr);
   1.570 +}
   1.571 +
   1.572 +/*  htoi
   1.573 + *
   1.574 + *  Attempts to interpret a numeric symbol as a hexadecimal value. On
   1.575 + *  success it returns the number of characters processed and the value is
   1.576 + *  stored in "val". Otherwise it return 0 and "val" is garbage.
   1.577 + */
   1.578 +static int htoi(cell *val,const unsigned char *curptr)
   1.579 +{
   1.580 +  const unsigned char *ptr;
   1.581 +
   1.582 +  *val=0;
   1.583 +  ptr=curptr;
   1.584 +  if (!isdigit(*ptr))   /* should start with digit */
   1.585 +    return 0;
   1.586 +  if (*ptr=='0' && *(ptr+1)=='x') {     /* C style hexadecimal notation */
   1.587 +    ptr+=2;
   1.588 +    while (ishex(*ptr) || *ptr=='_') {
   1.589 +      if (*ptr!='_') {
   1.590 +        assert(ishex(*ptr));
   1.591 +        *val= *val<<4;
   1.592 +        if (isdigit(*ptr))
   1.593 +          *val+= (*ptr-'0');
   1.594 +        else
   1.595 +          *val+= (tolower(*ptr)-'a'+10);
   1.596 +      } /* if */
   1.597 +      ptr++;
   1.598 +    } /* while */
   1.599 +  } else {
   1.600 +    return 0;
   1.601 +  } /* if */
   1.602 +  if (alphanum(*ptr))
   1.603 +    return 0;
   1.604 +  else
   1.605 +    return (int)(ptr-curptr);
   1.606 +}
   1.607 +
   1.608 +#if defined __GNUC__
   1.609 +static double pow10(int value)
   1.610 +{
   1.611 +  double res=1.0;
   1.612 +  while (value>=4) {
   1.613 +    res*=10000.0;
   1.614 +    value-=5;
   1.615 +  } /* while */
   1.616 +  while (value>=2) {
   1.617 +    res*=100.0;
   1.618 +    value-=2;
   1.619 +  } /* while */
   1.620 +  while (value>=1) {
   1.621 +    res*=10.0;
   1.622 +    value-=1;
   1.623 +  } /* while */
   1.624 +  return res;
   1.625 +}
   1.626 +#endif
   1.627 +
   1.628 +/*  ftoi
   1.629 + *
   1.630 + *  Attempts to interpret a numeric symbol as a rational number, either as
   1.631 + *  IEEE 754 single/double precision floating point or as a fixed point integer.
   1.632 + *  On success it returns the number of characters processed and the value is
   1.633 + *  stored in "val". Otherwise it returns 0 and "val" is unchanged.
   1.634 + *
   1.635 + *  Pawn has stricter definition for rational numbers than most:
   1.636 + *  o  the value must start with a digit; ".5" is not a valid number, you
   1.637 + *     should write "0.5"
   1.638 + *  o  a period must appear in the value, even if an exponent is given; "2e3"
   1.639 + *     is not a valid number, you should write "2.0e3"
   1.640 + *  o  at least one digit must follow the period; "6." is not a valid number,
   1.641 + *     you should write "6.0"
   1.642 + */
   1.643 +static int ftoi(cell *val,const unsigned char *curptr)
   1.644 +{
   1.645 +  const unsigned char *ptr;
   1.646 +  double fnum,ffrac,fmult;
   1.647 +  unsigned long dnum,dbase;
   1.648 +  int i, ignore;
   1.649 +
   1.650 +  assert(rational_digits>=0 && rational_digits<9);
   1.651 +  for (i=0,dbase=1; i<rational_digits; i++)
   1.652 +    dbase*=10;
   1.653 +  fnum=0.0;
   1.654 +  dnum=0L;
   1.655 +  ptr=curptr;
   1.656 +  if (!isdigit(*ptr))   /* should start with digit */
   1.657 +    return 0;
   1.658 +  while (isdigit(*ptr) || *ptr=='_') {
   1.659 +    if (*ptr!='_') {
   1.660 +      fnum=(fnum*10.0)+(*ptr-'0');
   1.661 +      dnum=(dnum*10L)+(*ptr-'0')*dbase;
   1.662 +    } /* if */
   1.663 +    ptr++;
   1.664 +  } /* while */
   1.665 +  if (*ptr!='.')
   1.666 +    return 0;           /* there must be a period */
   1.667 +  ptr++;
   1.668 +  if (!isdigit(*ptr))   /* there must be at least one digit after the dot */
   1.669 +    return 0;
   1.670 +  ffrac=0.0;
   1.671 +  fmult=1.0;
   1.672 +  ignore=FALSE;
   1.673 +  while (isdigit(*ptr) || *ptr=='_') {
   1.674 +    if (*ptr!='_') {
   1.675 +      ffrac=(ffrac*10.0)+(*ptr-'0');
   1.676 +      fmult=fmult/10.0;
   1.677 +      dbase /= 10L;
   1.678 +      dnum += (*ptr-'0')*dbase;
   1.679 +      if (dbase==0L && sc_rationaltag && rational_digits>0 && !ignore) {
   1.680 +        error(222);     /* number of digits exceeds rational number precision */
   1.681 +        ignore=TRUE;
   1.682 +      } /* if */
   1.683 +    } /* if */
   1.684 +    ptr++;
   1.685 +  } /* while */
   1.686 +  fnum += ffrac*fmult;  /* form the number so far */
   1.687 +  if (*ptr=='e') {      /* optional fractional part */
   1.688 +    int exp,sign;
   1.689 +    ptr++;
   1.690 +    if (*ptr=='-') {
   1.691 +      sign=-1;
   1.692 +      ptr++;
   1.693 +    } else {
   1.694 +      sign=1;
   1.695 +    } /* if */
   1.696 +    if (!isdigit(*ptr)) /* 'e' should be followed by a digit */
   1.697 +      return 0;
   1.698 +    exp=0;
   1.699 +    while (isdigit(*ptr)) {
   1.700 +      exp=(exp*10)+(*ptr-'0');
   1.701 +      ptr++;
   1.702 +    } /* while */
   1.703 +    #if defined __GNUC__
   1.704 +      fmult=pow10(exp*sign);
   1.705 +    #else
   1.706 +      fmult=pow(10,exp*sign);
   1.707 +    #endif
   1.708 +    fnum *= fmult;
   1.709 +    dnum *= (unsigned long)(fmult+0.5);
   1.710 +  } /* if */
   1.711 +
   1.712 +  /* decide how to store the number */
   1.713 +  if (sc_rationaltag==0) {
   1.714 +    error(70);          /* rational number support was not enabled */
   1.715 +    *val=0;
   1.716 +  } else if (rational_digits==0) {
   1.717 +    /* floating point */
   1.718 +    #if PAWN_CELL_SIZE==32
   1.719 +      float value=(float)fnum;
   1.720 +      *val=*((cell *)&value);
   1.721 +      #if 0 /* SourceMod - not needed */
   1.722 +        /* I assume that the C/C++ compiler stores "float" values in IEEE 754
   1.723 +         * format (as mandated in the ANSI standard). Test this assumption
   1.724 +         * anyway.
   1.725 +         * Note: problems have been reported with GCC 3.2.x, version 3.3.x works.
   1.726 +         */
   1.727 +        { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
   1.728 +          uint32_t bit = 1;
   1.729 +          /* test 0.0 == all bits 0 */
   1.730 +          assert(*(uint32_t*)&test1==0x00000000L);
   1.731 +          /* test sign & magnitude format */
   1.732 +          assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
   1.733 +          /* test a known value */
   1.734 +          assert(*(uint32_t*)&test2==0x42480000L);
   1.735 +        }
   1.736 +      #endif
   1.737 +    #elif PAWN_CELL_SIZE==64
   1.738 +      *val=*((cell *)&fnum);
   1.739 +      #if 0 /* SourceMod - not needed */
   1.740 +        /* I assume that the C/C++ compiler stores "double" values in IEEE 754
   1.741 +         * format (as mandated in the ANSI standard).
   1.742 +         */
   1.743 +        { float test1 = 0.0, test2 = 50.0, test3 = -50.0;
   1.744 +          uint64_t bit = 1;
   1.745 +          /* test 0.0 == all bits 0 */
   1.746 +          assert(*(uint64_t*)&test1==0x00000000L);
   1.747 +          /* test sign & magnitude format */
   1.748 +          assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1)));
   1.749 +        }
   1.750 +      #endif
   1.751 +    #else
   1.752 +      #error Unsupported cell size
   1.753 +    #endif
   1.754 +  } else {
   1.755 +    /* fixed point */
   1.756 +    *val=(cell)dnum;
   1.757 +  } /* if */
   1.758 +
   1.759 +  return (int)(ptr-curptr);
   1.760 +}
   1.761 +
   1.762 +/*  number
   1.763 + *
   1.764 + *  Reads in a number (binary, decimal or hexadecimal). It returns the number
   1.765 + *  of characters processed or 0 if the symbol couldn't be interpreted as a
   1.766 + *  number (in this case the argument "val" remains unchanged). This routine
   1.767 + *  relies on the 'early dropout' implementation of the logical or (||)
   1.768 + *  operator.
   1.769 + *
   1.770 + *  Note: the routine doesn't check for a sign (+ or -). The - is checked
   1.771 + *        for at "hier2()" (in fact, it is viewed as an operator, not as a
   1.772 + *        sign) and the + is invalid (as in K&R C, and unlike ANSI C).
   1.773 + */
   1.774 +static int number(cell *val,const unsigned char *curptr)
   1.775 +{
   1.776 +  int i;
   1.777 +  cell value;
   1.778 +
   1.779 +  if ((i=btoi(&value,curptr))!=0      /* binary? */
   1.780 +      || (i=htoi(&value,curptr))!=0   /* hexadecimal? */
   1.781 +      || (i=dtoi(&value,curptr))!=0)  /* decimal? */
   1.782 +  {
   1.783 +    *val=value;
   1.784 +    return i;
   1.785 +  } else {
   1.786 +    return 0;                      /* else not a number */
   1.787 +  } /* if */
   1.788 +}
   1.789 +
   1.790 +static void chrcat(char *str,char chr)
   1.791 +{
   1.792 +  str=strchr(str,'\0');
   1.793 +  *str++=chr;
   1.794 +  *str='\0';
   1.795 +}
   1.796 +
   1.797 +static int preproc_expr(cell *val,int *tag)
   1.798 +{
   1.799 +  int result;
   1.800 +  int index;
   1.801 +  cell code_index;
   1.802 +  char *term;
   1.803 +
   1.804 +  /* Disable staging; it should be disabled already because
   1.805 +   * expressions may not be cut off half-way between conditional
   1.806 +   * compilations. Reset the staging index, but keep the code
   1.807 +   * index.
   1.808 +   */
   1.809 +  if (stgget(&index,&code_index)) {
   1.810 +    error(57);                          /* unfinished expression */
   1.811 +    stgdel(0,code_index);
   1.812 +    stgset(FALSE);
   1.813 +  } /* if */
   1.814 +  assert((lptr-pline)<(int)strlen((char*)pline));   /* lptr must point inside the string */
   1.815 +  #if !defined NO_DEFINE
   1.816 +    /* preprocess the string */
   1.817 +    substallpatterns(pline,sLINEMAX);
   1.818 +    assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */
   1.819 +  #endif
   1.820 +  /* append a special symbol to the string, so the expression
   1.821 +   * analyzer won't try to read a next line when it encounters
   1.822 +   * an end-of-line
   1.823 +   */
   1.824 +  assert(strlen((char*)pline)<sLINEMAX);
   1.825 +  term=strchr((char*)pline,'\0');
   1.826 +  assert(term!=NULL);
   1.827 +  chrcat((char*)pline,PREPROC_TERM);    /* the "DEL" code (see SC.H) */
   1.828 +  result=constexpr(val,tag,NULL);       /* get value (or 0 on error) */
   1.829 +  *term='\0';                           /* erase the token (if still present) */
   1.830 +  lexclr(FALSE);                        /* clear any "pushed" tokens */
   1.831 +  return result;
   1.832 +}
   1.833 +
   1.834 +/* getstring
   1.835 + * Returns returns a pointer behind the closing quote or to the other
   1.836 + * character that caused the input to be ended.
   1.837 + */
   1.838 +static const unsigned char *getstring(unsigned char *dest,int max,const unsigned char *line)
   1.839 +{
   1.840 +  assert(dest!=NULL && line!=NULL);
   1.841 +  *dest='\0';
   1.842 +  while (*line<=' ' && *line!='\0')
   1.843 +    line++;             /* skip whitespace */
   1.844 +  if (*line=='"') {
   1.845 +    int len=0;
   1.846 +    line++;             /* skip " */
   1.847 +    while (*line!='"' && *line!='\0') {
   1.848 +      if (len<max-1)
   1.849 +        dest[len++]=*line;
   1.850 +      line++;
   1.851 +    } /* if */
   1.852 +    dest[len]='\0';
   1.853 +    if (*line=='"')
   1.854 +      line++;           /* skip closing " */
   1.855 +    else
   1.856 +      error(37);        /* invalid string */
   1.857 +  } else {
   1.858 +    error(37);          /* invalid string */
   1.859 +  } /* if */
   1.860 +  return line;
   1.861 +}
   1.862 +
   1.863 +enum {
   1.864 +  CMD_NONE,
   1.865 +  CMD_TERM,
   1.866 +  CMD_EMPTYLINE,
   1.867 +  CMD_CONDFALSE,
   1.868 +  CMD_INCLUDE,
   1.869 +  CMD_DEFINE,
   1.870 +  CMD_IF,
   1.871 +  CMD_DIRECTIVE,
   1.872 +};
   1.873 +
   1.874 +/*  command
   1.875 + *
   1.876 + *  Recognizes the compiler directives. The function returns:
   1.877 + *     CMD_NONE         the line must be processed
   1.878 + *     CMD_TERM         a pending expression must be completed before processing further lines
   1.879 + *     Other value: the line must be skipped, because:
   1.880 + *     CMD_CONDFALSE    false "#if.." code
   1.881 + *     CMD_EMPTYLINE    line is empty
   1.882 + *     CMD_INCLUDE      the line contains a #include directive
   1.883 + *     CMD_DEFINE       the line contains a #subst directive
   1.884 + *     CMD_IF           the line contains a #if/#else/#endif directive
   1.885 + *     CMD_DIRECTIVE    the line contains some other compiler directive
   1.886 + *
   1.887 + *  Global variables: iflevel, ifstack (altered)
   1.888 + *                    lptr      (altered)
   1.889 + */
   1.890 +static int command(void)
   1.891 +{
   1.892 +  int tok,ret;
   1.893 +  cell val;
   1.894 +  char *str;
   1.895 +  int index;
   1.896 +  cell code_index;
   1.897 +
   1.898 +  while (*lptr<=' ' && *lptr!='\0')
   1.899 +    lptr+=1;
   1.900 +  if (*lptr=='\0')
   1.901 +    return CMD_EMPTYLINE;       /* empty line */
   1.902 +  if (*lptr!='#')
   1.903 +    return SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* it is not a compiler directive */
   1.904 +  /* compiler directive found */
   1.905 +  indent_nowarn=TRUE;           /* allow loose indentation" */
   1.906 +  lexclr(FALSE);                /* clear any "pushed" tokens */
   1.907 +  /* on a pending expression, force to return a silent ';' token and force to
   1.908 +   * re-read the line
   1.909 +   */
   1.910 +  if (!sc_needsemicolon && stgget(&index,&code_index)) {
   1.911 +    lptr=term_expr;
   1.912 +    return CMD_TERM;
   1.913 +  } /* if */
   1.914 +  tok=lex(&val,&str);
   1.915 +  ret=SKIPPING ? CMD_CONDFALSE : CMD_DIRECTIVE;  /* preset 'ret' to CMD_DIRECTIVE (most common case) */
   1.916 +  switch (tok) {
   1.917 +  case tpIF:                    /* conditional compilation */
   1.918 +    ret=CMD_IF;
   1.919 +    assert(iflevel>=0);
   1.920 +    if (iflevel>=sCOMP_STACK)
   1.921 +      error(102,"Conditional compilation stack"); /* table overflow */
   1.922 +    iflevel++;
   1.923 +    if (SKIPPING)
   1.924 +      break;                    /* break out of switch */
   1.925 +    skiplevel=iflevel;
   1.926 +    preproc_expr(&val,NULL);    /* get value (or 0 on error) */
   1.927 +    ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
   1.928 +    check_empty(lptr);
   1.929 +    break;
   1.930 +  case tpELSE:
   1.931 +  case tpELSEIF:
   1.932 +    ret=CMD_IF;
   1.933 +    assert(iflevel>=0);
   1.934 +    if (iflevel==0) {
   1.935 +      error(26);                /* no matching #if */
   1.936 +      errorset(sRESET,0);
   1.937 +    } else {
   1.938 +      /* check for earlier #else */
   1.939 +      if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) {
   1.940 +        if (tok==tpELSEIF)
   1.941 +          error(61);            /* #elseif directive may not follow an #else */
   1.942 +        else
   1.943 +          error(60);            /* multiple #else directives between #if ... #endif */
   1.944 +        errorset(sRESET,0);
   1.945 +      } else {
   1.946 +        assert(iflevel>0);
   1.947 +        /* if there has been a "parse mode" on this level, set "skip mode",
   1.948 +         * otherwise, clear "skip mode"
   1.949 +         */
   1.950 +        if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) {
   1.951 +          /* there has been a parse mode already on this level, so skip the rest */
   1.952 +          ifstack[iflevel-1] |= (char)SKIPMODE;
   1.953 +        } else {
   1.954 +          /* previous conditions were all FALSE */
   1.955 +          if (tok==tpELSEIF) {
   1.956 +            /* get new expression */
   1.957 +            preproc_expr(&val,NULL);  /* get value (or 0 on error) */
   1.958 +            ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE);
   1.959 +          } else {
   1.960 +            /* a simple #else, clear skip mode */
   1.961 +            ifstack[iflevel-1] &= (char)~SKIPMODE;
   1.962 +          } /* if */
   1.963 +        } /* if */
   1.964 +      } /* if */
   1.965 +    } /* if */
   1.966 +    check_empty(lptr);
   1.967 +    break;
   1.968 +  case tpENDIF:
   1.969 +    ret=CMD_IF;
   1.970 +    if (iflevel==0){
   1.971 +      error(26);        /* no matching "#if" */
   1.972 +      errorset(sRESET,0);
   1.973 +    } else {
   1.974 +      iflevel--;
   1.975 +      if (iflevel<skiplevel)
   1.976 +        skiplevel=iflevel;
   1.977 +    } /* if */
   1.978 +    check_empty(lptr);
   1.979 +    break;
   1.980 +  case tINCLUDE:                /* #include directive */
   1.981 +  case tpTRYINCLUDE:
   1.982 +    ret=CMD_INCLUDE;
   1.983 +    if (!SKIPPING)
   1.984 +      doinclude(tok==tpTRYINCLUDE);
   1.985 +    break;
   1.986 +  case tpFILE:
   1.987 +    if (!SKIPPING) {
   1.988 +      char pathname[_MAX_PATH];
   1.989 +      lptr=getstring((unsigned char*)pathname,sizeof pathname,lptr);
   1.990 +      if (strlen(pathname)>0) {
   1.991 +        free(inpfname);
   1.992 +        inpfname=duplicatestring(pathname);
   1.993 +        if (inpfname==NULL)
   1.994 +          error(103);           /* insufficient memory */
   1.995 +      } /* if */
   1.996 +    } /* if */
   1.997 +    check_empty(lptr);
   1.998 +    break;
   1.999 +  case tpLINE:
  1.1000 +    if (!SKIPPING) {
  1.1001 +      if (lex(&val,&str)!=tNUMBER)
  1.1002 +        error(8);               /* invalid/non-constant expression */
  1.1003 +      fline=(int)val;
  1.1004 +    } /* if */
  1.1005 +    check_empty(lptr);
  1.1006 +    break;
  1.1007 +  case tpASSERT:
  1.1008 +    if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) {
  1.1009 +      for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++)
  1.1010 +        /* nothing */;          /* save start of expression */
  1.1011 +      preproc_expr(&val,NULL);  /* get constant expression (or 0 on error) */
  1.1012 +      if (!val)
  1.1013 +        error(110,str);         /* assertion failed */
  1.1014 +      check_empty(lptr);
  1.1015 +    } /* if */
  1.1016 +    break;
  1.1017 +  case tpPRAGMA:
  1.1018 +    if (!SKIPPING) {
  1.1019 +      if (lex(&val,&str)==tSYMBOL) {
  1.1020 +        if (strcmp(str,"amxlimit")==0) {
  1.1021 +          preproc_expr(&pc_amxlimit,NULL);
  1.1022 +        } else if (strcmp(str,"amxram")==0) {
  1.1023 +          preproc_expr(&pc_amxram,NULL);
  1.1024 +        } else if (strcmp(str,"codepage")==0) {
  1.1025 +          char name[sNAMEMAX+1];
  1.1026 +          while (*lptr<=' ' && *lptr!='\0')
  1.1027 +            lptr++;
  1.1028 +          if (*lptr=='"') {
  1.1029 +            lptr=getstring((unsigned char*)name,sizeof name,lptr);
  1.1030 +          } else {
  1.1031 +            int i;
  1.1032 +            for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
  1.1033 +              name[i]=*lptr;
  1.1034 +            name[i]='\0';
  1.1035 +          } /* if */
  1.1036 +          if (!cp_set(name))
  1.1037 +            error(108);         /* codepage mapping file not found */
  1.1038 +        } else if (strcmp(str,"compress")==0) {
  1.1039 +          cell val;
  1.1040 +          preproc_expr(&val,NULL);
  1.1041 +          sc_compress=(int)val; /* switch code packing on/off */
  1.1042 +        } else if (strcmp(str,"ctrlchar")==0) {
  1.1043 +          while (*lptr<=' ' && *lptr!='\0')
  1.1044 +            lptr++;
  1.1045 +          if (*lptr=='\0') {
  1.1046 +            sc_ctrlchar=sc_ctrlchar_org;
  1.1047 +          } else {
  1.1048 +            if (lex(&val,&str)!=tNUMBER)
  1.1049 +              error(27);          /* invalid character constant */
  1.1050 +            sc_ctrlchar=(char)val;
  1.1051 +          } /* if */
  1.1052 +        } else if (strcmp(str,"depricated")==0) {
  1.1053 +          while (*lptr<=' ' && *lptr!='\0')
  1.1054 +            lptr++;
  1.1055 +          pc_depricate=(char*)malloc(strlen((char*)lptr)+1);
  1.1056 +          if (pc_depricate!=NULL)
  1.1057 +            strcpy(pc_depricate,(char*)lptr);
  1.1058 +          lptr=(unsigned char*)strchr((char*)lptr,'\0'); /* skip to end (ignore "extra characters on line") */
  1.1059 +        } else if (strcmp(str,"dynamic")==0) {
  1.1060 +          preproc_expr(&pc_stksize,NULL);
  1.1061 +        } else if (strcmp(str,"library")==0) {
  1.1062 +          char name[sNAMEMAX+1];
  1.1063 +          while (*lptr<=' ' && *lptr!='\0')
  1.1064 +            lptr++;
  1.1065 +          if (*lptr=='"') {
  1.1066 +            lptr=getstring((unsigned char*)name,sizeof name,lptr);
  1.1067 +          } else {
  1.1068 +            int i;
  1.1069 +            for (i=0; i<sizeof name && (alphanum(*lptr) || *lptr=='-'); i++,lptr++)
  1.1070 +              name[i]=*lptr;
  1.1071 +            name[i]='\0';
  1.1072 +          } /* if */
  1.1073 +          if (strlen(name)==0) {
  1.1074 +            curlibrary=NULL;
  1.1075 +          } else if (strcmp(name,"-")==0) {
  1.1076 +            pc_addlibtable=FALSE;
  1.1077 +          } else {
  1.1078 +            /* add the name if it does not yet exist in the table */
  1.1079 +            if (find_constval(&libname_tab,name,0)==NULL)
  1.1080 +              curlibrary=append_constval(&libname_tab,name,0,0);
  1.1081 +          } /* if */
  1.1082 +#if 0	/* more unused */
  1.1083 +        } else if (strcmp(str,"pack")==0) {
  1.1084 +          cell val;
  1.1085 +          preproc_expr(&val,NULL);      /* default = packed/unpacked */
  1.1086 +          sc_packstr=(int)val;
  1.1087 +#endif
  1.1088 +        } else if (strcmp(str,"rational")==0) {
  1.1089 +          char name[sNAMEMAX+1];
  1.1090 +          cell digits=0;
  1.1091 +          int i;
  1.1092 +          /* first gather all information, start with the tag name */
  1.1093 +          while (*lptr<=' ' && *lptr!='\0')
  1.1094 +            lptr++;
  1.1095 +          for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
  1.1096 +            name[i]=*lptr;
  1.1097 +          name[i]='\0';
  1.1098 +          /* then the precision (for fixed point arithmetic) */
  1.1099 +          while (*lptr<=' ' && *lptr!='\0')
  1.1100 +            lptr++;
  1.1101 +          if (*lptr=='(') {
  1.1102 +            preproc_expr(&digits,NULL);
  1.1103 +            if (digits<=0 || digits>9) {
  1.1104 +              error(68);        /* invalid rational number precision */
  1.1105 +              digits=0;
  1.1106 +            } /* if */
  1.1107 +            if (*lptr==')')
  1.1108 +              lptr++;
  1.1109 +          } /* if */
  1.1110 +          /* add the tag (make it public) and check the values */
  1.1111 +          i=pc_addtag(name);
  1.1112 +          exporttag(i);
  1.1113 +          if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) {
  1.1114 +            sc_rationaltag=i;
  1.1115 +            rational_digits=(int)digits;
  1.1116 +          } else {
  1.1117 +            error(69);          /* rational number format already set, can only be set once */
  1.1118 +          } /* if */
  1.1119 +        } else if (strcmp(str,"semicolon")==0) {
  1.1120 +          cell val;
  1.1121 +          preproc_expr(&val,NULL);
  1.1122 +          sc_needsemicolon=(int)val;
  1.1123 +        } else if (strcmp(str,"tabsize")==0) {
  1.1124 +          cell val;
  1.1125 +          preproc_expr(&val,NULL);
  1.1126 +          sc_tabsize=(int)val;
  1.1127 +        } else if (strcmp(str,"align")==0) {
  1.1128 +          sc_alignnext=TRUE;
  1.1129 +        } else if (strcmp(str,"unused")==0) {
  1.1130 +          char name[sNAMEMAX+1];
  1.1131 +          int i,comma;
  1.1132 +          symbol *sym;
  1.1133 +          do {
  1.1134 +            /* get the name */
  1.1135 +            while (*lptr<=' ' && *lptr!='\0')
  1.1136 +              lptr++;
  1.1137 +            for (i=0; i<sizeof name && alphanum(*lptr); i++,lptr++)
  1.1138 +              name[i]=*lptr;
  1.1139 +            name[i]='\0';
  1.1140 +            /* get the symbol */
  1.1141 +            sym=findloc(name);
  1.1142 +            if (sym==NULL)
  1.1143 +              sym=findglb(name,sSTATEVAR);
  1.1144 +            if (sym!=NULL) {
  1.1145 +              sym->usage |= uREAD;
  1.1146 +              if (sym->ident==iVARIABLE || sym->ident==iREFERENCE
  1.1147 +                  || sym->ident==iARRAY || sym->ident==iREFARRAY)
  1.1148 +                sym->usage |= uWRITTEN;
  1.1149 +            } else {
  1.1150 +              error(17,name);     /* undefined symbol */
  1.1151 +            } /* if */
  1.1152 +            /* see if a comma follows the name */
  1.1153 +            while (*lptr<=' ' && *lptr!='\0')
  1.1154 +              lptr++;
  1.1155 +            comma= (*lptr==',');
  1.1156 +            if (comma)
  1.1157 +              lptr++;
  1.1158 +          } while (comma);
  1.1159 +        } else {
  1.1160 +          error(207);           /* unknown #pragma */
  1.1161 +        } /* if */
  1.1162 +      } else {
  1.1163 +        error(207);             /* unknown #pragma */
  1.1164 +      } /* if */
  1.1165 +      check_empty(lptr);
  1.1166 +    } /* if */
  1.1167 +    break;
  1.1168 +  case tpENDINPUT:
  1.1169 +  case tpENDSCRPT:
  1.1170 +    if (!SKIPPING) {
  1.1171 +      check_empty(lptr);
  1.1172 +      assert(inpf!=NULL);
  1.1173 +      if (inpf!=inpf_org)
  1.1174 +        pc_closesrc(inpf);
  1.1175 +      inpf=NULL;
  1.1176 +    } /* if */
  1.1177 +    break;
  1.1178 +#if !defined NOEMIT
  1.1179 +  case tpEMIT: {
  1.1180 +    /* write opcode to output file */
  1.1181 +    char name[40];
  1.1182 +    int i;
  1.1183 +    while (*lptr<=' ' && *lptr!='\0')
  1.1184 +      lptr++;
  1.1185 +    for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++)
  1.1186 +      name[i]=(char)tolower(*lptr);
  1.1187 +    name[i]='\0';
  1.1188 +    stgwrite("\t");
  1.1189 +    stgwrite(name);
  1.1190 +    stgwrite(" ");
  1.1191 +    code_idx+=opcodes(1);
  1.1192 +    /* write parameter (if any) */
  1.1193 +    while (*lptr<=' ' && *lptr!='\0')
  1.1194 +      lptr++;
  1.1195 +    if (*lptr!='\0') {
  1.1196 +      symbol *sym;
  1.1197 +      tok=lex(&val,&str);
  1.1198 +      switch (tok) {
  1.1199 +      case tNUMBER:
  1.1200 +      case tRATIONAL:
  1.1201 +        outval(val,FALSE);
  1.1202 +        code_idx+=opargs(1);
  1.1203 +        break;
  1.1204 +      case tSYMBOL:
  1.1205 +        sym=findloc(str);
  1.1206 +        if (sym==NULL)
  1.1207 +          sym=findglb(str,sSTATEVAR);
  1.1208 +        if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) {
  1.1209 +          error(17,str);        /* undefined symbol */
  1.1210 +        } else {
  1.1211 +          outval(sym->addr,FALSE);
  1.1212 +          /* mark symbol as "used", unknown whether for read or write */
  1.1213 +          markusage(sym,uREAD | uWRITTEN);
  1.1214 +          code_idx+=opargs(1);
  1.1215 +        } /* if */
  1.1216 +        break;
  1.1217 +      default: {
  1.1218 +        char s2[20];
  1.1219 +        extern char *sc_tokens[];/* forward declaration */
  1.1220 +        if (tok<256)
  1.1221 +          sprintf(s2,"%c",(char)tok);
  1.1222 +        else
  1.1223 +          strcpy(s2,sc_tokens[tok-tFIRST]);
  1.1224 +        error(1,sc_tokens[tSYMBOL-tFIRST],s2);
  1.1225 +        break;
  1.1226 +      } /* case */
  1.1227 +      } /* switch */
  1.1228 +    } /* if */
  1.1229 +    stgwrite("\n");
  1.1230 +    check_empty(lptr);
  1.1231 +    break;
  1.1232 +  } /* case */
  1.1233 +#endif
  1.1234 +#if !defined NO_DEFINE
  1.1235 +  case tpDEFINE: {
  1.1236 +    ret=CMD_DEFINE;
  1.1237 +    if (!SKIPPING) {
  1.1238 +      char *pattern,*substitution;
  1.1239 +      const unsigned char *start,*end;
  1.1240 +      int count,prefixlen;
  1.1241 +      stringpair *def;
  1.1242 +      /* find the pattern to match */
  1.1243 +      while (*lptr<=' ' && *lptr!='\0')
  1.1244 +        lptr++;
  1.1245 +      start=lptr;       /* save starting point of the match pattern */
  1.1246 +      count=0;
  1.1247 +      while (*lptr>' ' && *lptr!='\0') {
  1.1248 +        litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */
  1.1249 +        count++;
  1.1250 +      } /* while */
  1.1251 +      end=lptr;
  1.1252 +      /* check pattern to match */
  1.1253 +      if (!alpha(*start)) {
  1.1254 +        error(74);      /* pattern must start with an alphabetic character */
  1.1255 +        break;
  1.1256 +      } /* if */
  1.1257 +      /* store matched pattern */
  1.1258 +      pattern=(char*)malloc(count+1);
  1.1259 +      if (pattern==NULL)
  1.1260 +        error(103);     /* insufficient memory */
  1.1261 +      lptr=start;
  1.1262 +      count=0;
  1.1263 +      while (lptr!=end) {
  1.1264 +        assert(lptr<end);
  1.1265 +        assert(*lptr!='\0');
  1.1266 +        pattern[count++]=(char)litchar(&lptr,0);
  1.1267 +      } /* while */
  1.1268 +      pattern[count]='\0';
  1.1269 +      /* special case, erase trailing variable, because it could match anything */
  1.1270 +      if (count>=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%')
  1.1271 +        pattern[count-2]='\0';
  1.1272 +      /* find substitution string */
  1.1273 +      while (*lptr<=' ' && *lptr!='\0')
  1.1274 +        lptr++;
  1.1275 +      start=lptr;       /* save starting point of the match pattern */
  1.1276 +      count=0;
  1.1277 +      end=NULL;
  1.1278 +      while (*lptr!='\0') {
  1.1279 +        /* keep position of the start of trailing whitespace */
  1.1280 +        if (*lptr<=' ') {
  1.1281 +          if (end==NULL)
  1.1282 +            end=lptr;
  1.1283 +        } else {
  1.1284 +          end=NULL;
  1.1285 +        } /* if */
  1.1286 +        count++;
  1.1287 +        lptr++;
  1.1288 +      } /* while */
  1.1289 +      if (end==NULL)
  1.1290 +        end=lptr;
  1.1291 +      /* store matched substitution */
  1.1292 +      substitution=(char*)malloc(count+1);  /* +1 for '\0' */
  1.1293 +      if (substitution==NULL)
  1.1294 +        error(103);     /* insufficient memory */
  1.1295 +      lptr=start;
  1.1296 +      count=0;
  1.1297 +      while (lptr!=end) {
  1.1298 +        assert(lptr<end);
  1.1299 +        assert(*lptr!='\0');
  1.1300 +        substitution[count++]=*lptr++;
  1.1301 +      } /* while */
  1.1302 +      substitution[count]='\0';
  1.1303 +      /* check whether the definition already exists */
  1.1304 +      for (prefixlen=0,start=(unsigned char*)pattern; alphanum(*start); prefixlen++,start++)
  1.1305 +        /* nothing */;
  1.1306 +      assert(prefixlen>0);
  1.1307 +      if ((def=find_subst(pattern,prefixlen))!=NULL) {
  1.1308 +        if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0)
  1.1309 +          error(201,pattern);   /* redefinition of macro (non-identical) */
  1.1310 +        delete_subst(pattern,prefixlen);
  1.1311 +      } /* if */
  1.1312 +      /* add the pattern/substitution pair to the list */
  1.1313 +      assert(strlen(pattern)>0);
  1.1314 +      insert_subst(pattern,substitution,prefixlen);
  1.1315 +      free(pattern);
  1.1316 +      free(substitution);
  1.1317 +    } /* if */
  1.1318 +    break;
  1.1319 +  } /* case */
  1.1320 +  case tpUNDEF:
  1.1321 +    if (!SKIPPING) {
  1.1322 +      if (lex(&val,&str)==tSYMBOL) {
  1.1323 +        ret=delete_subst(str,strlen(str));
  1.1324 +        if (!ret) {
  1.1325 +          /* also undefine normal constants */
  1.1326 +          symbol *sym=findconst(str,NULL);
  1.1327 +          if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) {
  1.1328 +            delete_symbol(&glbtab,sym);
  1.1329 +            ret=TRUE;
  1.1330 +          } /* if */
  1.1331 +        } /* if */
  1.1332 +        if (!ret)
  1.1333 +          error(17,str);        /* undefined symbol */
  1.1334 +      } else {
  1.1335 +        error(20,str);          /* invalid symbol name */
  1.1336 +      } /* if */
  1.1337 +      check_empty(lptr);
  1.1338 +    } /* if */
  1.1339 +    break;
  1.1340 +#endif
  1.1341 +  case tpERROR:
  1.1342 +    while (*lptr<=' ' && *lptr!='\0')
  1.1343 +      lptr++;
  1.1344 +    if (!SKIPPING)
  1.1345 +      error(111,lptr);  /* user error */
  1.1346 +    break;
  1.1347 +  default:
  1.1348 +    error(31);          /* unknown compiler directive */
  1.1349 +    ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE;  /* process as normal line */
  1.1350 +  } /* switch */
  1.1351 +  return ret;
  1.1352 +}
  1.1353 +
  1.1354 +#if !defined NO_DEFINE
  1.1355 +static int is_startstring(const unsigned char *string)
  1.1356 +{
  1.1357 +  if (*string=='\"' || *string=='\'')
  1.1358 +    return TRUE;                        /* "..." */
  1.1359 +
  1.1360 +  if (*string=='!') {
  1.1361 +    string++;
  1.1362 +    if (*string=='\"' || *string=='\'')
  1.1363 +      return TRUE;                      /* !"..." */
  1.1364 +    if (*string==sc_ctrlchar) {
  1.1365 +      string++;
  1.1366 +      if (*string=='\"' || *string=='\'')
  1.1367 +        return TRUE;                    /* !\"..." */
  1.1368 +    } /* if */
  1.1369 +  } else if (*string==sc_ctrlchar) {
  1.1370 +    string++;
  1.1371 +    if (*string=='\"' || *string=='\'')
  1.1372 +      return TRUE;                      /* \"..." */
  1.1373 +    if (*string=='!') {
  1.1374 +      string++;
  1.1375 +      if (*string=='\"' || *string=='\'')
  1.1376 +        return TRUE;                    /* \!"..." */
  1.1377 +    } /* if */
  1.1378 +  } /* if */
  1.1379 +
  1.1380 +  return FALSE;
  1.1381 +}
  1.1382 +
  1.1383 +static const unsigned char *skipstring(const unsigned char *string)
  1.1384 +{
  1.1385 +  char endquote;
  1.1386 +  int flags=0;
  1.1387 +
  1.1388 +  while (*string=='!' || *string==sc_ctrlchar) {
  1.1389 +    if (*string==sc_ctrlchar)
  1.1390 +      flags=RAWMODE;
  1.1391 +    string++;
  1.1392 +  } /* while */
  1.1393 +
  1.1394 +  endquote=*string;
  1.1395 +  assert(endquote=='"' || endquote=='\'');
  1.1396 +  string++;             /* skip open quote */
  1.1397 +  while (*string!=endquote && *string!='\0')
  1.1398 +    litchar(&string,flags);
  1.1399 +  return string;
  1.1400 +}
  1.1401 +
  1.1402 +static const unsigned char *skippgroup(const unsigned char *string)
  1.1403 +{
  1.1404 +  int nest=0;
  1.1405 +  char open=*string;
  1.1406 +  char close;
  1.1407 +
  1.1408 +  switch (open) {
  1.1409 +  case '(':
  1.1410 +    close=')';
  1.1411 +    break;
  1.1412 +  case '{':
  1.1413 +    close='}';
  1.1414 +    break;
  1.1415 +  case '[':
  1.1416 +    close=']';
  1.1417 +    break;
  1.1418 +  case '<':
  1.1419 +    close='>';
  1.1420 +    break;
  1.1421 +  default:
  1.1422 +    assert(0);
  1.1423 +	close='\0';         /* only to avoid a compiler warning */
  1.1424 +  }/* switch */
  1.1425 +
  1.1426 +  string++;
  1.1427 +  while (*string!=close || nest>0) {
  1.1428 +    if (*string==open)
  1.1429 +      nest++;
  1.1430 +    else if (*string==close)
  1.1431 +      nest--;
  1.1432 +    else if (is_startstring(string))
  1.1433 +      string=skipstring(string);
  1.1434 +    if (*string=='\0')
  1.1435 +      break;
  1.1436 +    string++;
  1.1437 +  } /* while */
  1.1438 +  return string;
  1.1439 +}
  1.1440 +
  1.1441 +static char *strdel(char *str,size_t len)
  1.1442 +{
  1.1443 +  size_t length=strlen(str);
  1.1444 +  if (len>length)
  1.1445 +    len=length;
  1.1446 +  memmove(str, str+len, length-len+1);  /* include EOS byte */
  1.1447 +  return str;
  1.1448 +}
  1.1449 +
  1.1450 +static char *strins(char *dest,char *src,size_t srclen)
  1.1451 +{
  1.1452 +  size_t destlen=strlen(dest);
  1.1453 +  assert(srclen<=strlen(src));
  1.1454 +  memmove(dest+srclen, dest, destlen+1);/* include EOS byte */
  1.1455 +  memcpy(dest, src, srclen);
  1.1456 +  return dest;
  1.1457 +}
  1.1458 +
  1.1459 +static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution)
  1.1460 +{
  1.1461 +  int prefixlen;
  1.1462 +  const unsigned char *p,*s,*e;
  1.1463 +  unsigned char *args[10];
  1.1464 +  int match,arg,len,argsnum=0;
  1.1465 +
  1.1466 +  memset(args,0,sizeof args);
  1.1467 +
  1.1468 +  /* check the length of the prefix */
  1.1469 +  for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++)
  1.1470 +    /* nothing */;
  1.1471 +  assert(prefixlen>0);
  1.1472 +  assert(strncmp((char*)line,pattern,prefixlen)==0);
  1.1473 +
  1.1474 +  /* pattern prefix matches; match the rest of the pattern, gather
  1.1475 +   * the parameters
  1.1476 +   */
  1.1477 +  s=line+prefixlen;
  1.1478 +  p=(unsigned char*)pattern+prefixlen;
  1.1479 +  match=TRUE;         /* so far, pattern matches */
  1.1480 +  while (match && *s!='\0' && *p!='\0') {
  1.1481 +    if (*p=='%') {
  1.1482 +      p++;            /* skip '%' */
  1.1483 +      if (isdigit(*p)) {
  1.1484 +        arg=*p-'0';
  1.1485 +        assert(arg>=0 && arg<=9);
  1.1486 +        p++;          /* skip parameter id */
  1.1487 +        assert(*p!='\0');
  1.1488 +        /* match the source string up to the character after the digit
  1.1489 +         * (skipping strings in the process
  1.1490 +         */
  1.1491 +        e=s;
  1.1492 +        while (*e!=*p && *e!='\0' && *e!='\n') {
  1.1493 +          if (is_startstring(e))              /* skip strings */
  1.1494 +            e=skipstring(e);
  1.1495 +          else if (strchr("({[",*e)!=NULL)    /* skip parenthized groups */
  1.1496 +            e=skippgroup(e);
  1.1497 +          if (*e!='\0')
  1.1498 +            e++;      /* skip non-alphapetic character (or closing quote of
  1.1499 +                       * a string, or the closing paranthese of a group) */
  1.1500 +        } /* while */
  1.1501 +        /* store the parameter (overrule any earlier) */
  1.1502 +        if (args[arg]!=NULL)
  1.1503 +          free(args[arg]);
  1.1504 +		else
  1.1505 +          argsnum++;
  1.1506 +        len=(int)(e-s);
  1.1507 +        args[arg]=(unsigned char*)malloc(len+1);
  1.1508 +        if (args[arg]==NULL)
  1.1509 +          error(103); /* insufficient memory */
  1.1510 +        strlcpy((char*)args[arg],(char*)s,len+1);
  1.1511 +        /* character behind the pattern was matched too */
  1.1512 +        if (*e==*p) {
  1.1513 +          s=e+1;
  1.1514 +        } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
  1.1515 +          s=e;    /* allow a trailing ; in the pattern match to end of line */
  1.1516 +        } else {
  1.1517 +          assert(*e=='\0' || *e=='\n');
  1.1518 +          match=FALSE;
  1.1519 +          s=e;
  1.1520 +        } /* if */
  1.1521 +        p++;
  1.1522 +      } else {
  1.1523 +        match=FALSE;
  1.1524 +      } /* if */
  1.1525 +    } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) {
  1.1526 +      /* source may be ';' or end of the line */
  1.1527 +      while (*s<=' ' && *s!='\0')
  1.1528 +        s++;          /* skip white space */
  1.1529 +      if (*s!=';' && *s!='\0')
  1.1530 +        match=FALSE;
  1.1531 +      p++;            /* skip the semicolon in the pattern */
  1.1532 +    } else {
  1.1533 +      cell ch;
  1.1534 +      /* skip whitespace between two non-alphanumeric characters, except
  1.1535 +       * for two identical symbols
  1.1536 +       */
  1.1537 +      assert((char*)p>pattern);
  1.1538 +      if (!alphanum(*p) && *(p-1)!=*p)
  1.1539 +        while (*s<=' ' && *s!='\0')
  1.1540 +          s++;                  /* skip white space */
  1.1541 +      ch=litchar(&p,0);         /* this increments "p" */
  1.1542 +      if (*s!=ch)
  1.1543 +        match=FALSE;
  1.1544 +      else
  1.1545 +        s++;                    /* this character matches */
  1.1546 +    } /* if */
  1.1547 +  } /* while */
  1.1548 +
  1.1549 +  if (match && *p=='\0') {
  1.1550 +    /* if the last character to match is an alphanumeric character, the
  1.1551 +     * current character in the source may not be alphanumeric
  1.1552 +     */
  1.1553 +    assert(p>(unsigned char*)pattern);
  1.1554 +    if (alphanum(*(p-1)) && alphanum(*s))
  1.1555 +      match=FALSE;
  1.1556 +  } /* if */
  1.1557 +
  1.1558 +  if (match) {
  1.1559 +    /* calculate the length of the substituted string */
  1.1560 +    for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) {
  1.1561 +      if (*e=='%' && isdigit(*(e+1)) && argsnum) {
  1.1562 +        arg=*(e+1)-'0';
  1.1563 +        assert(arg>=0 && arg<=9);
  1.1564 +        if (args[arg]!=NULL) {
  1.1565 +          len+=strlen((char*)args[arg]);
  1.1566 +          e++;
  1.1567 +        } else {
  1.1568 +          len++;
  1.1569 +        }
  1.1570 +      } else {
  1.1571 +        len++;
  1.1572 +      } /* if */
  1.1573 +    } /* for */
  1.1574 +    /* check length of the string after substitution */
  1.1575 +    if (strlen((char*)line) + len - (int)(s-line) > buffersize) {
  1.1576 +      error(75);      /* line too long */
  1.1577 +    } else {
  1.1578 +      /* substitute pattern */
  1.1579 +      strdel((char*)line,(int)(s-line));
  1.1580 +      for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) {
  1.1581 +        if (*e=='%' && isdigit(*(e+1))) {
  1.1582 +          arg=*(e+1)-'0';
  1.1583 +          assert(arg>=0 && arg<=9);
  1.1584 +          if (args[arg]!=NULL) {
  1.1585 +            strins((char*)s,(char*)args[arg],strlen((char*)args[arg]));
  1.1586 +            s+=strlen((char*)args[arg]);
  1.1587 +            e++;
  1.1588 +          } else {
  1.1589 +            error(236); /* parameter does not exist, incorrect #define pattern */
  1.1590 +            strins((char*)s,(char*)e,2);
  1.1591 +            s+=2;
  1.1592 +          } /* if */
  1.1593 +          e++;          /* skip %, digit is skipped later */
  1.1594 +        } else if (*e == '"') {
  1.1595 +          p=e;
  1.1596 +          if (is_startstring(e)) {
  1.1597 +            e=skipstring(e);
  1.1598 +            strins((char*)s,(char *)p,(e-p+1));
  1.1599 +            s+=(e-p+1);
  1.1600 +          } else {
  1.1601 +            strins((char*)s,(char*)e,1);
  1.1602 +            s++;
  1.1603 +          }
  1.1604 +        } else {
  1.1605 +          strins((char*)s,(char*)e,1);
  1.1606 +          s++;
  1.1607 +        } /* if */
  1.1608 +      } /* for */
  1.1609 +    } /* if */
  1.1610 +  } /* if */
  1.1611 +
  1.1612 +  for (arg=0; arg<10; arg++)
  1.1613 +    if (args[arg]!=NULL)
  1.1614 +      free(args[arg]);
  1.1615 +
  1.1616 +  return match;
  1.1617 +}
  1.1618 +
  1.1619 +static void substallpatterns(unsigned char *line,int buffersize)
  1.1620 +{
  1.1621 +  unsigned char *start, *end;
  1.1622 +  int prefixlen;
  1.1623 +  stringpair *subst;
  1.1624 +
  1.1625 +  start=line;
  1.1626 +  while (*start!='\0') {
  1.1627 +    /* find the start of a prefix (skip all non-alphabetic characters),
  1.1628 +     * also skip strings
  1.1629 +     */
  1.1630 +    while (!alpha(*start) && *start!='\0') {
  1.1631 +      /* skip strings */
  1.1632 +      if (is_startstring(start)) {
  1.1633 +        start=(unsigned char *)skipstring(start);
  1.1634 +        if (*start=='\0')
  1.1635 +          break;        /* abort loop on error */
  1.1636 +      } /* if */
  1.1637 +      start++;          /* skip non-alphapetic character (or closing quote of a string) */
  1.1638 +    } /* while */
  1.1639 +    if (*start=='\0')
  1.1640 +      break;            /* abort loop on error */
  1.1641 +    /* if matching the operator "defined", skip it plus the symbol behind it */
  1.1642 +	if (strncmp((char*)start,"defined",7)==0 && !isalpha((char)*(start+7))) {
  1.1643 +      start+=7;         /* skip "defined" */
  1.1644 +      /* skip white space & parantheses */
  1.1645 +      while (*start<=' ' && *start!='\0' || *start=='(')
  1.1646 +        start++;
  1.1647 +      /* skip the symbol behind it */
  1.1648 +      while (alphanum(*start))
  1.1649 +        start++;
  1.1650 +      /* drop back into the main loop */
  1.1651 +      continue;
  1.1652 +    } /* if */
  1.1653 +    /* get the prefix (length), look for a matching definition */
  1.1654 +    prefixlen=0;
  1.1655 +    end=start;
  1.1656 +    while (alphanum(*end)) {
  1.1657 +      prefixlen++;
  1.1658 +      end++;
  1.1659 +    } /* while */
  1.1660 +    assert(prefixlen>0);
  1.1661 +    subst=find_subst((char*)start,prefixlen);
  1.1662 +    if (subst!=NULL) {
  1.1663 +      /* properly match the pattern and substitute */
  1.1664 +      if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second))
  1.1665 +        start=end;      /* match failed, skip this prefix */
  1.1666 +      /* match succeeded: do not update "start", because the substitution text
  1.1667 +       * may be matched by other macros
  1.1668 +       */
  1.1669 +    } else {
  1.1670 +      start=end;        /* no macro with this prefix, skip this prefix */
  1.1671 +    } /* if */
  1.1672 +  } /* while */
  1.1673 +}
  1.1674 +#endif
  1.1675 +
  1.1676 +/*  preprocess
  1.1677 + *
  1.1678 + *  Reads a line by readline() into "pline" and performs basic preprocessing:
  1.1679 + *  deleting comments, skipping lines with false "#if.." code and recognizing
  1.1680 + *  other compiler directives. There is an indirect recursion: lex() calls
  1.1681 + *  preprocess() if a new line must be read, preprocess() calls command(),
  1.1682 + *  which at his turn calls lex() to identify the token.
  1.1683 + *
  1.1684 + *  Global references: lptr     (altered)
  1.1685 + *                     pline    (altered)
  1.1686 + *                     freading (referred to only)
  1.1687 + */
  1.1688 +SC_FUNC void preprocess(void)
  1.1689 +{
  1.1690 +  int iscommand;
  1.1691 +
  1.1692 +  if (!freading)
  1.1693 +    return;
  1.1694 +  do {
  1.1695 +    readline(pline);
  1.1696 +    stripcom(pline);    /* ??? no need for this when reading back from list file (in the second pass) */
  1.1697 +    lptr=pline;         /* set "line pointer" to start of the parsing buffer */
  1.1698 +    iscommand=command();
  1.1699 +    if (iscommand!=CMD_NONE)
  1.1700 +      errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */
  1.1701 +    #if !defined NO_DEFINE
  1.1702 +      if (iscommand==CMD_NONE) {
  1.1703 +        assert(lptr!=term_expr);
  1.1704 +        substallpatterns(pline,sLINEMAX);
  1.1705 +        lptr=pline;       /* reset "line pointer" to start of the parsing buffer */
  1.1706 +      } /* if */
  1.1707 +    #endif
  1.1708 +    if (sc_status==statFIRST && sc_listing && freading
  1.1709 +        && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE))
  1.1710 +    {
  1.1711 +      listline++;
  1.1712 +      if (fline!=listline) {
  1.1713 +        listline=fline;
  1.1714 +        setlinedirect(fline);
  1.1715 +      } /* if */
  1.1716 +      if (iscommand==CMD_EMPTYLINE)
  1.1717 +        pc_writeasm(outf,"\n");
  1.1718 +      else
  1.1719 +        pc_writeasm(outf,(char*)pline);
  1.1720 +    } /* if */
  1.1721 +  } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */
  1.1722 +}
  1.1723 +
  1.1724 +static const unsigned char *unpackedstring(const unsigned char *lptr,int flags)
  1.1725 +{
  1.1726 +  while (*lptr!='\"' && *lptr!='\0') {
  1.1727 +    if (*lptr=='\a') {          /* ignore '\a' (which was inserted at a line concatenation) */
  1.1728 +      lptr++;
  1.1729 +      continue;
  1.1730 +    } /* if */
  1.1731 +    litadd(litchar(&lptr,flags | UTF8MODE));  /* litchar() alters "lptr" */
  1.1732 +  } /* while */
  1.1733 +  litadd(0);                    /* terminate string */
  1.1734 +  return lptr;
  1.1735 +}
  1.1736 +
  1.1737 +static const unsigned char *packedstring(const unsigned char *lptr,int flags)
  1.1738 +{
  1.1739 +  int i;
  1.1740 +  ucell val,c;
  1.1741 +
  1.1742 +  i=sizeof(ucell)-(sCHARBITS/8); /* start at most significant byte */
  1.1743 +  val=0;
  1.1744 +  while (*lptr!='\"' && *lptr!='\0') {
  1.1745 +    if (*lptr=='\a') {          /* ignore '\a' (which was inserted at a line concatenation) */
  1.1746 +      lptr++;
  1.1747 +      continue;
  1.1748 +    } /* if */
  1.1749 +    c=litchar(&lptr,flags);     /* litchar() alters "lptr" */
  1.1750 +    if (c>=(ucell)(1 << sCHARBITS))
  1.1751 +      error(43);                /* character constant exceeds range */
  1.1752 +    val |= (c << 8*i);
  1.1753 +    if (i==0) {
  1.1754 +      litadd(val);
  1.1755 +      val=0;
  1.1756 +    } /* if */
  1.1757 +    i=(i+sizeof(ucell)-(sCHARBITS/8)) % sizeof(ucell);
  1.1758 +  } /* if */
  1.1759 +  /* save last code; make sure there is at least one terminating zero character */
  1.1760 +  if (i!=(int)(sizeof(ucell)-(sCHARBITS/8)))
  1.1761 +    litadd(val);        /* at least one zero character in "val" */
  1.1762 +  else
  1.1763 +    litadd(0);          /* add full cell of zeros */
  1.1764 +  return lptr;
  1.1765 +}
  1.1766 +
  1.1767 +/*  lex(lexvalue,lexsym)        Lexical Analysis
  1.1768 + *
  1.1769 + *  lex() first deletes leading white space, then checks for multi-character
  1.1770 + *  operators, keywords (including most compiler directives), numbers,
  1.1771 + *  labels, symbols and literals (literal characters are converted to a number
  1.1772 + *  and are returned as such). If every check fails, the line must contain
  1.1773 + *  a single-character operator. So, lex() returns this character. In the other
  1.1774 + *  case (something did match), lex() returns the number of the token. All
  1.1775 + *  these tokens have been assigned numbers above 255.
  1.1776 + *
  1.1777 + *  Some tokens have "attributes":
  1.1778 + *     tNUMBER        the value of the number is return in "lexvalue".
  1.1779 + *     tRATIONAL      the value is in IEEE 754 encoding or in fixed point
  1.1780 + *                    encoding in "lexvalue".
  1.1781 + *     tSYMBOL        the first sNAMEMAX characters of the symbol are
  1.1782 + *                    stored in a buffer, a pointer to this buffer is
  1.1783 + *                    returned in "lexsym".
  1.1784 + *     tLABEL         the first sNAMEMAX characters of the label are
  1.1785 + *                    stored in a buffer, a pointer to this buffer is
  1.1786 + *                    returned in "lexsym".
  1.1787 + *     tSTRING        the string is stored in the literal pool, the index
  1.1788 + *                    in the literal pool to this string is stored in
  1.1789 + *                    "lexvalue".
  1.1790 + *
  1.1791 + *  lex() stores all information (the token found and possibly its attribute)
  1.1792 + *  in global variables. This allows a token to be examined twice. If "_pushed"
  1.1793 + *  is true, this information is returned.
  1.1794 + *
  1.1795 + *  Global references: lptr          (altered)
  1.1796 + *                     fline         (referred to only)
  1.1797 + *                     litidx        (referred to only)
  1.1798 + *                     _lextok, _lexval, _lexstr
  1.1799 + *                     _pushed
  1.1800 + */
  1.1801 +
  1.1802 +static int _pushed;
  1.1803 +static int _lextok;
  1.1804 +static cell _lexval;
  1.1805 +static char _lexstr[sLINEMAX+1];
  1.1806 +static int _lexnewline;
  1.1807 +
  1.1808 +SC_FUNC void lexinit(void)
  1.1809 +{
  1.1810 +  stkidx=0;             /* index for pushstk() and popstk() */
  1.1811 +  iflevel=0;            /* preprocessor: nesting of "#if" is currently 0 */
  1.1812 +  skiplevel=0;          /* preprocessor: not currently skipping */
  1.1813 +  icomment=0;           /* currently not in a multiline comment */
  1.1814 +  _pushed=FALSE;        /* no token pushed back into lex */
  1.1815 +  _lexnewline=FALSE;
  1.1816 +}
  1.1817 +
  1.1818 +char *sc_tokens[] = {
  1.1819 +         "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=",
  1.1820 +         "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--",
  1.1821 +         "...", "..", "::",
  1.1822 +         "assert", "break", "case", "char", "const", "continue", "default",
  1.1823 +         "defined", "do", "else", "enum", "exit", "for", "forward", "goto",
  1.1824 +         "if", "native", "new", "decl", "operator", "public", "return", "sizeof",
  1.1825 +         "sleep", "state", "static", "stock", "switch", "tagof", "while",
  1.1826 +         "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput",
  1.1827 +         "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma",
  1.1828 +         "#tryinclude", "#undef",
  1.1829 +         ";", ";", "-integer value-", "-rational value-", "-identifier-",
  1.1830 +         "-label-", "-string-"
  1.1831 +       };
  1.1832 +
  1.1833 +SC_FUNC int lex(cell *lexvalue,char **lexsym)
  1.1834 +{
  1.1835 +  int i,toolong,newline,stringflags;
  1.1836 +  char **tokptr;
  1.1837 +  const unsigned char *starttoken;
  1.1838 +
  1.1839 +  if (_pushed) {
  1.1840 +    _pushed=FALSE;      /* reset "_pushed" flag */
  1.1841 +    *lexvalue=_lexval;
  1.1842 +    *lexsym=_lexstr;
  1.1843 +    return _lextok;
  1.1844 +  } /* if */
  1.1845 +
  1.1846 +  _lextok=0;            /* preset all values */
  1.1847 +  _lexval=0;
  1.1848 +  _lexstr[0]='\0';
  1.1849 +  *lexvalue=_lexval;
  1.1850 +  *lexsym=_lexstr;
  1.1851 +  _lexnewline=FALSE;
  1.1852 +  if (!freading)
  1.1853 +    return 0;
  1.1854 +
  1.1855 +  newline= (lptr==pline);       /* does lptr point to start of line buffer */
  1.1856 +  while (*lptr<=' ') {          /* delete leading white space */
  1.1857 +    if (*lptr=='\0') {
  1.1858 +      preprocess();             /* preprocess resets "lptr" */
  1.1859 +      if (!freading)
  1.1860 +        return 0;
  1.1861 +      if (lptr==term_expr)      /* special sequence to terminate a pending expression */
  1.1862 +        return (_lextok=tENDEXPR);
  1.1863 +      _lexnewline=TRUE;         /* set this after preprocess(), because
  1.1864 +                                 * preprocess() calls lex() recursively */
  1.1865 +      newline=TRUE;
  1.1866 +    } else {
  1.1867 +      lptr+=1;
  1.1868 +    } /* if */
  1.1869 +  } /* while */
  1.1870 +  if (newline) {
  1.1871 +    stmtindent=0;
  1.1872 +    for (i=0; i<(int)(lptr-pline); i++)
  1.1873 +      if (pline[i]=='\t' && sc_tabsize>0)
  1.1874 +        stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize);
  1.1875 +      else
  1.1876 +        stmtindent++;
  1.1877 +  } /* if */
  1.1878 +
  1.1879 +  i=tFIRST;
  1.1880 +  tokptr=sc_tokens;
  1.1881 +  while (i<=tMIDDLE) {  /* match multi-character operators */
  1.1882 +    if (*lptr==**tokptr && match(*tokptr,FALSE)) {
  1.1883 +      _lextok=i;
  1.1884 +      if (pc_docexpr)   /* optionally concatenate to documentation string */
  1.1885 +        insert_autolist(*tokptr);
  1.1886 +      return _lextok;
  1.1887 +    } /* if */
  1.1888 +    i+=1;
  1.1889 +    tokptr+=1;
  1.1890 +  } /* while */
  1.1891 +  while (i<=tLAST) {    /* match reserved words and compiler directives */
  1.1892 +    if (*lptr==**tokptr && match(*tokptr,TRUE)) {
  1.1893 +      _lextok=i;
  1.1894 +      errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
  1.1895 +      if (pc_docexpr)   /* optionally concatenate to documentation string */
  1.1896 +        insert_autolist(*tokptr);
  1.1897 +      return _lextok;
  1.1898 +    } /* if */
  1.1899 +    i+=1;
  1.1900 +    tokptr+=1;
  1.1901 +  } /* while */
  1.1902 +
  1.1903 +  starttoken=lptr;      /* save start pointer (for concatenating to documentation string) */
  1.1904 +  if ((i=number(&_lexval,lptr))!=0) {   /* number */
  1.1905 +    _lextok=tNUMBER;
  1.1906 +    *lexvalue=_lexval;
  1.1907 +    lptr+=i;
  1.1908 +  } else if ((i=ftoi(&_lexval,lptr))!=0) {
  1.1909 +    _lextok=tRATIONAL;
  1.1910 +    *lexvalue=_lexval;
  1.1911 +    lptr+=i;
  1.1912 +  } else if (alpha(*lptr)) {            /* symbol or label */
  1.1913 +    /*  Note: only sNAMEMAX characters are significant. The compiler
  1.1914 +     *        generates a warning if a symbol exceeds this length.
  1.1915 +     */
  1.1916 +    _lextok=tSYMBOL;
  1.1917 +    i=0;
  1.1918 +    toolong=0;
  1.1919 +    while (alphanum(*lptr)){
  1.1920 +      _lexstr[i]=*lptr;
  1.1921 +      lptr+=1;
  1.1922 +      if (i<sNAMEMAX)
  1.1923 +        i+=1;
  1.1924 +      else
  1.1925 +        toolong=1;
  1.1926 +    } /* while */
  1.1927 +    _lexstr[i]='\0';
  1.1928 +    if (toolong)
  1.1929 +      error(200,_lexstr,sNAMEMAX);  /* symbol too long, truncated to sNAMEMAX chars */
  1.1930 +    if (_lexstr[0]==PUBLIC_CHAR && _lexstr[1]=='\0') {
  1.1931 +      _lextok=PUBLIC_CHAR;  /* '@' all alone is not a symbol, it is an operator */
  1.1932 +    } else if (_lexstr[0]=='_' && _lexstr[1]=='\0') {
  1.1933 +      _lextok='_';      /* '_' by itself is not a symbol, it is a placeholder */
  1.1934 +    } /* if */
  1.1935 +    if (*lptr==':' && *(lptr+1)!=':' && _lextok!=PUBLIC_CHAR) {
  1.1936 +      if (sc_allowtags) {
  1.1937 +        _lextok=tLABEL; /* it wasn't a normal symbol, it was a label/tagname */
  1.1938 +        lptr+=1;        /* skip colon */
  1.1939 +      } else if (find_constval(&tagname_tab,_lexstr,0)!=NULL) {
  1.1940 +        /* this looks like a tag override (because a tag with this name
  1.1941 +         * exists), but tags are not allowed right now, so it is probably an
  1.1942 +         * error
  1.1943 +         */
  1.1944 +        error(220);
  1.1945 +      } /* if */
  1.1946 +    } /* if */
  1.1947 +  } else if (*lptr=='\"' || *lptr==sc_ctrlchar && *(lptr+1)=='\"')
  1.1948 +  {                                     /* unpacked string literal */
  1.1949 +    _lextok=tSTRING;
  1.1950 +    stringflags= (*lptr==sc_ctrlchar) ? RAWMODE : 0;
  1.1951 +    *lexvalue=_lexval=litidx;
  1.1952 +    lptr+=1;            /* skip double quote */
  1.1953 +    if ((stringflags & RAWMODE)!=0)
  1.1954 +      lptr+=1;          /* skip "escape" character too */
  1.1955 +    lptr=sc_packstr ? packedstring(lptr,stringflags) : unpackedstring(lptr,stringflags);
  1.1956 +    if (*lptr=='\"')
  1.1957 +      lptr+=1;          /* skip final quote */
  1.1958 +    else
  1.1959 +      error(37);        /* invalid (non-terminated) string */
  1.1960 +  } else if (*lptr=='!' && *(lptr+1)=='\"'
  1.1961 +             || *lptr=='!' && *(lptr+1)==sc_ctrlchar && *(lptr+2)=='\"'
  1.1962 +             || *lptr==sc_ctrlchar && *(lptr+1)=='!' && *(lptr+2)=='\"')
  1.1963 +  {                                     /* packed string literal */
  1.1964 +    _lextok=tSTRING;
  1.1965 +    stringflags= (*lptr==sc_ctrlchar || *(lptr+1)==sc_ctrlchar) ? RAWMODE : 0;
  1.1966 +    *lexvalue=_lexval=litidx;
  1.1967 +    lptr+=2;            /* skip exclamation point and double quote */
  1.1968 +    if ((stringflags & RAWMODE)!=0)
  1.1969 +      lptr+=1;          /* skip "escape" character too */
  1.1970 +    lptr=sc_packstr ? unpackedstring(lptr,stringflags) : packedstring(lptr,stringflags);
  1.1971 +    if (*lptr=='\"')
  1.1972 +      lptr+=1;          /* skip final quote */
  1.1973 +    else
  1.1974 +      error(37);        /* invalid (non-terminated) string */
  1.1975 +  } else if (*lptr=='\'') {             /* character literal */
  1.1976 +    lptr+=1;            /* skip quote */
  1.1977 +    _lextok=tNUMBER;
  1.1978 +    *lexvalue=_lexval=litchar(&lptr,UTF8MODE);
  1.1979 +    if (*lptr=='\'')
  1.1980 +      lptr+=1;          /* skip final quote */
  1.1981 +    else
  1.1982 +      error(27);        /* invalid character constant (must be one character) */
  1.1983 +  } else if (*lptr==';') {      /* semicolumn resets "error" flag */
  1.1984 +    _lextok=';';
  1.1985 +    lptr+=1;
  1.1986 +    errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/
  1.1987 +  } else {
  1.1988 +    _lextok=*lptr;      /* if every match fails, return the character */
  1.1989 +    lptr+=1;            /* increase the "lptr" pointer */
  1.1990 +  } /* if */
  1.1991 +
  1.1992 +  if (pc_docexpr) {     /* optionally concatenate to documentation string */
  1.1993 +    char *docstr=(char*)malloc(((int)(lptr-starttoken)+1)*sizeof(char));
  1.1994 +    if (docstr!=NULL) {
  1.1995 +      strlcpy(docstr,(char*)starttoken,(int)(lptr-starttoken)+1);
  1.1996 +      insert_autolist(docstr);
  1.1997 +      free(docstr);
  1.1998 +    } /* if */
  1.1999 +  } /* if */
  1.2000 +  return _lextok;
  1.2001 +}
  1.2002 +
  1.2003 +/*  lexpush
  1.2004 + *
  1.2005 + *  Pushes a token back, so the next call to lex() will return the token
  1.2006 + *  last examined, instead of a new token.
  1.2007 + *
  1.2008 + *  Only one token can be pushed back.
  1.2009 + *
  1.2010 + *  In fact, lex() already stores the information it finds into global
  1.2011 + *  variables, so all that is to be done is set a flag that informs lex()
  1.2012 + *  to read and return the information from these variables, rather than
  1.2013 + *  to read in a new token from the input file.
  1.2014 + */
  1.2015 +SC_FUNC void lexpush(void)
  1.2016 +{
  1.2017 +  assert(_pushed==FALSE);
  1.2018 +  _pushed=TRUE;
  1.2019 +}
  1.2020 +
  1.2021 +/*  lexclr
  1.2022 + *
  1.2023 + *  Sets the variable "_pushed" to 0 to make sure lex() will read in a new
  1.2024 + *  symbol (a not continue with some old one). This is required upon return
  1.2025 + *  from Assembler mode, and in a few cases after detecting an syntax error.
  1.2026 + */
  1.2027 +SC_FUNC void lexclr(int clreol)
  1.2028 +{
  1.2029 +  _pushed=FALSE;
  1.2030 +  if (clreol) {
  1.2031 +    lptr=(unsigned char*)strchr((char*)pline,'\0');
  1.2032 +    assert(lptr!=NULL);
  1.2033 +  } /* if */
  1.2034 +}
  1.2035 +
  1.2036 +/*  matchtoken
  1.2037 + *
  1.2038 + *  This routine is useful if only a simple check is needed. If the token
  1.2039 + *  differs from the one expected, it is pushed back.
  1.2040 + *  This function returns 1 for "token found" and 2 for "implied statement
  1.2041 + *  termination token" found --the statement termination is an end of line in
  1.2042 + *  an expression where there is no pending operation. Such an implied token
  1.2043 + *  (i.e. not present in the source code) should not be pushed back, which is
  1.2044 + *  why it is sometimes important to distinguish the two.
  1.2045 + */
  1.2046 +SC_FUNC int matchtoken(int token)
  1.2047 +{
  1.2048 +  cell val;
  1.2049 +  char *str;
  1.2050 +  int tok;
  1.2051 +
  1.2052 +  tok=lex(&val,&str);
  1.2053 +  if (tok==token || token==tTERM && (tok==';' || tok==tENDEXPR)) {
  1.2054 +    return 1;
  1.2055 +  } else if (!sc_needsemicolon && token==tTERM && (_lexnewline || !freading)) {
  1.2056 +    /* Push "tok" back, because it is the token following the implicit statement
  1.2057 +     * termination (newline) token.
  1.2058 +     */
  1.2059 +    lexpush();
  1.2060 +    return 2;
  1.2061 +  } else {
  1.2062 +    lexpush();
  1.2063 +    return 0;
  1.2064 +  } /* if */
  1.2065 +}
  1.2066 +
  1.2067 +/*  tokeninfo
  1.2068 + *
  1.2069 + *  Returns additional information of a token after using "matchtoken()"
  1.2070 + *  or needtoken(). It does no harm using this routine after a call to
  1.2071 + *  "lex()", but lex() already returns the same information.
  1.2072 + *
  1.2073 + *  The token itself is the return value. Normally, this one is already known.
  1.2074 + */
  1.2075 +SC_FUNC int tokeninfo(cell *val,char **str)
  1.2076 +{
  1.2077 +  /* if the token was pushed back, tokeninfo() returns the token and
  1.2078 +   * parameters of the *next* token, not of the *current* token.
  1.2079 +   */
  1.2080 +  assert(!_pushed);
  1.2081 +  *val=_lexval;
  1.2082 +  *str=_lexstr;
  1.2083 +  return _lextok;
  1.2084 +}
  1.2085 +
  1.2086 +/*  needtoken
  1.2087 + *
  1.2088 + *  This routine checks for a required token and gives an error message if
  1.2089 + *  it isn't there (and returns 0/FALSE in that case). Like function matchtoken(),
  1.2090 + *  this function returns 1 for "token found" and 2 for "statement termination
  1.2091 + *  token" found; see function matchtoken() for details.
  1.2092 + *
  1.2093 + *  Global references: _lextok;
  1.2094 + */
  1.2095 +SC_FUNC int needtoken(int token)
  1.2096 +{
  1.2097 +  char s1[20],s2[20];
  1.2098 +  int t;
  1.2099 +
  1.2100 +  if ((t=matchtoken(token))!=0) {
  1.2101 +    return t;
  1.2102 +  } else {
  1.2103 +    /* token already pushed back */
  1.2104 +    assert(_pushed);
  1.2105 +    if (token<256)
  1.2106 +      sprintf(s1,"%c",(char)token);        /* single character token */
  1.2107 +    else
  1.2108 +      strcpy(s1,sc_tokens[token-tFIRST]);  /* multi-character symbol */
  1.2109 +    if (!freading)
  1.2110 +      strcpy(s2,"-end of file-");
  1.2111 +    else if (_lextok<256)
  1.2112 +      sprintf(s2,"%c",(char)_lextok);
  1.2113 +    else
  1.2114 +      strcpy(s2,sc_tokens[_lextok-tFIRST]);
  1.2115 +    error(1,s1,s2);     /* expected ..., but found ... */
  1.2116 +    return FALSE;
  1.2117 +  } /* if */
  1.2118 +}
  1.2119 +
  1.2120 +/*  match
  1.2121 + *
  1.2122 + *  Compares a series of characters from the input file with the characters
  1.2123 + *  in "st" (that contains a token). If the token on the input file matches
  1.2124 + *  "st", the input file pointer "lptr" is adjusted to point to the next
  1.2125 + *  token, otherwise "lptr" remains unaltered.
  1.2126 + *
  1.2127 + *  If the parameter "end: is true, match() requires that the first character
  1.2128 + *  behind the recognized token is non-alphanumeric.
  1.2129 + *
  1.2130 + *  Global references: lptr   (altered)
  1.2131 + */
  1.2132 +static int match(char *st,int end)
  1.2133 +{
  1.2134 +  int k;
  1.2135 +  const unsigned char *ptr;
  1.2136 +
  1.2137 +  k=0;
  1.2138 +  ptr=lptr;
  1.2139 +  while (st[k]) {
  1.2140 +    if ((unsigned char)st[k]!=*ptr)
  1.2141 +      return 0;
  1.2142 +    k+=1;
  1.2143 +    ptr+=1;
  1.2144 +  } /* while */
  1.2145 +  if (end) {            /* symbol must terminate with non-alphanumeric char */
  1.2146 +    if (alphanum(*ptr))
  1.2147 +      return 0;
  1.2148 +  } /* if */
  1.2149 +  lptr=ptr;     /* match found, skip symbol */
  1.2150 +  return 1;
  1.2151 +}
  1.2152 +
  1.2153 +static void chk_grow_litq(void)
  1.2154 +{
  1.2155 +  if (litidx>=litmax) {
  1.2156 +    cell *p;
  1.2157 +
  1.2158 +    litmax+=sDEF_LITMAX;
  1.2159 +    p=(cell *)realloc(litq,litmax*sizeof(cell));
  1.2160 +    if (p==NULL)
  1.2161 +      error(102,"literal table");   /* literal table overflow (fatal error) */
  1.2162 +    litq=p;
  1.2163 +  } /* if */
  1.2164 +}
  1.2165 +
  1.2166 +/*  litadd
  1.2167 + *
  1.2168 + *  Adds a value at the end of the literal queue. The literal queue is used
  1.2169 + *  for literal strings used in functions and for initializing array variables.
  1.2170 + *
  1.2171 + *  Global references: litidx  (altered)
  1.2172 + *                     litq    (altered)
  1.2173 + */
  1.2174 +SC_FUNC void litadd(cell value)
  1.2175 +{
  1.2176 +  chk_grow_litq();
  1.2177 +  assert(litidx<litmax);
  1.2178 +  litq[litidx++]=value;
  1.2179 +}
  1.2180 +
  1.2181 +/*  litinsert
  1.2182 + *
  1.2183 + *  Inserts a value into the literal queue. This is sometimes necessary for
  1.2184 + *  initializing multi-dimensional arrays.
  1.2185 + *
  1.2186 + *  Global references: litidx  (altered)
  1.2187 + *                     litq    (altered)
  1.2188 + */
  1.2189 +SC_FUNC void litinsert(cell value,int pos)
  1.2190 +{
  1.2191 +  chk_grow_litq();
  1.2192 +  assert(litidx<litmax);
  1.2193 +  assert(pos>=0 && pos<=litidx);
  1.2194 +  memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell));
  1.2195 +  litidx++;
  1.2196 +  litq[pos]=value;
  1.2197 +}
  1.2198 +
  1.2199 +/*  litchar
  1.2200 + *
  1.2201 + *  Return current literal character and increase the pointer to point
  1.2202 + *  just behind this literal character.
  1.2203 + *
  1.2204 + *  Note: standard "escape sequences" are suported, but the backslash may be
  1.2205 + *        replaced by another character; the syntax '\ddd' is supported,
  1.2206 + *        but ddd must be decimal!
  1.2207 + */
  1.2208 +static cell litchar(const unsigned char **lptr,int flags)
  1.2209 +{
  1.2210 +  cell c=0;
  1.2211 +  const unsigned char *cptr;
  1.2212 +
  1.2213 +  cptr=*lptr;
  1.2214 +  if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) {  /* no escape character */
  1.2215 +    #if !defined NO_UTF8
  1.2216 +      if (sc_is_utf8 && (flags & UTF8MODE)!=0) {
  1.2217 +        c=get_utf8_char(cptr,&cptr);
  1.2218 +        assert(c>=0);   /* file was already scanned for conformance to UTF-8 */
  1.2219 +      } else {
  1.2220 +    #endif
  1.2221 +      #if !defined NO_CODEPAGE
  1.2222 +        c=cp_translate(cptr,&cptr);
  1.2223 +      #else
  1.2224 +        c=*cptr;
  1.2225 +        cptr+=1;
  1.2226 +      #endif
  1.2227 +    #if !defined NO_UTF8
  1.2228 +      } /* if */
  1.2229 +    #endif
  1.2230 +  } else {
  1.2231 +    cptr+=1;
  1.2232 +    if (*cptr==sc_ctrlchar) {
  1.2233 +      c=*cptr;          /* \\ == \ (the escape character itself) */
  1.2234 +      cptr+=1;
  1.2235 +    } else {
  1.2236 +      switch (*cptr) {
  1.2237 +      case 'a':         /* \a == audible alarm */
  1.2238 +        c=7;
  1.2239 +        cptr+=1;
  1.2240 +        break;
  1.2241 +      case 'b':         /* \b == backspace */
  1.2242 +        c=8;
  1.2243 +        cptr+=1;
  1.2244 +        break;
  1.2245 +      case 'e':         /* \e == escape */
  1.2246 +        c=27;
  1.2247 +        cptr+=1;
  1.2248 +        break;
  1.2249 +      case 'f':         /* \f == form feed */
  1.2250 +        c=12;
  1.2251 +        cptr+=1;
  1.2252 +        break;
  1.2253 +      case 'n':         /* \n == NewLine character */
  1.2254 +        c=10;
  1.2255 +        cptr+=1;
  1.2256 +        break;
  1.2257 +      case 'r':         /* \r == carriage return */
  1.2258 +        c=13;
  1.2259 +        cptr+=1;
  1.2260 +        break;
  1.2261 +      case 't':         /* \t == horizontal TAB */
  1.2262 +        c=9;
  1.2263 +        cptr+=1;
  1.2264 +        break;
  1.2265 +      case 'v':         /* \v == vertical TAB */
  1.2266 +        c=11;
  1.2267 +        cptr+=1;
  1.2268 +        break;
  1.2269 +      case 'x':
  1.2270 +        cptr+=1;
  1.2271 +        c=0;
  1.2272 +        while (ishex(*cptr)) {
  1.2273 +          if (isdigit(*cptr))
  1.2274 +            c=(c<<4)+(*cptr-'0');
  1.2275 +          else
  1.2276 +            c=(c<<4)+(tolower(*cptr)-'a'+10);
  1.2277 +          cptr++;
  1.2278 +        } /* while */
  1.2279 +        if (*cptr==';')
  1.2280 +          cptr++;       /* swallow a trailing ';' */
  1.2281 +        break;
  1.2282 +      case '\'':        /* \' == ' (single quote) */
  1.2283 +      case '"':         /* \" == " (single quote) */
  1.2284 +      case '%':         /* \% == % (percent) */
  1.2285 +        c=*cptr;
  1.2286 +        cptr+=1;
  1.2287 +        break;
  1.2288 +      default:
  1.2289 +        if (isdigit(*cptr)) {   /* \ddd */
  1.2290 +          c=0;
  1.2291 +          while (*cptr>='0' && *cptr<='9')  /* decimal! */
  1.2292 +            c=c*10 + *cptr++ - '0';
  1.2293 +          if (*cptr==';')
  1.2294 +            cptr++;     /* swallow a trailing ';' */
  1.2295 +        } else {
  1.2296 +          error(27);    /* invalid character constant */
  1.2297 +        } /* if */
  1.2298 +      } /* switch */
  1.2299 +    } /* if */
  1.2300 +  } /* if */
  1.2301 +  *lptr=cptr;
  1.2302 +  assert(c>=0);
  1.2303 +  return c;
  1.2304 +}
  1.2305 +
  1.2306 +/*  alpha
  1.2307 + *
  1.2308 + *  Test if character "c" is alphabetic ("a".."z"), an underscore ("_")
  1.2309 + *  or an "at" sign ("@"). The "@" is an extension to standard C.
  1.2310 + */
  1.2311 +static int alpha(char c)
  1.2312 +{
  1.2313 +  return (isalpha(c) || c=='_' || c==PUBLIC_CHAR);
  1.2314 +}
  1.2315 +
  1.2316 +/*  alphanum
  1.2317 + *
  1.2318 + *  Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@")
  1.2319 + */
  1.2320 +SC_FUNC int alphanum(char c)
  1.2321 +{
  1.2322 +  return (alpha(c) || isdigit(c));
  1.2323 +}
  1.2324 +
  1.2325 +/*  ishex
  1.2326 + *
  1.2327 + *  Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f").
  1.2328 + */
  1.2329 +SC_FUNC int ishex(char c)
  1.2330 +{
  1.2331 +  return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
  1.2332 +}
  1.2333 +
  1.2334 +/* The local variable table must be searched backwards, so that the deepest
  1.2335 + * nesting of local variables is searched first. The simplest way to do
  1.2336 + * this is to insert all new items at the head of the list.
  1.2337 + * In the global list, the symbols are kept in sorted order, so that the
  1.2338 + * public functions are written in sorted order.
  1.2339 + */
  1.2340 +static symbol *add_symbol(symbol *root,symbol *entry,int sort)
  1.2341 +{
  1.2342 +  symbol *newsym;
  1.2343 +
  1.2344 +  if (sort)
  1.2345 +    while (root->next!=NULL && strcmp(entry->name,root->next->name)>0)
  1.2346 +      root=root->next;
  1.2347 +
  1.2348 +  if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) {
  1.2349 +    error(103);
  1.2350 +    return NULL;
  1.2351 +  } /* if */
  1.2352 +  memcpy(newsym,entry,sizeof(symbol));
  1.2353 +  newsym->next=root->next;
  1.2354 +  root->next=newsym;
  1.2355 +  return newsym;
  1.2356 +}
  1.2357 +
  1.2358 +static void free_symbol(symbol *sym)
  1.2359 +{
  1.2360 +  arginfo *arg;
  1.2361 +
  1.2362 +  /* free all sub-symbol allocated memory blocks, depending on the
  1.2363 +   * kind of the symbol
  1.2364 +   */
  1.2365 +  assert(sym!=NULL);
  1.2366 +  if (sym->ident==iFUNCTN) {
  1.2367 +    /* run through the argument list; "default array" arguments
  1.2368 +     * must be freed explicitly; the tag list must also be freed */
  1.2369 +    assert(sym->dim.arglist!=NULL);
  1.2370 +    for (arg=sym->dim.arglist; arg->ident!=0; arg++) {
  1.2371 +      if (arg->ident==iREFARRAY && arg->hasdefault)
  1.2372 +        free(arg->defvalue.array.data);
  1.2373 +      else if (arg->ident==iVARIABLE
  1.2374 +               && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0))
  1.2375 +        free(arg->defvalue.size.symname);
  1.2376 +      assert(arg->tags!=NULL);
  1.2377 +      free(arg->tags);
  1.2378 +    } /* for */
  1.2379 +    free(sym->dim.arglist);
  1.2380 +    if (sym->states!=NULL) {
  1.2381 +      delete_consttable(sym->states);
  1.2382 +      free(sym->states);
  1.2383 +    } /* if */
  1.2384 +  } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) {
  1.2385 +    if (sym->states!=NULL) {
  1.2386 +      delete_consttable(sym->states);
  1.2387 +      free(sym->states);
  1.2388 +    } /* if */
  1.2389 +  } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) {
  1.2390 +    /* free the constant list of an enum root */
  1.2391 +    assert(sym->dim.enumlist!=NULL);
  1.2392 +    delete_consttable(sym->dim.enumlist);
  1.2393 +    free(sym->dim.enumlist);
  1.2394 +  } /* if */
  1.2395 +  assert(sym->refer!=NULL);
  1.2396 +  free(sym->refer);
  1.2397 +  if (sym->documentation!=NULL)
  1.2398 +    free(sym->documentation);
  1.2399 +  free(sym);
  1.2400 +}
  1.2401 +
  1.2402 +SC_FUNC void delete_symbol(symbol *root,symbol *sym)
  1.2403 +{
  1.2404 +  /* find the symbol and its predecessor
  1.2405 +   * (this function assumes that you will never delete a symbol that is not
  1.2406 +   * in the table pointed at by "root")
  1.2407 +   */
  1.2408 +  assert(root!=sym);
  1.2409 +  while (root->next!=sym) {
  1.2410 +    root=root->next;
  1.2411 +    assert(root!=NULL);
  1.2412 +  } /* while */
  1.2413 +
  1.2414 +  /* unlink it, then free it */
  1.2415 +  root->next=sym->next;
  1.2416 +  free_symbol(sym);
  1.2417 +}
  1.2418 +
  1.2419 +SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions)
  1.2420 +{
  1.2421 +  symbol *sym,*parent_sym;
  1.2422 +  constvalue *stateptr;
  1.2423 +  int mustdelete;
  1.2424 +
  1.2425 +  /* erase only the symbols with a deeper nesting level than the
  1.2426 +   * specified nesting level */
  1.2427 +  while (root->next!=NULL) {
  1.2428 +    sym=root->next;
  1.2429 +    if (sym->compound<level)
  1.2430 +      break;
  1.2431 +    switch (sym->ident) {
  1.2432 +    case iLABEL:
  1.2433 +      mustdelete=delete_labels;
  1.2434 +      break;
  1.2435 +    case iVARIABLE:
  1.2436 +    case iARRAY:
  1.2437 +      /* do not delete global variables if functions are preserved */
  1.2438 +      mustdelete=delete_functions;
  1.2439 +      break;
  1.2440 +    case iREFERENCE:
  1.2441 +      /* always delete references (only exist as function parameters) */
  1.2442 +      mustdelete=TRUE;
  1.2443 +      break;
  1.2444 +    case iREFARRAY:
  1.2445 +      /* a global iREFARRAY symbol is the return value of a function: delete
  1.2446 +       * this only if "globals" must be deleted; other iREFARRAY instances
  1.2447 +       * (locals) are also deleted
  1.2448 +       */
  1.2449 +      mustdelete=delete_functions;
  1.2450 +      for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent)
  1.2451 +        assert(parent_sym->ident==iREFARRAY);
  1.2452 +      assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL));
  1.2453 +      if (parent_sym==NULL || parent_sym->ident!=iFUNCTN)
  1.2454 +        mustdelete=TRUE;
  1.2455 +      break;
  1.2456 +    case iCONSTEXPR:
  1.2457 +      /* delete constants, except predefined constants */
  1.2458 +      mustdelete=delete_functions || (sym->usage & uPREDEF)==0;
  1.2459 +      break;
  1.2460 +    case iFUNCTN:
  1.2461 +      /* optionally preserve globals (variables & functions), but
  1.2462 +       * NOT native functions
  1.2463 +       */
  1.2464 +      mustdelete=delete_functions || (sym->usage & uNATIVE)!=0;
  1.2465 +      assert(sym->parent==NULL);
  1.2466 +      break;
  1.2467 +    case iARRAYCELL:
  1.2468 +    case iARRAYCHAR:
  1.2469 +    case iEXPRESSION:
  1.2470 +    case iVARARGS:
  1.2471 +    default:
  1.2472 +      assert(0);
  1.2473 +      break;
  1.2474 +    } /* switch */
  1.2475 +    if (mustdelete) {
  1.2476 +      root->next=sym->next;
  1.2477 +      free_symbol(sym);
  1.2478 +    } else {
  1.2479 +      /* if the function was prototyped, but not implemented in this source,
  1.2480 +       * mark it as such, so that its use can be flagged
  1.2481 +       */
  1.2482 +      if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0)
  1.2483 +        sym->usage |= uMISSING;
  1.2484 +      if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY)
  1.2485 +        sym->usage &= ~uDEFINE; /* clear "defined" flag */
  1.2486 +      /* set all states as "undefined" too */
  1.2487 +      if (sym->states!=NULL)
  1.2488 +        for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next)
  1.2489 +          stateptr->value=0;
  1.2490 +      /* for user defined operators, also remove the "prototyped" flag, as
  1.2491 +       * user-defined operators *must* be declared before use
  1.2492 +       */
  1.2493 +      if (sym->ident==iFUNCTN && !alpha(*sym->name))
  1.2494 +        sym->usage &= ~uPROTOTYPED;
  1.2495 +      root=sym;                 /* skip the symbol */
  1.2496 +    } /* if */
  1.2497 +  } /* if */
  1.2498 +}
  1.2499 +
  1.2500 +/* The purpose of the hash is to reduce the frequency of a "name"
  1.2501 + * comparison (which is costly). There is little interest in avoiding
  1.2502 + * clusters in similar names, which is why this function is plain simple.
  1.2503 + */
  1.2504 +SC_FUNC uint32_t namehash(const char *name)
  1.2505 +{
  1.2506 +  const unsigned char *ptr=(const unsigned char *)name;
  1.2507 +  int len=strlen(name);
  1.2508 +  if (len==0)
  1.2509 +    return 0L;
  1.2510 +  assert(len<256);
  1.2511 +  return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]);
  1.2512 +}
  1.2513 +
  1.2514 +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag)
  1.2515 +{
  1.2516 +  symbol *firstmatch=NULL;
  1.2517 +  symbol *sym=root->next;
  1.2518 +  int count=0;
  1.2519 +  unsigned long hash=namehash(name);
  1.2520 +  while (sym!=NULL) {
  1.2521 +    if (hash==sym->hash && strcmp(name,sym->name)==0        /* check name */
  1.2522 +        && (sym->parent==NULL || sym->ident==iCONSTEXPR)    /* sub-types (hierarchical types) are skipped, except for enum fields */
  1.2523 +        && (sym->fnumber<0 || sym->fnumber==fnumber))       /* check file number for scope */
  1.2524 +    {
  1.2525 +      assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */
  1.2526 +      if (sym->ident==iFUNCTN
  1.2527 +          || automaton<0 && sym->states==NULL
  1.2528 +          || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton)
  1.2529 +      {
  1.2530 +        if (cmptag==NULL)
  1.2531 +          return sym;   /* return first match */
  1.2532 +        /* return closest match or first match; count number of matches */
  1.2533 +        if (firstmatch==NULL)
  1.2534 +          firstmatch=sym;
  1.2535 +        assert(cmptag!=NULL);
  1.2536 +        if (*cmptag==0)
  1.2537 +          count++;
  1.2538 +        if (*cmptag==sym->tag) {
  1.2539 +          *cmptag=1;    /* good match found, set number of matches to 1 */
  1.2540 +          return sym;
  1.2541 +        } /* if */
  1.2542 +      } /* if */
  1.2543 +    } /*  */
  1.2544 +    sym=sym->next;
  1.2545 +  } /* while */
  1.2546 +  if (cmptag!=NULL && firstmatch!=NULL)
  1.2547 +    *cmptag=count;
  1.2548 +  return firstmatch;
  1.2549 +}
  1.2550 +
  1.2551 +static symbol *find_symbol_child(const symbol *root,const symbol *sym)
  1.2552 +{
  1.2553 +  symbol *ptr=root->next;
  1.2554 +  while (ptr!=NULL) {
  1.2555 +    if (ptr->parent==sym)
  1.2556 +      return ptr;
  1.2557 +    ptr=ptr->next;
  1.2558 +  } /* while */
  1.2559 +  return NULL;
  1.2560 +}
  1.2561 +
  1.2562 +/* Adds "bywhom" to the list of referrers of "entry". Typically,
  1.2563 + * bywhom will be the function that uses a variable or that calls
  1.2564 + * the function.
  1.2565 + */
  1.2566 +SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom)
  1.2567 +{
  1.2568 +  int count;
  1.2569 +
  1.2570 +  assert(bywhom!=NULL);         /* it makes no sense to add a "void" referrer */
  1.2571 +  assert(entry!=NULL);
  1.2572 +  assert(entry->refer!=NULL);
  1.2573 +
  1.2574 +  /* see if it is already there */
  1.2575 +  for (count=0; count<entry->numrefers && entry->refer[count]!=bywhom; count++)
  1.2576 +    /* nothing */;
  1.2577 +  if (count<entry->numrefers) {
  1.2578 +    assert(entry->refer[count]==bywhom);
  1.2579 +    return TRUE;
  1.2580 +  } /* if */
  1.2581 +
  1.2582 +  /* see if there is an empty spot in the referrer list */
  1.2583 +  for (count=0; count<entry->numrefers && entry->refer[count]!=NULL; count++)
  1.2584 +    /* nothing */;
  1.2585 +  assert(count <= entry->numrefers);
  1.2586 +  if (count==entry->numrefers) {
  1.2587 +    symbol **refer;
  1.2588 +    int newsize=2*entry->numrefers;
  1.2589 +    assert(newsize>0);
  1.2590 +    /* grow the referrer list */
  1.2591 +    refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*));
  1.2592 +    if (refer==NULL)
  1.2593 +      return FALSE;             /* insufficient memory */
  1.2594 +    /* initialize the new entries */
  1.2595 +    entry->refer=refer;
  1.2596 +    for (count=entry->numrefers; count<newsize; count++)
  1.2597 +      entry->refer[count]=NULL;
  1.2598 +    count=entry->numrefers;     /* first empty spot */
  1.2599 +    entry->numrefers=newsize;
  1.2600 +  } /* if */
  1.2601 +
  1.2602 +  /* add the referrer */
  1.2603 +  assert(entry->refer[count]==NULL);
  1.2604 +  entry->refer[count]=bywhom;
  1.2605 +  return TRUE;
  1.2606 +}
  1.2607 +
  1.2608 +SC_FUNC void markusage(symbol *sym,int usage)
  1.2609 +{
  1.2610 +  assert(sym!=NULL);
  1.2611 +  sym->usage |= (char)usage;
  1.2612 +  if ((usage & uWRITTEN)!=0)
  1.2613 +    sym->lnumber=fline;
  1.2614 +  /* check if (global) reference must be added to the symbol */
  1.2615 +  if ((usage & (uREAD | uWRITTEN))!=0) {
  1.2616 +    /* only do this for global symbols */
  1.2617 +    if (sym->vclass==sGLOBAL) {
  1.2618 +      /* "curfunc" should always be valid, since statements may not occurs
  1.2619 +       * outside functions; in the case of syntax errors, however, the
  1.2620 +       * compiler may arrive through this function
  1.2621 +       */
  1.2622 +      if (curfunc!=NULL)
  1.2623 +        refer_symbol(sym,curfunc);
  1.2624 +    } /* if */
  1.2625 +  } /* if */
  1.2626 +}
  1.2627 +
  1.2628 +
  1.2629 +/*  findglb
  1.2630 + *
  1.2631 + *  Returns a pointer to the global symbol (if found) or NULL (if not found)
  1.2632 + */
  1.2633 +SC_FUNC symbol *findglb(const char *name,int filter)
  1.2634 +{
  1.2635 +  /* find a symbol with a matching automaton first */
  1.2636 +  symbol *sym=NULL;
  1.2637 +
  1.2638 +  if (filter>sGLOBAL && sc_curstates>0) {
  1.2639 +    /* find a symbol whose state list matches the current fsa */
  1.2640 +    sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL);
  1.2641 +    if (sym!=NULL && sym->ident!=iFUNCTN) {
  1.2642 +      /* if sym!=NULL, we found a variable in the automaton; now we should
  1.2643 +       * also verify whether there is an intersection between the symbol's
  1.2644 +       * state list and the current state list
  1.2645 +       */
  1.2646 +      assert(sym->states!=NULL && sym->states->next!=NULL);
  1.2647 +      if (!state_conflict_id(sc_curstates,sym->states->next->index))
  1.2648 +        sym=NULL;
  1.2649 +    } /* if */
  1.2650 +  } /* if */
  1.2651 +
  1.2652 +  /* if no symbol with a matching automaton exists, find a variable/function
  1.2653 +   * that has no state(s) attached to it
  1.2654 +   */
  1.2655 +  if (sym==NULL)
  1.2656 +    sym=find_symbol(&glbtab,name,fcurrent,-1,NULL);
  1.2657 +  return sym;
  1.2658 +}
  1.2659 +
  1.2660 +/*  findloc
  1.2661 + *
  1.2662 + *  Returns a pointer to the local symbol (if found) or NULL (if not found).
  1.2663 + *  See add_symbol() how the deepest nesting level is searched first.
  1.2664 + */
  1.2665 +SC_FUNC symbol *findloc(const char *name)
  1.2666 +{
  1.2667 +  return find_symbol(&loctab,name,-1,-1,NULL);
  1.2668 +}
  1.2669 +
  1.2670 +SC_FUNC symbol *findconst(const char *name,int *cmptag)
  1.2671 +{
  1.2672 +  symbol *sym;
  1.2673 +
  1.2674 +  sym=find_symbol(&loctab,name,-1,-1,cmptag);  /* try local symbols first */
  1.2675 +  if (sym==NULL || sym->ident!=iCONSTEXPR)     /* not found, or not a constant */
  1.2676 +    sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag);
  1.2677 +  if (sym==NULL || sym->ident!=iCONSTEXPR)
  1.2678 +    return NULL;
  1.2679 +  assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0);
  1.2680 +  /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */
  1.2681 +  return sym;
  1.2682 +}
  1.2683 +
  1.2684 +SC_FUNC symbol *finddepend(const symbol *parent)
  1.2685 +{
  1.2686 +  symbol *sym;
  1.2687 +
  1.2688 +  sym=find_symbol_child(&loctab,parent);    /* try local symbols first */
  1.2689 +  if (sym==NULL)                            /* not found */
  1.2690 +    sym=find_symbol_child(&glbtab,parent);
  1.2691 +  return sym;
  1.2692 +}
  1.2693 +
  1.2694 +/*  addsym
  1.2695 + *
  1.2696 + *  Adds a symbol to the symbol table (either global or local variables,
  1.2697 + *  or global and local constants).
  1.2698 + */
  1.2699 +SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage)
  1.2700 +{
  1.2701 +  symbol entry, **refer;
  1.2702 +
  1.2703 +  /* labels may only be defined once */
  1.2704 +  assert(ident!=iLABEL || findloc(name)==NULL);
  1.2705 +
  1.2706 +  /* create an empty referrer list */
  1.2707 +  if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) {
  1.2708 +    error(103);         /* insufficient memory */
  1.2709 +    return NULL;
  1.2710 +  } /* if */
  1.2711 +  *refer=NULL;
  1.2712 +
  1.2713 +  /* first fill in the entry */
  1.2714 +  memset(&entry,0,sizeof entry);
  1.2715 +  strcpy(entry.name,name);
  1.2716 +  entry.hash=namehash(name);
  1.2717 +  entry.addr=addr;
  1.2718 +  entry.codeaddr=code_idx;
  1.2719 +  entry.vclass=(char)vclass;
  1.2720 +  entry.ident=(char)ident;
  1.2721 +  entry.tag=tag;
  1.2722 +  entry.usage=(char)usage;
  1.2723 +  entry.fnumber=-1;     /* assume global visibility (ignored for local symbols) */
  1.2724 +  entry.lnumber=fline;
  1.2725 +  entry.numrefers=1;
  1.2726 +  entry.refer=refer;
  1.2727 +
  1.2728 +  /* then insert it in the list */
  1.2729 +  if (vclass==sGLOBAL)
  1.2730 +    return add_symbol(&glbtab,&entry,TRUE);
  1.2731 +  else
  1.2732 +    return add_symbol(&loctab,&entry,FALSE);
  1.2733 +}
  1.2734 +
  1.2735 +SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag,
  1.2736 +                            int dim[],int numdim,int idxtag[])
  1.2737 +{
  1.2738 +  symbol *sym;
  1.2739 +
  1.2740 +  /* global variables may only be defined once
  1.2741 +   * One complication is that functions returning arrays declare an array
  1.2742 +   * with the same name as the function, so the assertion must allow for
  1.2743 +   * this special case. Another complication is that variables may be
  1.2744 +   * "redeclared" if they are local to an automaton (and findglb() will find
  1.2745 +   * the symbol without states if no symbol with states exists).
  1.2746 +   */
  1.2747 +  assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0
  1.2748 +         || sym->ident==iFUNCTN && sym==curfunc
  1.2749 +         || sym->states==NULL && sc_curstates>0);
  1.2750 +
  1.2751 +  if (ident==iARRAY || ident==iREFARRAY) {
  1.2752 +    symbol *parent=NULL,*top;
  1.2753 +    int level;
  1.2754 +    sym=NULL;                   /* to avoid a compiler warning */
  1.2755 +    for (level=0; level<numdim; level++) {
  1.2756 +      top=addsym(name,addr,ident,vclass,tag,uDEFINE);
  1.2757 +      top->dim.array.length=dim[level];
  1.2758 +      top->dim.array.level=(short)(numdim-level-1);
  1.2759 +      top->x.tags.index=idxtag[level];
  1.2760 +      top->parent=parent;
  1.2761 +      parent=top;
  1.2762 +      if (level==0)
  1.2763 +        sym=top;
  1.2764 +    } /* for */
  1.2765 +  } else {
  1.2766 +    sym=addsym(name,addr,ident,vclass,tag,uDEFINE);
  1.2767 +  } /* if */
  1.2768 +  return sym;
  1.2769 +}
  1.2770 +
  1.2771 +/*  getlabel
  1.2772 + *
  1.2773 + *  Returns te next internal label number. The global variable sc_labnum is
  1.2774 + *  initialized to zero.
  1.2775 + */
  1.2776 +SC_FUNC int getlabel(void)
  1.2777 +{
  1.2778 +  return sc_labnum++;
  1.2779 +}
  1.2780 +
  1.2781 +/*  itoh
  1.2782 + *
  1.2783 + *  Converts a number to a hexadecimal string and returns a pointer to that
  1.2784 + *  string. This function is NOT re-entrant.
  1.2785 + */
  1.2786 +SC_FUNC char *itoh(ucell val)
  1.2787 +{
  1.2788 +static char itohstr[30];
  1.2789 +  char *ptr;
  1.2790 +  int i,nibble[16];             /* a 64-bit hexadecimal cell has 16 nibbles */
  1.2791 +  int max;
  1.2792 +
  1.2793 +  #if PAWN_CELL_SIZE==16
  1.2794 +    max=4;
  1.2795 +  #elif PAWN_CELL_SIZE==32
  1.2796 +    max=8;
  1.2797 +  #elif PAWN_CELL_SIZE==64
  1.2798 +    max=16;
  1.2799 +  #else
  1.2800 +    #error Unsupported cell size
  1.2801 +  #endif
  1.2802 +  ptr=itohstr;
  1.2803 +  for (i=0; i<max; i+=1){
  1.2804 +    nibble[i]=(int)(val & 0x0f);        /* nibble 0 is lowest nibble */
  1.2805 +    val>>=4;
  1.2806 +  } /* endfor */
  1.2807 +  i=max-1;
  1.2808 +  while (nibble[i]==0 && i>0)   /* search for highest non-zero nibble */
  1.2809 +    i-=1;
  1.2810 +  while (i>=0){
  1.2811 +    if (nibble[i]>=10)
  1.2812 +      *ptr++=(char)('a'+(nibble[i]-10));
  1.2813 +    else
  1.2814 +      *ptr++=(char)('0'+nibble[i]);
  1.2815 +    i-=1;
  1.2816 +  } /* while */
  1.2817 +  *ptr='\0';            /* and a zero-terminator */
  1.2818 +  return itohstr;
  1.2819 +}
  1.2820 +