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