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 +