Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed


#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;
}