/* 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  <stdio.h>
#include  <stdlib.h>
#include  <ctype.h>
#include  <string.h>
#ifndef _MSC_VER
#include  <strings.h>
#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<VALBEF))
               er=0;
          else
          {
               l=(int)strlen(cmd[i]);
               while(isspace(t[l])) l++;

               er=(*func[i])(t+l);
          }
     }
     return(er);
}

int pp_ifdef(char *t)
{
/*     int x;
     printf("t=%s\n",t);
     x=searchdef(t);
     printf("searchdef(t)=%d\n",x);
*/   
     loopfl=(loopfl<<1)+( searchdef(t) ? 0 : 1 );
     return(0);
}

int pp_ifndef(char *t)
{
     loopfl=(loopfl<<1)+( searchdef(t) ? 1 : 0 );
     return(0);
}

int pp_ifldef(char *t)
{
	loopfl=(loopfl<<1)+( ll_pdef(t) ? 1 : 0 );
	return(0);
}

int pp_iflused(char *t)
{
	int n;
	loopfl=(loopfl<<1)+( ll_search(t,&n,STD) ? 1 : 0 );
	return(0);
}

int pp_echo(char *t)
{
     int er;
     
     if((er=pp_replace(s,t,-1,rlist)))
          errout(er);
     else
     {
          logout(s);
          logout("\n");
     }
     return(0);
}

int pp_print(char *t)
{
     int f,a,er;
     
     logout(t);
     if((er=pp_replace(s,t,-1,rlist)))
     {
          logout("\n");
          errout(er);
     }
     else
     {
          logout("=");
          logout(s);
          logout("=");
          er=b_term(s,&a,&f,pc[segment]);
          if(er)
          {
               logout("\n");
               errout(er);
          }
          else
               { sprintf(s,"%d\n",a); logout(s); }
     }
     
     return(0);
}

int pp_error(char *t)
{
	/* the offending line is printed for us */
	return E_UERROR;
}

int pp_if(char *t)
{
     int a,f,l,er;

/* XXX: #if XYZ, if XYZ is not defined, XYZ gets treated as a label. */

     if((er=pp_replace(s,t,-1,rlist))) {
          errout(er);
     } else
     {
	dsb_len = 1;
          f=b_term(s,&a,&l,pc[segment]);
	dsb_len = 0;

          if((!loopfl) && f) {
               errout(f);
          } else
               loopfl=(loopfl<<1)+( a ? 0 : 1 );
     }
     return(0);
}

int pp_else(char *t)
{
     (void)t;		/* quench warning */
     loopfl ^=1;
     return(0);
}

int pp_endif(char *t)
{
     (void)t;		/* quench warning */
     loopfl=loopfl>>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<liste[i].p_anz;j++)
               {
                    x+=strlen(x)+1;
                    sprintf(s+strlen(s),"%s%c",x,(liste[i].p_anz-j-1) ? ',' : ')');
               }
          }
          sprintf(s+strlen(s),"=%s\n",liste[i].replace);
          logout(s);
     }
     return(E_OK);
}

int searchdef(char *t)
{
     int i=0,j,k,l=0;

     while(t[l]!=' ' && t[l]!='\0') l++;

     if(rlist)
     {
       i=hashindex[hashcode(t,l)];
     
       do   /*for(i=0;i<rlist;i++)*/
       {
          k=liste[i].s_len;
          j=0;

          if(k && (k==l))
          {
               while((t[j]==liste[i].search[j])&&j<k) j++;
               if(j==k)
                    break;
          }
          
          if(!i)
          {
               i=rlist;
               break;
          }
          
          i=liste[i].nextindex;
               
       } while(1);
     } 
    
     return(i-rlist);
}

int ga_pp(void)
{
	return(rlist);
}

int gm_pp(void)
{
	return(ANZDEF);
}

long gm_ppm(void)
{
	return(MAXPP);
}

long ga_ppm(void)
{
	return(MAXPP-memfre);
}
    
int pp_define(char *k)
{
     int i,er=E_OK,hash,rl;
     char h[MAXLINE*2],*t;
     unsigned int j;
     
     t=k;
          
     if(rlist>=ANZDEF || memfre<MAXLINE*2)
          return(E_NOMEM);
/*
     printf("define:mem=%04lx\n",mem);
     getchar();
*/   
     rl=rlist++;
     
     liste[rl].search=mem;
     for(i=0; (t[i]!=' ') && (t[i]!='\0') && (t[i]!='(') ;i++)
          *mem++=t[i];
     *mem++='\0';
     memfre-=i+1;
     liste[rl].s_len=i;
     liste[rl].p_anz=0;


/*   printf("define:%s\nlen1=%d\n",liste[rl].search,liste[rl].s_len);
     getchar();
*/

     if(t[i]=='(')
     {
          while(t[i]!=')' && t[i]!='\0')
          {
               i++;
               liste[rl].p_anz++;
	       // skip whitespace before parameter name
	       while(isspace(t[i])) i++;
	       // find length
               for(j=0; (!isspace(t[i+j])) && t[i+j]!=')' && t[i+j]!=',' && t[i+j]!='\0';j++);
               if(j<memfre)
               {
                    strncpy(mem,t+i,j);
                    mem+=j;
		    *mem++='\0';
                    memfre-=j+1;
               }
               i+=j;
	       // skip trailing whitespace after parameter name
	       while(isspace(t[i])) i++;
          }
          if(t[i]==')')
               i++;
     }
     while(t[i]==' ')
          i++;
     t+=i;
     
     er = pp_replace(h,t,-1,rlist);

     t=h;     

     liste[rl].replace=mem;
     (void)strcpy(mem,t);
     mem+=strlen(t)+1;
#if 0	/* debug */
     { char *ss;
     if(liste[rl].p_anz)
     {
          ss=liste[rl].search;
          printf("define:\n%s(",liste[rl].search);
          for(j=0;j<liste[rl].p_anz;j++)
          {
               ss+=strlen(ss)+1;
               printf("%s%c",ss,(liste[rl].p_anz-j-1) ? ',' : ')');
          }
          printf("=%s\n",liste[rl].replace);
          getchar();
     }
     else
     {
          printf("define:%s=%s\nlen1=%d\n",liste[rl].search,
               liste[rl].replace,liste[rl].s_len);
     }
     }
#endif
     if(!er)
     {
          hash=hashcode(liste[rl].search,liste[rl].s_len);
          liste[rl].nextindex=hashindex[hash];
          hashindex[hash]=rl;
     } else
          rlist=rl;
     
     return(er);
}

int tcompare(char s[],char *v[], int n)
{
     int i,j,l;
     static char t[MAXLINE];

     for(i=0; s[i]!='\0'; i++) 
          t[i]=tolower(s[i]);
     t[i]='\0';

     for(i=0;i<n;i++)
     {
          l=(int)strlen(v[i]);
          
          for(j=0;j<l;j++)
          {
               if(t[j]!=v[i][j])
                    break;
          }
          if(j==l)
               break;
     }
     return((i==n)? -1 : i);
}

static int check_name(char *t, int n) {

                    int i=0;
		    { 
                        char *x=liste[n].search;
                        while(t[i]==*x++ && t[i] && (isalnum(t[i]) || t[i]=='_'))
                            i++;
		    }

#ifdef DEBUG_RECMAC	
	printf("check name with n=%d, name='%s' ->i=%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 || memfre<MAXLINE*2)
                                   er=E_NOMEM;
                              else
                              {
				   // ... passed
				   // y points to the char behind the input name (i.e. the '(')
                                   char *y=t+sl; 
				   // x points into the pp definition, with the parameter definition names
				   // following the definition name, separated by \0. Starts here behind the 
				   // name - i.e. now points to the name of the first parameter
                                   char *x=liste[n].search+sl+1;
				   // does the input actually have a '(' as parameter marker?
                                   if(*y!='(') {
					// no. Should probably get an own error (E_PARAMETER_EXPECTED)
                                        er=E_SYNTAX;
				   }
                                   else
                                   {
					// mx now points to next free memory (mem current pointer in text table)
                                        char *mx=mem-1;
					// walk through the pp parameters
					// by creating "fake" preprocessor definitions at the end
					// of the current definition list, i.e. in liste[] after index
					// rlist.
                                        for(i=0;i<liste[n].p_anz;i++)
                                        {
					     char c;
                                             int hkfl=0;	// quote flag
					     int klfl=0;	// brackets counter

					     // create new replacement entry
                                             liste[blist+i].search=x;
                                             liste[blist+i].s_len=(int)strlen(x);
                                             liste[blist+i].p_anz=0;
                                             liste[blist+i].replace=mx+1;
					     // move x over to the next parameter name
                                             x+=strlen(x)+1;
					     // points to first char of the parameter name in the input
					     // copy over first char into text memory (note that the position
					     // is already stored in liste[].replace above)
                                             c=*(++mx)=*(++y);
					     // copy over the other characters
                                             while(c!='\0' 
                                                  && ((hkfl!=0 
                                                       || klfl!=0) 
                                                       || (c!=',' 
                                                       && c!=')') 
                                                       )
                                                  )
                                             {
                                                  if(c=='\"')
                                                       hkfl=hkfl^1;
                                                  if(!hkfl)
                                                  {
                                                       if(c=='(')
                                                            klfl++;
                                                       if(c==')')
                                                            klfl--;
                                                  }     
                                                  c=*(++mx)=*(++y);
                                             } 
                                             // zero-terminate stored string
                                             *mx='\0';
					     // if i is for the last parameter, then check if the
					     // last copied char was ')', otherwise it should be ','
					     // as separator for the next parameter
                                             if(c!=((i==liste[n].p_anz-1) ? ')' : ','))
                                             {
                                                  er=E_ANZPAR;
						  // note: this break only exits the innermost loop!
                                                  break;
                                             }
                                        }
					// at this point we have "augmented" the pp definitions
					// with a list of new definitions for the macro parameters
					// so that when pp_replace'ing them recursively the macro parameters
					// will automatically be replaced.   
					
					// if we ran into an error, report so
					if (er != E_OK) {
						return (er);
					}

					// let mx point to first free (and not last non-free) byte
					mx++;
	
					// before we use the parameter replacements, we need to 
					// recursively evaluate and replace them as well.
					
#ifdef DEBUG_RECMAC
     printf("replace (er=%d):\n", er);
     printf("%s=%s\n",liste[n].search,liste[n].replace);
#endif
     // loop over all arguments
     for(i=0;i<liste[n].p_anz;i++) {
/* recursively evaluate arguments */
	char nfto[MAXLINE];
	char nfwas[MAXLINE];
	//int j = 0;
	//int k = 0;

	// copy over the replacement string into a buffer nfwas
	(void)strcpy(nfwas, liste[blist+i].replace);
	if (!er) {
		// replace the tokens in the parameter, adding possible pseudo params
		// on top of the liste[] into nfto
		er=pp_replace(nfto,nfwas,-1,blist+liste[n].p_anz);
	}
#ifdef DEBUG_RECMAC
        printf("-%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace);
	printf("SUBB: -%s=%s\n", nfwas, nfto);
#endif
#if 0
	// as long as the strings don't match, loop...
	while ((k = strcmp(nfto, nfwas))) {
		// copy original from nfwas back to nfto
		(void)strcpy(nfto, nfwas);
		if (!er) {
			// save-guard our replacement strings in global memory for the
			// recursive pp_replace call. Note: is cleaned up after return, 
			// so need to restore mem only at the end of this function.
			mem = mx;
			// replace tokens
			er=pp_replace(nfto,nfwas,-1,rlist);
		}
		// and copy result into input buffer
		(void)strcpy(nfwas, nfto);
#ifdef DEBUG_RECMAC
		printf("SUBB2 (%i): -%s=%s\n", k, liste[rlist+i].replace, nfto);
#endif
		if (++j>10) {
			// 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<len-1) ? i+1 : len-1;
     } while(1);

     *rlen = i;
     return(c);
}

