#include <u.h>
#include <libc.h>
#include <bio.h>
#include "String.h"

struct Sinstack{
       int     depth;
       Biobuf  *fp[32];        /* hard limit to avoid infinite recursion */
};

/* initialize */
extern Sinstack *
s_allocinstack(char *file)
{
       Sinstack *sp;
       Biobuf *fp;

       fp = Bopen(file, OREAD);
       if(fp == nil)
               return nil;

       sp = malloc(sizeof *sp);
       sp->depth = 0;
       sp->fp[0] = fp;
       return sp;
}

extern void
s_freeinstack(Sinstack *sp)
{
       while(sp->depth >= 0)
               Bterm(sp->fp[sp->depth--]);
       free(sp);
}

/*  Append an input line to a String.
*
*  Empty lines and leading whitespace are removed.
*/
static char *
rdline(Biobuf *fp, String *to)
{
       int c;
       int len = 0;

       c = Bgetc(fp);

       /* eat leading white */
       while(c==' ' || c=='\t' || c=='\n' || c=='\r')
               c = Bgetc(fp);

       if(c < 0)
               return 0;

       for(;;){
               switch(c) {
               case -1:
                       goto out;
               case '\\':
                       c = Bgetc(fp);
                       if (c != '\n') {
                               s_putc(to, '\\');
                               s_putc(to, c);
                               len += 2;
                       }
                       break;
               case '\r':
                       break;
               case '\n':
                       if(len != 0)
                               goto out;
                       break;
               default:
                       s_putc(to, c);
                       len++;
                       break;
               }
               c = Bgetc(fp);
       }
out:
       s_terminate(to);
       return to->ptr - len;
}

/* Append an input line to a String.
*
* Returns a pointer to the character string (or 0).
* Leading whitespace and newlines are removed.
* Lines starting with #include cause us to descend into the new file.
* Empty lines and other lines starting with '#' are ignored.
*/
extern char *
s_rdinstack(Sinstack *sp, String *to)
{
       char *p;
       Biobuf *fp, *nfp;

       s_terminate(to);
       fp = sp->fp[sp->depth];

       for(;;){
               p = rdline(fp, to);
               if(p == nil){
                       if(sp->depth == 0)
                               break;
                       Bterm(fp);
                       sp->depth--;
                       return s_rdinstack(sp, to);
               }

               if(strncmp(p, "#include", 8) == 0 && (p[8] == ' ' || p[8] == '\t')){
                       to->ptr = p;
                       p += 8;

                       /* sanity (and looping) */
                       if(sp->depth >= nelem(sp->fp))
                               sysfatal("s_recgetline: includes too deep");

                       /* skip white */
                       while(*p == ' ' || *p == '\t')
                               p++;

                       nfp = Bopen(p, OREAD);
                       if(nfp == nil)
                               continue;
                       sp->depth++;
                       sp->fp[sp->depth] = nfp;
                       return s_rdinstack(sp, to);
               }

               /* got milk? */
               if(*p != '#')
                       break;

               /* take care of comments */
               to->ptr = p;
               s_terminate(to);
       }
       return p;
}