/* preprocess.c */

#include "config.h"
#include "object.h"
#include "file.h"
#include "token.h"
#include "construct.h"
#include "instr.h"

extern char expand_buf[EBUFSIZ+1];
extern char tmpbuf[EBUFSIZ+1];
extern char name_buf[MAX_TOK_LEN+1];
extern char string_buf[MAX_STR_LEN+1];

#define isstart(c) (isalpha(c) || ((c)=='_'))
#define iscname(c) (isstart(c) || isdigit(c))
#define getch() *((file_info->expanded)++)
#define ungetch() --(file_info->expanded)

char *make_include_name(char *name)
{
  static char nbuf[MAX_STR_LEN];
  int c,n;

  c=0;
  n=strlen(name);
  if (n<3) return "";
  if ((*name=='<') && (name[n-1]=='>')) {
    strcpy(nbuf,INCLUDE_PATH);
    c=strlen(INCLUDE_PATH);
    nbuf[c++]='/';
  } else
    if (!((*name=='\"') && (name[n-1]=='\"')))
      return "";
  if (c+n-1>MAX_STR_LEN) return "";
  strncpy(nbuf+c,name+1,n-2);
  nbuf[c+n-2]='\0';
  return nbuf;
}

char *find_parm(struct define *def, char *name)
{
  struct parm *curr;

  curr=def->params;
  while (curr) {
    if (!strcmp(curr->name,name))
      return curr->exp;
    curr=curr->next;
  }
  return NULL;
}

void add_expand_buf(char *s1, char *s2)
{
  int counter;

  counter=0;
  if (s1==expand_buf)
    counter=strlen(s1);
  else
    while ((*s1) && (counter<EBUFSIZ))
      expand_buf[counter++]=*(s1++);
  while ((*s2) && (counter<EBUFSIZ))
    expand_buf[counter++]=*(s2++);
  expand_buf[counter]='\0';
}

void free_parm_exp(struct define *def)
{
  struct parm *curr;

  curr=def->params;
  while (curr) {
    if (curr->exp) {
      FREE(curr->exp);
      curr->exp=NULL;
    }
    curr=curr->next;
  }
}

int preprocess(filptr *file_info)
{
  int counter,c,done;
  struct define *currdef,*prevdef;
  struct parm *currparm,*nextparm;
  FILE *tmp;
  struct file_stack *tmp2;

  counter=0;
  c=fgetc(file_info->curr_file);
  while ((c!=EOF) && (!isspace(c)) && (counter<MAX_TOK_LEN)) {
    name_buf[counter++]=c;
    c=fgetc(file_info->curr_file);
  }
  if ((c!=EOF) && (!(isspace(c))))
    return 1;
  name_buf[counter]='\0';
  if (!strcmp(name_buf,"undef")) {
    while ((c!=EOF) && isspace(c)) c=fgetc(file_info->curr_file);
    counter=0;
    while ((c!=EOF) && (!isspace(c)) && (counter<MAX_TOK_LEN)) {
      name_buf[counter++]=c;
      c=fgetc(file_info->curr_file);
    }
    name_buf[counter]='\0';
    ungetc(c,file_info->curr_file);
    currdef=file_info->defs;
    prevdef=NULL;
    while (currdef)
      if (!strcmp(currdef->name,name_buf)) {
        if (prevdef)
          prevdef->next=currdef->next;
        else
          file_info->defs=currdef->next;
        FREE(currdef->name);
        FREE(currdef->definition);
        currparm=currdef->params;
        while (currparm) {
          nextparm=currparm->next;
          FREE(currparm->name);
          if (currparm->exp)
            FREE(currparm->exp);
          FREE(currparm);
          currparm=nextparm;
        }
        FREE(currdef);
      } else
        currdef=currdef->next;
    return 0;
  } else if (!strcmp(name_buf,"include")) {
    counter=0;
    while ((c!=EOF) && isspace(c)) c=fgetc(file_info->curr_file);
    counter=0;
    while ((c!=EOF) && (!isspace(c)) && (counter<MAX_STR_LEN)) {
      string_buf[counter++]=c;
      c=fgetc(file_info->curr_file);
    }
    string_buf[counter]='\0';
    ungetc(c,file_info->curr_file);
    if (!(tmp=open_file(make_include_name(string_buf),"r",NULL))) {
      set_c_err_msg("couldn't open include file");
      return 1;
    }
    tmp2=(struct file_stack *) MALLOC(sizeof(struct file_stack));
    tmp2->file_ptr=file_info->curr_file;
    tmp2->previous=file_info->previous;
    file_info->previous=tmp2;
    file_info->curr_file=tmp;
    ungetc('\n',tmp);
    return 0;
  } else if (!strcmp(name_buf,"define")) {
    counter=0;
    while ((c!=EOF) && isspace(c)) c=fgetc(file_info->curr_file);
    counter=0;
    if (!isstart(c)) return 1;
    while ((c!=EOF) && iscname(c) && (counter<MAX_TOK_LEN)) {
      name_buf[counter++]=c;
      c=fgetc(file_info->curr_file);
    }
    name_buf[counter]='\0';
    currdef=(struct define *) MALLOC(sizeof(struct define));
    currdef->name=copy_string(name_buf);
    currdef->has_paren=0;
    currdef->params=NULL;
    currparm=NULL;
    if (c=='(') {
      currdef->has_paren=1;
      c=fgetc(file_info->curr_file);
      while (c!=EOF) {
        while ((c!=EOF) && (c!='\n') && isspace(c))
          c=fgetc(file_info->curr_file);
        counter=0;
        if (c=='\n') {
          currdef->params=currparm;
          currdef->next=file_info->defs;
          file_info->defs=currdef;
          currdef->definition=copy_string("");
          set_c_err_msg("malformed #define");
          return 1;
        }
        if (c==')') {
          c=fgetc(file_info->curr_file);
          break;
        }
        while ((c!=EOF) && (iscname(c)) && (counter<MAX_TOK_LEN)) {
          name_buf[counter++]=c;
          c=fgetc(file_info->curr_file);
        }
        name_buf[counter]='\0';
        nextparm=(struct parm *) MALLOC(sizeof(struct parm));
        nextparm->next=currparm;
        nextparm->name=copy_string(name_buf);
        nextparm->exp=NULL;
        currparm=nextparm;
        while ((c!=EOF) && (c!='\n') && (isspace(c)))
          c=fgetc(file_info->curr_file);
        if ((c!=',') && (c!=')')) {
          currdef->params=currparm;
          currdef->next=file_info->defs;
          currdef->definition=copy_string("");
          file_info->defs=currdef;
          set_c_err_msg("malformed #define");
          return 1;
        }
        if (c==',') c=fgetc(file_info->curr_file);
      }
      c=fgetc(file_info->curr_file);
    } else
      ungetc(c,file_info->curr_file);
    while (currparm) {
      nextparm=currparm->next;
      currparm->next=currdef->params;
      currdef->params=currparm;
      currparm=nextparm;
    }
    currdef->next=file_info->defs;
    file_info->defs=currdef;
    while ((c!=EOF) && (c!='\n') && (isspace(c))) c=fgetc(file_info->curr_file);
    counter=0;
    while ((c!=EOF) && (c!='\n') && (counter<EBUFSIZ)) {
      if (c=='\\') {
        c=fgetc(file_info->curr_file);
        if (c=='\n') {
          if (!(file_info->previous))
            ++(file_info->phys_line);
          c=' ';
        } else {
          ungetc(c,file_info->curr_file);
          c='\\';
        }
      }
      if (c=='/') {
        c=fgetc(file_info->curr_file);
        if (c=='*') {
          while (c!=EOF) {
            c=fgetc(file_info->curr_file);
            if (c=='*') {
              c=fgetc(file_info->curr_file);
              if (c=='/') {
                c=' ';
                break;
              } else {
                ungetc(c,file_info->curr_file);
              }
            } else if (c=='\n')
              if (!(file_info->previous))
                ++(file_info->phys_line);
          }
        } else {
          ungetc(c,file_info->curr_file);
          c='/';
        }
      }
      if ((c!=EOF) && (c!='\n') && (isspace(c))) {
        expand_buf[counter++]=' ';
        while ((c!=EOF) && (c!='\n') && (isspace(c)))
          c=fgetc(file_info->curr_file);
      } else if ((c!=EOF) && (c!='\n')) {
        expand_buf[counter++]=c;
        c=fgetc(file_info->curr_file);
      }
    }
    ungetc(c,file_info->curr_file);
    expand_buf[counter]='\0';
    currdef->definition=copy_string(expand_buf);
    return 0;
  } else
    return 1;
}

void expand_def(struct define *def, char *buf)
{
  char *s,*s2;
  int counter,counter2;

  s=def->definition;
  counter=0;
  while ((*s) && (counter<EBUFSIZ)) {
    if (isstart(*s)) {
      counter2=0;
      while ((counter2<MAX_TOK_LEN) && (iscname(*s)))
        name_buf[counter2++]=*(s++);
      name_buf[counter2]='\0';
      if (!(s2=find_parm(def,name_buf)))
        s2=name_buf;
      while ((*s2) && (counter<EBUFSIZ))
        buf[counter++]=*(s2++);
    } else
      buf[counter++]=*(s++);
  }
  buf[counter]='\0';
}

void expand_exp(struct define *def, filptr *file_info)
{
  char c;
  int done,paren_counter,in_string,counter;
  struct parm *currparm;

  if (def->has_paren) {
    do {
      c=getch();
    } while (c && isspace(c));
    if (c!='(')
      return;
    paren_counter=0;
    in_string=0;
    currparm=def->params;
    do {
      do {
        c=getch();
      } while (c && isspace(c));
      if (c==')')
        break;
      if (!currparm) {
        free_parm_exp(def);
        return;
      }
      counter=0;
      while (c && (counter<EBUFSIZ)) {
        if (c=='\"')
          in_string=(!in_string);
        else if (c=='(' && (!in_string))
          paren_counter++;
        else if (c==')' && (!in_string))
          if (!paren_counter) {
            ungetch();
            break;
          } else
            paren_counter--;
        else if (c==',' && (!paren_counter) && (!in_string))
          break;
        else if (c=='\\' && in_string) {
          c=getch();
          if (counter<(EBUFSIZ-1))
            tmpbuf[counter++]='\\';
        }
        tmpbuf[counter++]=c;
        c=getch();
      }
      tmpbuf[counter]='\0';
      currparm->exp=copy_string(tmpbuf);
      currparm=currparm->next;
    }  while (c);
    if (!c) {
      free_parm_exp(def);
      return;
    }
    strcpy(tmpbuf,file_info->expanded);
    expand_def(def,expand_buf);
    add_expand_buf(expand_buf,tmpbuf);
    file_info->expanded=expand_buf;
    free_parm_exp(def);
  } else {
    strcpy(tmpbuf,file_info->expanded);
    add_expand_buf(def->definition,tmpbuf);
    file_info->expanded=expand_buf;
  }
}

void expand(struct define *def, filptr *file_info)
{
  int c,paren_counter,in_string,counter;
  struct parm *currparm;

  if (def->has_paren) {
    do {
      c=fgetc(file_info->curr_file);
      if (c=='\n')
        if (!(file_info->previous))
          ++(file_info->phys_line);
    } while ((c!=EOF) && isspace(c));
    if (c!='(')
      return;
    paren_counter=0;
    in_string=0;
    currparm=def->params;
    do {
      do {
        c=fgetc(file_info->curr_file);
        if (c=='\n')
          if (!(file_info->previous))
            ++(file_info->phys_line);
      } while ((c!=EOF) && isspace(c));
      if (c==')')
        break;
      if (!currparm) {
        free_parm_exp(def);
        return;
      }
      counter=0;
      while ((c!=EOF) && (counter<EBUFSIZ)) {
        if (c=='\n') {
          if (!(file_info->previous))
            ++(file_info->phys_line);
          c=' ';
        }
        if (c=='\"')
          in_string=(!in_string);
        else if (c=='(' && (!in_string))
          paren_counter++;
        else if (c==')' && (!in_string))
          if (!paren_counter) {
            ungetc(c,file_info->curr_file);
            break;
          } else
            paren_counter--;
        else if (c==',' && (!paren_counter) && (!in_string))
          break;
        else if (c=='\\' && in_string) {
          c=fgetc(file_info->curr_file);
          if (counter<(EBUFSIZ-1))
            tmpbuf[counter++]='\\';
        }
        tmpbuf[counter++]=c;
        c=fgetc(file_info->curr_file);
      }
      tmpbuf[counter]='\0';
      currparm->exp=copy_string(tmpbuf);
      currparm=currparm->next;
    }  while (c!=EOF);
    if (c==EOF) {
      free_parm_exp(def);
      return;
    }
    expand_def(def,expand_buf);
    file_info->expanded=expand_buf;
    free_parm_exp(def);
  } else {
    strcpy(expand_buf,def->definition);
    file_info->expanded=expand_buf;
  }
}