Newer
Older
uBix-Retro / tools / vasm / vasm / source.c
/* source.c - source files, include paths and dependencies */
/* (c) in 2020 by Volker Barthelmann and Frank Wille */

#include "vasm.h"
#include "osdep.h"
#include "dwarf.h"

#define SRCREADINC (64*1024)  /* extend buffer in these steps when reading */

char *compile_dir;
int ignore_multinc,depend,depend_all;

static struct include_path *first_incpath;
static struct source_file *first_source;
static struct deplist *first_depend,*last_depend;


void source_debug_init(int type,void *data)
{
  if (type) {
    /* @@@ currently we only support DWARF source level debugging here */
    dwarf_init((struct dwarf_info *)data,first_incpath,first_source);
  }
}


static void add_depend(char *name)
{
  if (depend) {
    struct deplist *d = first_depend;

    /* check if an entry with the same file name already exists */
    while (d != NULL) {
      if (!strcmp(d->filename,name))
        return;
      d = d->next;
    }

    /* append new dependency record */
    d = mymalloc(sizeof(struct deplist));
    d->next = NULL;
    if (name[0]=='.'&&(name[1]=='/'||name[1]=='\\'))
      name += 2;  /* skip "./" in paths */
    d->filename = mystrdup(name);
    if (last_depend)
      last_depend = last_depend->next = d;
    else
      first_depend = last_depend = d;
  }
}


void write_depends(FILE *f)
{
  struct deplist *d = first_depend;

  if (depend==DEPEND_MAKE && d!=NULL && outname!=NULL)
    fprintf(f,"%s:",outname);

  while (d != NULL) {
    switch (depend) {
      case DEPEND_LIST:
        fprintf(f,"%s\n",d->filename);
        break;
      case DEPEND_MAKE:
        if (str_is_graph(d->filename))
          fprintf(f," %s",d->filename);
        else
          fprintf(f," \"%s\"",d->filename);
        break;
      default:
        ierror(0);
    }
    d = d->next;
  }

  if (depend == DEPEND_MAKE)
    fputc('\n',f);
}


static FILE *open_path(char *compdir,char *path,char *name,char *mode)
{
  char pathbuf[MAXPATHLEN];
  FILE *f;

  if (strlen(compdir) + strlen(path) + strlen(name) + 1 <= MAXPATHLEN) {
    strcpy(pathbuf,compdir);
    strcat(pathbuf,path);
    strcat(pathbuf,name);

    if (f = fopen(pathbuf,mode)) {
      if (depend_all || !abs_path(pathbuf))
        add_depend(pathbuf);
      return f;
    }
  }
  return NULL;
}


static FILE *locate_file(char *filename,char *mode,struct include_path **ipath_used)
{
  struct include_path *ipath;
  FILE *f;

  if (abs_path(filename)) {
    /* file name is absolute, then don't use any include paths */
    if (f = fopen(filename,mode)) {
      if (depend_all)
        add_depend(filename);
      if (ipath_used)
        *ipath_used = NULL;  /* no path used, file name was absolute */
      return f;
    }
  }
  else {
    /* locate file name in all known include paths */
    for (ipath=first_incpath; ipath; ipath=ipath->next) {
      if ((f = open_path(emptystr,ipath->path,filename,mode)) == NULL) {
        if (compile_dir && !abs_path(ipath->path) &&
            (f = open_path(compile_dir,ipath->path,filename,mode)))
          ipath->compdir_based = 1;
      }
      if (f != NULL) {
        if (ipath_used)
          *ipath_used = ipath;
        return f;
      }
    }
  }
  general_error(12,filename);
  return NULL;
}


/* create a new source text instance, which has cur_src as parent */
source *new_source(char *srcname,struct source_file *srcfile,
                   char *text,size_t size)
{
  static unsigned long id = 0;
  source *s = mymalloc(sizeof(source));
  size_t i;
  char *p;

  /* scan the source for strange characters */
  for (p=text,i=0; i<size; i++,p++) {
    if (*p == 0x1a) {
      /* EOF character - replace by newline and ignore rest of source */
      *p = '\n';
      size = i + 1;
      break;
    }
  }

  s->parent = cur_src;
  s->parent_line = cur_src ? cur_src->line : 0;
  s->srcfile = srcfile; /* NULL for macros and repetitions */
  s->name = mystrdup(srcname);
  s->text = text;
  s->size = size;
  s->defsrc = NULL;
  s->defline = 0;
  s->srcdebug = cur_src ? cur_src->srcdebug : 1;  /* source-level debugging */
  s->macro = NULL;
  s->repeat = 1;        /* read just once */
  s->irpname = NULL;
  s->cond_level = clev; /* remember level of conditional nesting */
  s->num_params = -1;   /* not a macro, no parameters */
  s->param[0] = emptystr;
  s->param_len[0] = 0;
  s->id = id++;	        /* every source has unique id - important for macros */
  s->srcptr = text;
  s->line = 0;
  s->bufsize = INITLINELEN;
  s->linebuf = mymalloc(INITLINELEN);
#ifdef CARGSYM
  s->cargexp = NULL;
#endif
#ifdef REPTNSYM
  /* -1 outside of a repetition block */
  s->reptn = cur_src ? cur_src->reptn : -1;
#endif
  return s;
}


/* quit parsing the current source instance, leave macros, repeat loops
   and restore the conditional assembly level */
void end_source(source *s)
{
  if(s){
    s->srcptr=s->text+s->size;
    s->repeat=1;
    clev=s->cond_level;
  }
}


source *include_source(char *inc_name)
{
  static int srcfileidx;
  char *filename;
  struct source_file **nptr = &first_source;
  struct source_file *srcfile;
  source *newsrc = NULL;
  FILE *f;

  filename = convert_path(inc_name);

  /* check whether this source file name was already included */
  while (srcfile = *nptr) {
    if (!filenamecmp(srcfile->name,filename)) {
      myfree(filename);
      nptr = NULL;  /* reuse existing source in memory */
      break;
    }
    nptr = &srcfile->next;
  }

  if (nptr != NULL) {
    /* allocate, locate and read a new source file */
    struct include_path *ipath;

    if (f = locate_file(filename,"r",&ipath)) {
      char *text;
      size_t size;

      for (text=NULL,size=0; ; size+=SRCREADINC) {
        size_t nchar;
        text = myrealloc(text,size+SRCREADINC);
        nchar = fread(text+size,1,SRCREADINC,f);
        if (nchar < SRCREADINC) {
          size += nchar;
          break;
        }
      }
      if (feof(f)) {
        if (size > 0) {
          text = myrealloc(text,size+2);
          *(text+size) = '\n';
          *(text+size+1) = '\0';
          size++;
        }
        else {
          myfree(text);
          text = "\n";
          size = 1;
        }
        srcfile = mymalloc(sizeof(struct source_file));
        srcfile->next = NULL;
        srcfile->name = filename;
        srcfile->incpath = ipath;
        srcfile->text = text;
        srcfile->size = size;
        srcfile->index = ++srcfileidx;
        *nptr = srcfile;
        cur_src = newsrc = new_source(filename,srcfile,text,size);
      }
      else
        general_error(29,filename);
      fclose(f);
    }
  }
  else {
    /* same source was already loaded before, source_file node exists */
    if (ignore_multinc)
      return NULL;  /* ignore multiple inclusion of this source completely */

    /* new source instance from existing source file */
    cur_src = newsrc = new_source(srcfile->name,srcfile,srcfile->text,
                                  srcfile->size);
  }
  return newsrc;
}


void include_binary_file(char *inname,long nbskip,unsigned long nbkeep)
/* locate a binary file and convert into a data atom */
{
  char *filename;
  FILE *f;

  filename = convert_path(inname);
  if (f = locate_file(filename,"rb",NULL)) {
    size_t size = filesize(f);

    if (size > 0) {
      if (nbskip>=0 && (size_t)nbskip<=size) {
        dblock *db = new_dblock();

        if (nbkeep > (unsigned long)(size - (size_t)nbskip) || nbkeep==0)
          db->size = size - (size_t)nbskip;
        else
          db->size = nbkeep;

        db->data = mymalloc(size);
        if (nbskip > 0)
          fseek(f,nbskip,SEEK_SET);

        if (fread(db->data,1,db->size,f) != db->size)
          general_error(29,filename);  /* read error */
        add_atom(0,new_data_atom(db,1));
      }
      else
        general_error(46);  /* bad file-offset argument */
    }
    fclose(f);
  }
  myfree(filename);
}


static struct include_path *new_ipath_node(char *pathname)
{
  struct include_path *new = mymalloc(sizeof(struct include_path));

  new->next = NULL;
  new->path = pathname;
  new->compdir_based = 0;
  return new;
}


static char *make_canonical_path(char *pathname)
{
  char *newpath = convert_path(pathname);

  pathname = append_path_delimiter(newpath);  /* append '/', when needed */
  myfree(newpath);
  return pathname;
}


/* add the main source include path, which is searched first */
void main_include_path(char *pathname)
{
  struct include_path *ipath;

  ipath = new_ipath_node(make_canonical_path(pathname));
  ipath->next = first_incpath;
  first_incpath = ipath;
}


struct include_path *new_include_path(char *pathname)
{
  struct include_path *ipath;

  /* check if path already exists, otherwise append new node */
  pathname = make_canonical_path(pathname);
  for (ipath=first_incpath; ipath; ipath=ipath->next) {
    if (!filenamecmp(pathname,ipath->path)) {
      myfree(pathname);
      return ipath;
    }
    if (ipath->next == NULL)
      return ipath->next = new_ipath_node(pathname);
  }
  return first_incpath = new_ipath_node(pathname);
}