/* xa65 - 65xx/65816 cross-assembler and utility suite * * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) * Maintained by Cameron Kaiser * * File handling and preprocessor (also see xaa.c) module * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #ifndef _MSC_VER #include #endif #include "xad.h" #include "xah.h" #include "xah2.h" #include "xar.h" #include "xa.h" #include "xam.h" #include "xal.h" #include "xat.h" #include "xap.h" /* define this for recursive evaluation output */ #undef DEBUG_RECMAC #undef DEBUG_REPLACE char s[MAXLINE]; Datei *filep; static int tcompare(char*,char**,int); static int pp_replace(char*,char*,int,int); static int searchdef(char*); static int fgetline(char*,int len, int *rlen, FILE*); /*static int icl_open(char*);*/ static int pp_ifdef(char*),pp_ifndef(char*); static int pp_else(char*),pp_endif(char*); static int pp_echo(char*),pp_if(char*),pp_print(char*),pp_prdef(char*); static int pp_ifldef(char*),pp_iflused(char*); static int pp_undef(char*); static int pp_error(char*); #define ANZBEF 14 #define VALBEF 7 static int quotebs = 0; static int inquote = 0; static char *cmd[]={ "echo","include","define","undef","printdef","print","error", /* VALBEF */ "ifdef","ifndef","else","endif", "ifldef","iflused","if" }; /* ANZBEF */ static int (*func[])(char*) = { pp_echo,icl_open,pp_define,pp_undef, pp_prdef,pp_print,pp_error,pp_ifdef,pp_ifndef, pp_else,pp_endif, pp_ifldef,pp_iflused,pp_if }; static char *mem; static unsigned long memfre; static int nlf; static int nff; static int hashindex[256]; static List *liste; static unsigned int rlist; static int fsp; static int loopfl; static Datei flist[MAXFILE+1]; static char in_line[MAXLINE]; int pp_comand(char *t) { int i,l,er=1; i=tcompare(t,cmd,ANZBEF); if(i>=0) { if(loopfl && (i>1; return(0); } /* stub for handling CPP directives */ int pp_cpp(char *t) { char name[MAXLINE]; if(sscanf(t, " %d \"%s\"", &filep->fline, name) == 2) { /* massage it into our parameters and drop last quote */ char *u; filep->fline--; if((u = (char *)strrchr(name, '"'))) *u = '\0'; free(filep->fname); filep->fname = strdup(name); if(!filep->fname) { fprintf(stderr,"Oops, no more memory!\n"); exit(1); } return (0); } else { return(E_SYNTAX); } } /* pp_undef is a great hack to get it working fast... */ int pp_undef(char *t) { int i; if((i=searchdef(t))) { i+=rlist; liste[i].s_len=0; } return 0; } int pp_prdef(char *t) { char *x; int i,j; if((i=searchdef(t))) { i+=rlist; x=liste[i].search; sprintf(s,"\n%s",x); if(liste[i].p_anz) { sprintf(s+strlen(s),"("); for(j=0;j=ANZDEF || memfrei=%d, len=%d\n", n, liste[n].search,i, liste[n].s_len); #endif return i == liste[n].s_len; } /** * this is a break out of the original pp_replace code, as this code * was basically duplicated for initial and recursive calls. * * to -> original output buffer for overflow check (only!) * t -> where to write the output * n -> replace macro n in liste[] * sl -> length of liste[n].search (= liste[n].s_len) * recursive -> * l -> * blist -> * */ static int pp_replace_part(char *to, char *t, int n, int sl, int recursive, int *l, int blist) { int er = E_OK; int i, d; char c; // save mem, to restore it when we don't need the pseudo replacements anymore // Note: in a real version, that should probably be a parameter, and not fiddling // with global variables... char *saved_mem = mem; #ifdef DEBUG_RECMAC printf("replace part: n=%d, sl=%d, rec=%d, %s\n", n, sl, recursive, t); #endif // yes, mark replacement string char *rs=liste[n].replace; // does it have parameters? if(liste[n].p_anz) { // yes, we have parameters, so we need to pp_replace them // as well. char fti[MAXLINE],fto[MAXLINE]; // copy replacement into temp buffer (void)strcpy(fti,liste[n].replace); // boundary checks ... if(blist+liste[n].p_anz>=ANZDEF || memfre10) { // we ran into 10 recursions deep - that does not look sound, bail out errout(E_ORECMAC); } } #endif // catch an error situation if (liste[rlist+i].replace == NULL) { errout(E_ANZPAR); return E_ANZPAR; } // copy over the replacement string into free memory (using mx as pointer) (void)strcpy(mx, nfto); // replace the pointer to the (now obsolete) old replacement with the one we just created // Note that due to the nature of the (more or less static) memory allocation, this is not // being freed. Oh well... liste[blist+i].replace = mx; mx += strlen(mx)+1; #ifdef DEBUG_RECMAC printf("FINAL: -%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace); #endif } if(!er) { // safe-guard our memory allocations mem = mx; // only change (of two) from recursive: rlist is 0 there #ifdef DEBUG_RECMAC printf("replace macro: recursive=%d, blist=%d, -> b=%d\n", recursive, blist, blist+liste[n].p_anz); printf(" from: %s\n", fti); #endif er=pp_replace(fto,fti,recursive ? 0 : blist,blist+liste[n].p_anz); #ifdef DEBUG_RECMAC printf(" to: %s\n", fto); #endif } /* if(flag) printf("sl=%d,",sl);*/ sl=(int)((long)y+1L-(long)t); /* if(flag) printf("sl=%d\n",sl);*/ rs=fto; #ifdef DEBUG_RECMAC printf("->%s\n",fto); #endif } } if(er) { mem = saved_mem; return(er); } } // end if(liste[n].p_anz), i.e. if it has parameters d=(int)strlen(rs)-sl; if(strlen(to)+d>=MAXLINE) { mem = saved_mem; return(E_NOMEM); } /* if(d<0) { y=t+sl+d; x=t+sl; while(*y++=*x++); } if(d>0) { for(ll=strlen(t);ll>=sl;ll--) t[ll+d]=t[ll]; } */ if(d) { // d can be positive or negative, so strcpy cannot be used, use memmove instead (void)memmove(t+sl+d,t+sl, strlen(t) - sl + 1); } i=0; while((c=rs[i])) { t[i++]=c; } // other change from recursive. there sl is missing from add //*l=(recursive ? 0 : sl) + d;/*=0;*/ *l=sl + d;/*=0;*/ mem = saved_mem; return (er); } /** * copy the input string pointed to by ti into * an output string buffer pointed to by to, replacing all * preprocessor definitions in the process. * * Note: this method is called recursively, with "a" being -1 * when called from the "outside" respectively in a new context * (like macro parameters) * * The "b" parameter denotes the index in the list from which on * pseudo replacement entries are being created for replacement * parameters * */ int pp_replace(char *to, char *ti, int a,int b) { char *t=to; int l,n,sl,er=E_OK; int ld; // length of name/token to analyse /* int flag=!strncmp(ti,"TOUT",4); if(flag) printf("flag=%d\n",flag); */ // t points to to, so copy input to output 1:1 // then below work on the copy in-place (void)strcpy(t,ti); // if there are replacements in the list of replacements if(rlist) { // loop over the whole input while(t[0]!='\0' && t[0] != ';') { // skip over the whitespace // comment handling is NASTY NASTY NASTY // but I currently don't see another way, as comments and colons // can (and do) appear in preprocessor replacements char quotefl = 0; char commentfl = 0; while((t[0] != 0) && (quotefl || commentfl || ((!isalpha(t[0]) && t[0]!='_')))) { if (t[0]=='\0') { break; /*return(E_OK);*/ } else { if (t[0] == ';' && !quotefl) { commentfl = 1; } if (t[0] == ':' && !quotefl && !ca65 && !masm) { // note that both ca65 and masm allow colons in comments // so in these cases we cannot reset the comment handling here commentfl = 0; } if (quotefl) { // ignore other quotes within a quote if (t[0] == quotefl) { quotefl = 0; } } else if (t[0] == '"' || t[0] == '\'') { quotefl = t[0]; } t++; ti++; } } // determine the length of the name for(l=0;isalnum(t[l])||t[l]=='_';l++); // store in ld ld=l; #ifdef DEBUG_RECMAC printf("l=%d,a=%d,b=%d, t=%s\n",l,a, b ,t); #endif // the following if() is executed when being called from an // 'external' context, i.e. not from a recursion if(a<0) { // when called from an external context, not by recursion // compute hashcode for the name for the search index entry (liste[n]) n=hashindex[hashcode(t,l)]; // loop over all entries in linked list for hash code (think: hash collisions) do // while(1); { // length of name of pp definition sl=liste[n].s_len; #ifdef DEBUG_RECMAC printf("macro entry: name=%s, sl=%d, p_anz=%d\n", liste[n].search, sl, liste[n].p_anz); #endif // does pp definition match what we have found? if(sl && (sl==l) && check_name(t, n)) { er = pp_replace_part(to, t, n, sl, 0, &l, b); if (er != E_OK) { return er; } break; } if(!n) break; // next index in linked list for given hash code n=liste[n].nextindex; } while(1); } else { // called when in recursive call // loop over all the replacement entries from the given b down to 0 // that allows to replace the parameters first (as they were added at // the end of the list) for(n=b-1;n>=a;n--) { sl=liste[n].s_len; if(sl && (sl==l) && check_name(t, n)) { er = pp_replace_part(to, t, n, sl, 1, &l, b); break; } } } // advance input by length of name ti+=ld; // advance output by l t+=l; } /* end while(t[0] != 0) */ } /* end if(rlist) */ return(E_OK); } int pp_init(void) { int er; for(er=0;er<256;er++) hashindex[er]=0; fsp=0; er=0; mem=malloc(MAXPP); if(!mem) er=E_NOMEM; memfre=MAXPP; rlist=0; nlf=1; nff=1; if(!er) { liste=malloc((long)ANZDEF*sizeof(List)); if(!liste) er=E_NOMEM; if(!er && !xa23) { er = pp_define("XA_MAJOR " progmajor); if (!er) er = pp_define("XA_MINOR " progminor); if (!er) er = pp_define("XA_PATCH " progpatch); } } return(er); } int pp_open(char *name) { FILE *fp; int l; fp=xfopen(name,"r"); l = strlen(name); /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ flist[0].fname = malloc(l+1); if(!flist[0].fname) { fprintf(stderr,"Oops, no more memory!\n"); exit(1); } (void)strncpy(flist[0].fname,name,l+1); flist[0].fline=0; flist[0].bdepth=b_depth(); flist[0].filep=fp; flist[0].flinep=NULL; return (fp == NULL); } void pp_close(void) { if(flist[fsp].bdepth != b_depth()) { fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", flist[fsp].fname, flist[fsp].bdepth, b_depth()); } fclose(flist[fsp].filep); } void pp_end(void) { } Datei *pp_getidat(void) { return &flist[fsp]; } int icl_close(int *c) { *c='\n'; if(!fsp) return(E_EOF); if(flist[fsp].bdepth != b_depth()) { fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", flist[fsp].fname, flist[fsp].bdepth, b_depth()); } fclose(flist[fsp--].filep); nff=1; return(E_OK); } int icl_open(char *tt) { FILE *fp2; char *namep; int len,j,i=0; pp_replace(s,tt,-1,rlist); if(fsp>=MAXFILE) return(-1); if(s[i]=='<' || s[i]=='"') i++; for(j=i;s[j];j++) if(s[j]=='>' || s[j]=='"') s[j]='\0'; fp2=xfopen(s+i,"r"); if(!fp2) return(E_FNF); setvbuf(fp2,NULL,_IOFBF,BUFSIZE); fsp++; namep = s+i; len = strlen(namep); /* we have to alloc it dynamically to make the name survive another pp_open - it's used in the cross-reference list */ flist[fsp].fname = malloc(len+1); if(!flist[fsp].fname) { fprintf(stderr,"Oops, no more memory!\n"); exit(1); } strncpy(flist[fsp].fname,namep, len+1); flist[fsp].fline=0; flist[fsp].bdepth=b_depth(); flist[fsp].flinep=NULL; flist[fsp].filep=fp2; nff=1; return(0); } int pgetline(char *t) { int c,er=E_OK; int rlen, tlen; char *p = 0; char *q = 0; loopfl =0; /* set if additional fetch needed */ filep =flist+fsp; do { int is_continuation = 0; tlen = 0; // start of current line in in_line[] do { c=fgetline(in_line + tlen, MAXLINE - tlen, &rlen, flist[fsp].filep); //fprintf(stderr, "fgetline -> c=%02x, rlen->%d, t->%s\n", c, rlen, in_line+tlen); /* check for continuation lines */ is_continuation = ((c == '\n') && (rlen > 0) && (in_line[tlen + rlen - 1]=='\\')); if (is_continuation) { // cut off the continuation character rlen--; in_line[tlen + rlen] = 0; } tlen += rlen; } while (is_continuation); if(in_line[0]=='#' || in_line[0] == altppchar) { if (in_line[1]==' ') { /* cpp comment -- pp_comand doesn't handle this right */ er=pp_cpp(in_line+1); } else { if((er=pp_comand(in_line+1))) { if(er!=1) { logout(in_line); logout("\n"); } } } } else { er=1; } if(c==EOF) { if (loopfl && fsp) { char bletch[MAXLINE]; sprintf(bletch, "at end of included file %s:\n", flist[fsp].fname); logout(bletch); errout(W_OPENPP); } er=icl_close(&c); } } while(!er || (loopfl && er!=E_EOF)); if (loopfl) { errout(E_OPENPP); } if(!er || loopfl) { in_line[0]='\0'; } er= (er==1) ? E_OK : er ; if(!er) { #ifdef DEBUG_REPLACE // printf("<<<: %s\n", in_line); #endif er=pp_replace(t,in_line,-1,rlist); #ifdef DEBUG_REPLACE printf(">>>: %s\n", t); #endif } if(!er && nff) er=E_NEWFILE; if(!er && nlf) er=E_NEWLINE; nlf=nff=0; filep=flist+fsp; filep->flinep=in_line; //fprintf(stderr, "pgetline -> er=%d, t=%s\n", er, t); return(er); } /*************************************************************************/ /* smart getc that can skip C comment blocks */ int rgetc(FILE *fp) { int incomment; int c; static int d; static int d_isvalid = 0; static int last = 0; incomment=0; do { restart: /* we had a look-ahead to see if two-char sequence was received? */ if (d_isvalid) { c = d; d_isvalid = 0; } else { c = getc(fp); } while(c==13) { c = getc(fp); }; /* remove ^M for unices */ /* a newlinebreaks any quote */ if (c == '\n') { inquote = 0; flist[fsp].fline++; nlf=1; } /* check for start of comment anyway, to allow for nested comments */ /* only allow this with 2.3 compatibility */ if(!inquote && c=='/' && (xa23 || !incomment)) { d = getc(fp); // C++ double slash comment if (d == '/') { do { c = getc(fp); } while (c != '\n' && c != EOF); if (c == '\n') { flist[fsp].fline++; nlf=1; } } else if (d == '*') { /* start block comment */ incomment++; /* convene normal processing */ goto restart; } else { d_isvalid = 1; } } /* here we are in a dilemma. If we interpret quotes in comments, * this may break end-of-block comment when singular quotes are * in a comment. If we don't interpret quotes in a comment, * a quoted string with end-of-comment in it may break when * commented. * * Unfortunately GeckOS has quotes in comments, and in an inconsistent way. * * #define E_FILLNAM <-34 / * illegal name (joker "*","?","\"") * / * * ... except if we handle backslash-escaped quotes? No, then this breaks * in apps/lsh/lsh.a65:347 * * / * TODO: interpret "\" escape codes, shell variables * / * * But I guess for 2.4 it is ok to break things like this. */ if (!incomment) { /* not in comment */ /* flag if we're in a quoted string */ /* but only if quote isnot escaped with backslash */ if(c=='"' && last!='\\' && !quotebs) { inquote ^= 1; } /* implement backslashed quotes for 2.4 */ if(!xa23 && c=='\\') quotebs=1; else quotebs=0; } else { /* in comment */ /* check for end of comment */ /* note: for xa2.3, incomment only set true if not quoted, and quote not changed in comment */ if((!inquote || !xa23) && (c=='*')) { if((d=getc(fp))!='/') { d_isvalid = 1; } else { incomment--; /* convene normal processing */ goto restart; } } } /* force newline at the end of a file */ if (c == EOF && last != EOF) { last = EOF; c = '\n'; } else { last = c; } } while(incomment && (c!=EOF)); return(c-'\t'?c:' '); } /** * Note that the line returned is always zero-terminated, * the rlen out parameter is just convenience, so that * a further strlen() can be saved */ int fgetline(char *t, int len, int *rlen, FILE *fp) { static int c,i; i=0; do { c=rgetc(fp); if(c==EOF || c=='\n') { t[i]='\0'; break; } t[i]=c; i= (i