diff options
Diffstat (limited to 'src/io.c')
| -rw-r--r-- | src/io.c | 1255 |
1 files changed, 1255 insertions, 0 deletions
diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..d33afb1 --- /dev/null +++ b/src/io.c @@ -0,0 +1,1255 @@ +#include "c/lex.h" +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +struct wbuf bstdout, bstderr; + +void +ioinit(void) +{ + bstdout.fp = stdout; + bstdout.isfp = 1; + bstderr.fp = stderr; + bstderr.isfp = 1; +} + +void +iowrite(struct wbuf *buf, const void *Src, int n) +{ + const uchar *src = Src; + + if (buf->isfp) { + fwrite(Src, 1, n, buf->fp); + buf->err = ferror(buf->fp) != 0; + return; + } + while (n > 0) { + int avail = buf->cap - buf->len; + int amt = avail < n ? avail : n; + + memcpy(buf->buf + buf->len, src, amt); + n -= amt; + src += amt; + buf->len += amt; + if (n > 0 && buf->len == buf->cap) { + if (buf->fd < 0) { + buf->err = 1; + return; + } + ioflush(buf); + } + } +} + +void +ioflush(struct wbuf *buf) +{ + int i, ret; + + if (buf->isfp) { + fflush(buf->fp); + buf->err = ferror(buf->fp) != 0; + return; + } + buf->err = 0; + if (buf->fd < 0) { + buf->len = 0; + return; + } + for (i = 0; buf->len > 0;) { + ret = write(buf->fd, buf->buf + i, buf->len); + if (ret > 0) { + assert(ret <= buf->len); + buf->len -= ret; + i += ret; + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + buf->err = 1; + break; + } + } +} + +void +ioputc(struct wbuf *buf, uchar c) +{ + if (buf->isfp) { + buf->err = fputc(c, buf->fp) != EOF; + return; + } + if (buf->len == buf->cap) { + if (buf->fd < 0) { + buf->err = 1; + return; + } + ioflush(buf); + } + buf->buf[buf->len++] = c; +} + +static int +putquoted(struct wbuf *buf, uchar c, uchar qchar, int next) +{ + if (c == qchar || c == '\\' || !aisprint(c)) { + int n = (ioputc(buf, '\\'), 1); + uchar cseq; + + switch (c) { + case '\\': + case '\'': + case '"': + cseq = c; + Charseq: + n += (ioputc(buf, cseq), 1); + break; + case '\a': cseq = 'a'; goto Charseq; + case '\b': cseq = 'b'; goto Charseq; + case '\f': cseq = 'f'; goto Charseq; + case '\r': cseq = 'r'; goto Charseq; + case '\t': cseq = 't'; goto Charseq; + case '\v': cseq = 'v'; goto Charseq; + case '\n': cseq = 'n'; goto Charseq; + default: + if (!next || in_range(next, '0', '7')) + n += bfmt(buf, "%.3o", c); + else + n += bfmt(buf, "%o", c); + } + return n; + } + if (c == '?' && (!next || next == '?')) { + return ioputc(buf, c), ioputc(buf, '\\'), 2; + } + return ioputc(buf, c), 1; +} + +static int +putuint(struct wbuf *buf, uvlong x, int base, bool lower) +{ + uchar tmp[64]; + uchar *end = tmp + sizeof(tmp); + uchar *s = end; + switch (base) { + case 2: + do *--s = '0' + x%2; while (x >>= 1); + break; + case 8: + do *--s = '0' + x%8; while (x >>= 3); + break; + case 10: + do *--s = '0' + x%10; while (x /= 10); + break; + case 16: + do *--s = "0123456789ABCDEF"[x&15] | (-lower & 0x20); while (x >>= 4); + break; + default: + assert(0&&"base"); + } + iowrite(buf, s, end - s); + return end - s; +} + +static void +fmterr(const char *fmt, ...) +{ + va_list ap; + + efmt("fmt Error: "); + va_start(ap, fmt); + vbfmt(&bstderr, fmt, ap); + va_end(ap); + ioputc(&bstderr, '\n'); + ioflush(&bstderr); + ioflush(&bstdout); + abort(); +} + +#define bwriteS(buf, S) (iowrite(buf, S, sizeof S - 1), sizeof S - 1) +#define bputc(B, C) (ioputc(B, C), 1) + +static int +priquals(struct wbuf *buf, int q) +{ + const char s[] = " const volatile", *p = s; + int m = sizeof s - 1; + if (!q) return 0; + else if (q == QCONST) m -= 9; + else if (q == QVOLATILE) p += 6, m -= 6; + else assert(q == (QCONST | QVOLATILE)); + iowrite(buf, p, m); + return m; +} +static int +pritypebefore(struct wbuf *buf, union type ty, int qual) +{ + const char *s, *s2; + union type chld; + int n; + switch (ty.t) { + case TYVOID: s = "void"; Prim: n = bfmt(buf, "%s", s); return n + priquals(buf, qual); + case TYBOOL: s = "bool"; goto Prim; + case TYCHAR: s = "char"; goto Prim; + case TYSCHAR: s = "signed char"; goto Prim; + case TYUCHAR: s = "unsigned char"; goto Prim; + case TYSHORT: s = "short"; goto Prim; + case TYUSHORT: s = "unsigned short"; goto Prim; + case TYINT: s = "int"; goto Prim; + case TYUINT: s = "unsigned int"; goto Prim; + case TYLONG: s = "long"; goto Prim; + case TYULONG: s = "unsigned long"; goto Prim; + case TYVLONG: s = "long long"; goto Prim; + case TYUVLONG: s = "unsigned long long"; goto Prim; + case TYFLOAT: s = "float"; goto Prim; + case TYDOUBLE: s = "double"; goto Prim; + case TYLDOUBLE:s = "long double"; goto Prim; + case TYCOMPLEXF:s = "float complex"; goto Prim; + case TYCOMPLEX: s = "double complex"; goto Prim; + case TYCOMPLEXL:s = "long double complex"; goto Prim; + case TYPTR: + chld = typechild(ty); + n = pritypebefore(buf, chld, ty.flag & TFCHLDQUAL); + if (!isptrcvtt(chld.t)) + n += bputc(buf, ' '); + if (chld.t == TYARRAY || chld.t == TYFUNC) + n += bputc(buf, '('); + n += bputc(buf, '*'); + n += priquals(buf, qual); + return n; + case TYARRAY: + return pritypebefore(buf, typechild(ty), ty.flag & TFCHLDQUAL); + case TYFUNC: + return pritypebefore(buf, typedata[ty.dat].ret, 0); + case TYSTRUCT: + s = "struct"; + Tagged: + n = bfmt(buf, "%s %s", s, (s2 = (char *)ttypenames[typedata[ty.dat].id]) ? s2 : "(anonymous)"); + return n + priquals(buf, qual); + case TYUNION: + s = "union"; + goto Tagged; + case TYENUM: + s = "enum"; + goto Tagged; + default: + return bfmt(buf, "?\?%d?",ty.t); + } +} + +static int +pritypeafter(struct wbuf *buf, union type ty, int qual) +{ + const struct typedata *td; + int n = 0; + switch (ty.t) { + case TYPTR: + if (typechild(ty).t == TYARRAY || typechild(ty).t == TYFUNC) + n += bputc(buf, ')'); + n += pritypeafter(buf, typechild(ty), ty.flag & TFCHLDQUAL); + break; + case TYARRAY: + n += bputc(buf, '['); + if (typearrlen(ty)) + n += bfmt(buf, "%u", typearrlen(ty)); + n += bputc(buf, ']'); + n += pritypeafter(buf, typechild(ty), ty.flag & TFCHLDQUAL); + break; + case TYFUNC: + td = &typedata[ty.dat]; + n += bputc(buf, '('); + for (int i = 0; i < td->nmemb; ++i) { + n += bfmt(buf, "%ty", td->param[i]); + if (i < td->nmemb - 1 || td->variadic) + n += bwriteS(buf, ", "); + } + if (td->variadic) n += bwriteS(buf, "..."); + else if (td->nmemb == 0 && !td->kandr) n += bwriteS(buf, "void"); + n += bwriteS(buf, ")"); + n += pritypeafter(buf, td->ret, 0); + break; + } + return n; +} + +static int +fmttype(struct wbuf *buf, union type ty, int qual) +{ + int n = pritypebefore(buf, ty, qual); + n += pritypeafter(buf, ty, qual); + return n; +} + +static int +putdouble(struct wbuf *buf, double x) +{ + char tmp[200]; + int n = snprintf(tmp, sizeof tmp, "%f", x); + if (n >= sizeof tmp-1) n = snprintf(tmp, sizeof tmp, "%g", x); + assert(n < sizeof tmp-1); + iowrite(buf, tmp, n); + return n; +} + +int +vbfmt(struct wbuf *out, const char *fmt, va_list ap) +{ + bool quote, umod, lmod, zmod, lower, possign; + int base; + vlong i; + int pad, prec, q; + const char *s; + void *p; + struct token *tok; + union type ty; + double f; + char tmpbuf1[70], tmpbuf2[70]; + struct wbuf tmp1 = MEMBUF(tmpbuf1, sizeof tmpbuf1); + struct wbuf tmp2 = MEMBUF(tmpbuf2, sizeof tmpbuf2); + struct wbuf *buf = out; + int n = 0, prevn; + + while (*fmt) { + buf = out; + if (*fmt++ != '%') { + n += bputc(buf, fmt[-1]); + continue; + } + if (*fmt == '%') { + n += bputc(buf, *fmt++); + continue; + } + fmt += quote = *fmt == '\''; + fmt += possign = *fmt == '+'; + pad = 0; + if (aisdigit(*fmt)) { /* left pad */ + for (; aisdigit(*fmt); ++fmt) + pad = pad*10 + *fmt-'0'; + if (pad) { + tmp1.len = 0; + buf = &tmp1; + } + } else if (*fmt == '-') { /* right pad */ + if (!aisdigit(*++fmt)) + fmterr("padding amount expected"); + for (; aisdigit(*fmt); ++fmt) + pad = pad*10 - (*fmt-'0'); + } + prec = -1; + if (*fmt == '.') { + if (!aisdigit(*++fmt)) + fmterr("precision expected"); + prec = 0; + for (; aisdigit(*fmt); ++fmt) + prec = prec*10 + *fmt-'0'; + } + fmt += umod = *fmt == 'u'; + if ((zmod = *fmt == 'z')) + ++fmt, lmod = 0; + else + fmt += lmod = *fmt == 'l'; + lower = 0; + prevn = n; + switch (*fmt++) { + case 'c': /* character */ + if (quote) { + n += bputc(buf, '\''); + n += putquoted(buf, va_arg(ap, int), '\'', -1); + n += bputc(buf, '\''); + } else { + n += bputc(buf, va_arg(ap, int)); + } + break; + case 's': /* nullterminated string */ + s = va_arg(ap, const char *); + if (quote) { + if (!s) { + n += bwriteS(buf, "(null)"); + break; + } + QuotedStr: + n += bputc(buf, '"'); + if (lmod) /* lower */ + for (; *s; ++s) n += putquoted(buf, aisalpha(*s) ? *s|32 : *s, '"', s[1]); + else + for (; *s; ++s) n += putquoted(buf, *s, '"', s[1]); + n += bputc(buf, '"'); + } else { + assert(s && "%s null!"); + if (lmod) /* lower */ + for (; *s; ++s) n += bputc(buf, aisalpha(*s) ? *s|32 : *s); + else + while (*s) n += bputc(buf, *s++); + } + break; + case 'S': /* string ptr + len */ + s = va_arg(ap, const char *); + i = va_arg(ap, uint); + PriS: + assert(s && "%S null"); + if (quote) { + n += bputc(buf, '"'); + for (; i--; ++s) n += putquoted(buf, *s, '"', i ? s[1] : -1); + n += bputc(buf, '"'); + } else { + iowrite(buf, s, i); + n += i; + } + break; + case 'y': /* symbol: print string literally if valid identifier, or quote it */ + s = va_arg(ap, const char *); + assert(s && "%y null"); + for (i = 0; s[i]; ++i) { + if (aisalpha(s[i]) || s[i] == '_' || (i > 0 && aisdigit(s[i]))) + continue; + goto QuotedStr; + } + /* valid identifier */ + while (*s) n += bputc(buf, *s++); + break; + case 'd': /* decimal */ + base = 10; + Int: + if (base != 10) umod = 1; + i = lmod ? va_arg(ap, vlong) + : umod ? va_arg(ap, uint) + : zmod && sizeof(&i-&i) > sizeof(int) ? va_arg(ap, vlong) + : (vlong)va_arg(ap, int); + tmp2.len = 0; + if (!umod && i < 0) { + n += bputc(buf, '-'); + i = -(uvlong)i; + } else if (possign) { + n += bputc(buf, '+'); + } + if (quote) { + switch (base) { + case 2: n += bwriteS(buf, "0b"); break; + case 8: n += bwriteS(buf, "0"); break; + case 16: n += bwriteS(buf, "0x"); break; + } + } + n += putuint(prec > 0 ? &tmp2 : buf, i, base, lower); + if (prec > 0) { + int fil = prec - tmp2.len; + while (fil-- > 0) n += bputc(buf, '0'); + iowrite(buf, tmp2.buf, tmp2.len); + } + break; + case 'o': /* octal */ + base = 8; + goto Int; + case 'b': /* binary */ + base = 2; + goto Int; + case 'x': case 'X': /* hexadecimal */ + base = 16; + lower = fmt[-1] == 'x'; + goto Int; + case 'p': /* pointer */ + p = va_arg(ap, void *); + if (!p && quote) { + n += bwriteS(buf, "NULL"); + } else { + n += bwriteS(buf, "0x"); + tmp2.len = 0; + n += putuint(prec > 0 ? &tmp2 : buf, (uvlong)p, 16, 1); + if (prec > 0) { + int fil = prec - tmp2.len; + while (fil-- > 0) n += bputc(buf, '0'); + iowrite(buf, tmp2.buf, tmp2.len); + } + } + break; + case 'f': /* float */ + f = va_arg(ap, double); + n += putdouble(buf, f); + break; + case 't': /* token/tokentag/type */ + switch (*fmt++) { + case 'k': /* tk token */ + tok = va_arg(ap, struct token *); + Tok: + switch (tok->t) { + case TKXXX: + n += bwriteS(buf, "\?\?\?"); + break; + case TKNUMLIT: + if (quote) { + n += bputc(buf, '`'); + iowrite(buf, tok->s, tok->len); + n += tok->len; + n += bputc(buf, '\''); + } else { + s = tok->s; + i = tok->len; + goto PriS; + } + break; + case TKCHRLIT: + if (tok->wide) n += bputc(buf, tok->wideuni ? tok->wide == 1 ? 'u' : 'U' : 'L'); + n += bputc(buf, '\''); + if (tok->wide == 0) + for (int i = 0; i < tok->len; ++i) + n += putquoted(buf, tok->s[i], '\'', i < tok->len - 1 ? tok->s[i+1] : -1); + else { + char p[4]; + uint c = tok->wide == 1 ? tok->ws16[0] : tok->ws32[0]; + int l = utf8enc(p, c); + if (l == 1) + n += putquoted(buf, *p, '\'', -1); + else + n += (iowrite(buf, p, l), l); + } + n += bputc(buf, '\''); + break; + case TKSTRLIT: + if (tok->wide == 0) { + s = tok->s; + i = tok->len; + quote = 1; + goto PriS; + } else { + n += bputc(buf, tok->wideuni ? tok->wide == 1 ? 'u' : 'U' : 'L'); + n += bputc(buf, '\"'); + for (int i = 0; i < tok->len; ++i) { + char p[4]; + uint c = tok->wide == 1 ? tok->ws16[i] : tok->ws32[i]; + int l = utf8enc(p, c); + if (l == 1) + n += putquoted(buf, *p, '\"', 0); + else + n += (iowrite(buf, p, l), l); + } + n += bputc(buf, '\"'); + } + break; + case TKPPMACSTR: + if (quote) n += bputc(buf, '`'); + n += bfmt(buf, "#%s", tok->s); + if (quote) n += bputc(buf, '\''); + break; + case TKPPMACARG: + case TKIDENT: + if (quote) n += bputc(buf, '`'); + n += bfmt(buf, "%s", tok->name); + if (quote) n += bputc(buf, '\''); + break; + case TKEOF: + n += bwriteS(buf, "<end-of-file>"); + break; + case TKEQU: s = "=="; C2: iowrite(buf, s, 2); n += 2; break; + case TKNEQ: s = "!="; goto C2; + case TKLTE: s = "<="; goto C2; + case TKGTE: s = ">="; goto C2; + case TKSHR: s = ">>"; goto C2; + case TKSHL: s = "<<"; goto C2; + case TKINC: s = "++"; goto C2; + case TKDEC: s = "--"; goto C2; + case TKDOTS: n += bwriteS(buf, "..."); break; + case TKARROW: s = "->"; goto C2; + case TKPPCAT: s = "##"; goto C2; + case TKLOGAND: s = "&&"; goto C2; + case TKLOGIOR: s = "||"; goto C2; + case TKSETADD: s = "+="; goto C2; + case TKSETSUB: s = "-="; goto C2; + case TKSETMUL: s = "*="; goto C2; + case TKSETDIV: s = "/="; goto C2; + case TKSETREM: s = "%="; goto C2; + case TKSETIOR: s = "|="; goto C2; + case TKSETXOR: s = "^="; goto C2; + case TKSETAND: s = "&="; goto C2; + case TKSETSHR: n += bwriteS(buf, ">>="); break; + case TKSETSHL: n += bwriteS(buf, "<<="); break; + default: + if (quote) n += bputc(buf, '`'); + if (in_range(tok->t, TKWBEGIN_, TKWEND_)) { + iowrite(buf, tok->name, tok->len); + n += tok->len; + } else if (aisprint(tok->t)) { + n += bputc(buf, tok->t); + } else { + n += bwriteS(buf, "??"); + } + if (quote) n += bputc(buf, '\''); + break; + } + break; + case 't': /* tt token tag */ + tok = &(struct token) { va_arg(ap, int) }; + switch (tok->t) { + case TKNUMLIT: + n += bwriteS(buf, "numeric literal"); + break; + case TKSTRLIT: + n += bwriteS(buf, "string literal"); + break; + case TKIDENT: + n += bwriteS(buf, "identifier"); + break; + case TKEOF: + n += bwriteS(buf, "<end-of-file>"); + break; + default: + if (tok->t >= TKWBEGIN_ && tok->t <= TKWEND_) { + static const char *tab[] = { + #define _(kw, c, ...) #kw, + #include "c/keywords.def" + #undef _ + }; + tok->s = tab[tok->t - TKWBEGIN_]; + tok->len = strlen(tok->s); + } + goto Tok; + } + break; + case 'y': /* ty type */ + ty = va_arg(ap, union type); + n += fmttype(buf, ty, 0); + break; + case 'q': /* tq qualified type */ + ty = va_arg(ap, union type); + q = va_arg(ap, int); + n += fmttype(buf, ty, q); + break; + default: + if (fmt[-1] == ' ' || !aisprint(fmt[-1])) + fmterr("expected format specifier"); + else + fmterr("unknown format specifier 't%c'", fmt[-1]); + } + break; + case 'g': /* graphics rendition (color) */ + if (!ccopt.nocolor) n += bwriteS(buf, "\033["); + while (*fmt++ != '.') { + if (ccopt.nocolor) continue; + n += bputc(buf, fmt[-1]); + } + if (!ccopt.nocolor) n += bputc(buf, 'm'); + break; + case 'M': /* cc mode */ + iowrite(buf, &"C89\0C99\0C11\0C23"[ccopt.cstd*4], 3); + n += 3; + n += bwriteS(buf, " mode"); + break; + default: + if (umod || lmod) { + --fmt; + base = 10; + goto Int; + } + if (fmt[-1] == ' ' || !aisprint(fmt[-1])) + fmterr("expected format specifier"); + else + fmterr("unknown format specifier '%c'", fmt[-1]); + } + if (pad > 0) { /* left pad */ + while (pad-- > buf->len) + n += bputc(out, ' '); + assert(buf != out); + iowrite(out, buf->buf, buf->len); + out->err |= buf->err; + } else if (pad < 0) { /* right pad */ + int len = n - prevn; + while (pad++ < -len) + n += bputc(out, ' '); + } + } + return n; +} + +int +bfmt(struct wbuf *buf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vbfmt(buf, fmt, ap); + va_end(ap); + return ret; +} + +void +gpritype(union type ty) +{ + efmt("%ty\n", ty); + ioflush(&bstderr); +} + +static uint pagesiz; + +extern struct embedfile embedfilesdir[]; + +struct memfile +mapopen(const char **err, const char *path) +{ + struct stat stat; + int fd = -1; + void *p = NULL; + struct memfile f = {0}; + uint mapsiz; + + assert("nullp" && err && path); + + if (!pagesiz) pagesiz = sysconf(_SC_PAGESIZE); + *err = NULL; + + if (*path == '@' && path[1] == ':') { + for (struct embedfile *e = embedfilesdir; e->name; ++e) { + if (!strcmp(e->name, path+2)) { + return (struct memfile) { (const uchar *)e->s, e->len, .statik = 1 }; + } + } + } + + if ((fd = open(path, O_RDONLY)) < 0) + goto Err; + if (fstat(fd, &stat) != 0) + goto Err; + + if (S_ISREG(stat.st_mode)) { + if (stat.st_size > UINT_MAX) { + Big: + errno = EFBIG; + goto Err; + } + mapsiz = alignup(stat.st_size, pagesiz); + if ((p = mmap(NULL, mapsiz + pagesiz, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) + goto Err; + if (mapsiz > 0 && mmap(p, mapsiz, PROT_READ, MAP_FIXED|MAP_PRIVATE, fd, 0) == MAP_FAILED) + goto Err; + + close(fd); + f.p = p; + f.n = stat.st_size; + return f; + } else if (S_ISFIFO(stat.st_mode) || S_ISCHR(stat.st_mode)) { + uint cap = 0; + int ret; + + do { + enum { CHUNKSIZ = 1<<10 }; + if (f.n + CHUNKSIZ >= cap && (cap += CHUNKSIZ) < CHUNKSIZ) { + /* overflow */ + free(p); + goto Big; + } + if (!(f.p = p ? realloc(p, cap) : malloc(cap))) { + free(p); + goto Err; + } + p = (void *)f.p; + ret = read(fd, (char *)p + f.n, CHUNKSIZ); + if (ret >= 0) + f.n += ret; + else if (errno != EAGAIN && errno != EWOULDBLOCK) + goto Err; + } while (ret != 0); + + close(fd); + fd = -1; + mapsiz = alignup(f.n, pagesiz); + if ((f.p = mmap(NULL, mapsiz + pagesiz, PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { + free(p); + goto Err; + } + memcpy((void *)f.p, p, f.n); + free(p); + mprotect((void *)f.p, mapsiz + pagesiz, PROT_READ); + return f; + } else { + *err = "Not a file"; + } + +Err: + if (fd >= 0) close(fd); + if (!*err) *err = strerror(errno); + return f; +} + +void +mapclose(struct memfile *f) +{ + assert(f->p); + if (!f->statik) + munmap((void *)f->p, alignup(f->n, pagesiz) + pagesiz); + memset(f, 0, sizeof *f); +} + +void * +mapzeros(uint N) +{ + void *p = mmap(NULL, N, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + return p == MAP_FAILED ? NULL : p; +} + +void +_assertfmt(const char *file, int line, const char *func, const char *expr) +{ + ioflush(&bstdout); + efmt("%s:%d: %s: Assertion `%s' failed.\n", file, line, func, expr); + ioflush(&bstderr); +} + +struct fileuid { + long dev; + union { + long ino; + const char *str; + }; +}; + +/* one entry per #line */ +struct linemap { + int phys; + int toline; + const char *tofile; +}; + +static struct file { + struct fileuid uid; + const char *path; + struct memfile f; + vec_of(uint) lineoffs; + vec_of(struct linemap) linemap; + bool once; + bool seen; + internstr guardmac; +} *fileht[1<<SPANFILEBITS]; +static int nfiles; + +int +getpredeffile(struct memfile **pf, const char *name) +{ + struct file *f; + struct fileuid uid; + uint h, id, n = countof(fileht); + + uid.dev = -11; + uid.ino = hashs(0, name); + for (id = h = uid.dev ^ uid.ino;; ++id) { + id &= countof(fileht) - 1; + f = fileht[id]; + if (f && f->uid.dev == uid.dev && f->uid.ino == uid.ino) { + break; + } else if (!f) { + f = allocz(&globarena, sizeof *f, 0); + f->uid = uid; + f->path = name; + f->f = (struct memfile) { .statik = 1 }; + fileht[id] = f; + vinit(&f->lineoffs, NULL, 10); + vpush(&f->lineoffs, 0); + ++nfiles; + break; + } + assert(--n > 0 && "fileht full"); + } + *pf = &f->f; + return id; +} + +int +openfile(const char **err, struct memfile **pf, const char *path) +{ + struct stat st; + struct file *f; + struct fileuid uid; + size_t h, id, n = countof(fileht); + + if (*path == '@' && path[1] == ':') { + /* fast path to rule out filenames we know for sure aren't builtin */ + /* !KEEP SYNC with embedfilesdir */ + if (path[2] != 's' /* std*.h */ + && path[2] != 'f' /* float.h */) return -1; + uid.dev = -1; + uid.str = path; + h = hashs(0, path+2); + } else { + if (stat(path, &st) != 0) { + *err = strerror(errno); + return -1; + } + uid.dev = st.st_dev, uid.ino = st.st_ino; + h = uid.dev ^ uid.ino; + } + for (id = h;; ++id) { + id &= countof(fileht) - 1; + f = fileht[id]; + if (f && f->uid.dev == uid.dev && (uid.dev >= 0 ? f->uid.ino == uid.ino : !strcmp(f->uid.str, uid.str))) { + break; + } else if (!f) { + struct memfile m; + m = mapopen(err, path); + if (*err) return -1; + f = allocz(&globarena, sizeof *f, 0); + f->uid = uid; + f->path = path; + f->f = m; + fileht[id] = f; + vinit(&f->lineoffs, NULL, 50); + vpush(&f->lineoffs, 0); + ++nfiles; + break; + } + assert(--n > 0 && "fileht full"); + } + *pf = &f->f; + return id; +} + +const char * +getfilename(int id, uint atoff) +{ + assert((uint)id < countof(fileht) && fileht[id]); + if (!fileht[id]->linemap.n || !atoff) + return fileht[id]->path; + return getfilepos(NULL, NULL, id, atoff); +} + +struct memfile * +getfile(int id) +{ + assert((uint)id < countof(fileht) && fileht[id]); + return &fileht[id]->f; +} + +void +addfileline(int id, uint off) +{ + assert((uint)id < countof(fileht) && fileht[id]); + vec_of(uint) *lineoffs = (void *)&fileht[id]->lineoffs; + if (lineoffs->n && off > lineoffs->p[lineoffs->n-1]) + vpush(lineoffs, off); +} + +void +setfileline(int id, uint off, int line, const char *file) +{ + assert((uint)id < countof(fileht) && fileht[id]); + vec_of(struct linemap) *linemap = (void *)&fileht[id]->linemap; + vec_of(uint) *lineoffs = (void *)&fileht[id]->lineoffs; + int phys = 2; + for (int i = lineoffs->n-1; i >= 0; --i) { + if (lineoffs->p[i] < off) { + phys = i+2; + break; + } + } + if (linemap->n > 0) { + assert(linemap->p[linemap->n-1].phys < phys); + if (!file) file = linemap->p[linemap->n-1].tofile; + } + vpush(linemap, ((struct linemap){ phys, line, file })); +} + +const char * +getfilepos(int *pline, int *pcol, int id, uint off) +{ + assert((uint)id < countof(fileht) && fileht[id]); + uint *offs = fileht[id]->lineoffs.p; + uint n = fileht[id]->lineoffs.n; + /* binary search over offsets array */ + int l = 0, h = n - 1, i = 0; + while (l <= h) { + i = (l + h) / 2; + if (offs[i] < off) l = i + 1; + else if (offs[i] > off) h = i - 1; + else break; + } + i -= offs[i] > off; + int line = i + 1, col = off - offs[i] + 1; + const char *file = fileht[id]->path; + vec_of(struct linemap) *linemap = (void *)&fileht[id]->linemap; + if (linemap->n) { + /* binary search over linemap array */ + l = 0, h = linemap->n - 1, i = 0; + while (l <= h) { + i = (l + h) / 2; + if (linemap->p[i].phys < line) l = i + 1; + else if (linemap->p[i].phys > line) h = i - 1; + else break; + } + i -= linemap->p[i].phys > line; + if (i >= 0) { + line = linemap->p[i].toline + (line - linemap->p[i].phys); + if (linemap->p[i].tofile) file = linemap->p[i].tofile; + } + } + if (pline) *pline = line; + if (pcol) *pcol = col; + return file; +} + +bool +isoncefile(int id, internstr *guard) +{ + assert(id < countof(fileht) && fileht[id]); + *guard = fileht[id]->guardmac; + return fileht[id]->once; +} + +void +markfileonce(int id, internstr guard) +{ + assert(id < countof(fileht) && fileht[id]); + fileht[id]->once = 1; + fileht[id]->guardmac = guard; +} + +void +markfileseen(int id) +{ + assert(id < countof(fileht) && fileht[id]); + fileht[id]->seen = 1; +} + +bool +isfileseen(int id) +{ + assert(id < countof(fileht) && fileht[id]); + return fileht[id]->seen; +} + +void +closefile(int id) +{ + assert(id < countof(fileht) && fileht[id]); + mapclose(&fileht[id]->f); +} + +void +vdiag(const struct span *span, enum diagkind kind, const char *fmt, va_list ap) +{ + /* to avoid concurrent invocations of the compiler mixing up the diagnostics + * in the unbuffered stderr output, use a separate buffer here and write() + * it all out bypassing stdio */ + static char ebuf[4096]; + static struct wbuf out = FDBUF(ebuf, sizeof ebuf, STDERR_FILENO); + static int depth = 0; /* needed for nested note() calls */ + + static const char *label[] = { "error", "warning", "note" }; + static const char *color[] = { "%g1;31.", "%g1;35.", "%g1;36." }; + int line, col; + struct memfile *f; + const struct span0 *loc; + + ++depth; + if (span) { + loc = span->ex.len ? &span->ex : &span->sl; + f = getfile(loc->file); + const char *file = getfilepos(&line, &col, loc->file, loc->off); + bfmt(&out, "%s:%d:%d: ", file, line, col); + } + bfmt(&out, color[kind]); + bfmt(&out, "%s: %g.", label[kind]); + vbfmt(&out, fmt, ap); + bfmt(&out, "\n"); + if (span) { + uint i; + int nmark; + char mark = '^'; + + /* find start of line */ + for (i = loc->off - 1; i + 1 > 0 && f->p[i] != '\n'; --i) ; + if (i || f->p[i] == '\n') ++i; + + nmark = loc->len; + while (i < loc->off + loc->len) { + int j, end; + int curoff = bfmt(&out, "%5d | ", line); + const uchar *linep = &f->p[i]; + bool begintabs = 1; + for (end = 0; f->p[i] != '\n' && i < f->n; ++i, ++end) { + uchar c = f->p[i]; + if (c == '\t') { + if (!begintabs) c = ' '; + } else { + begintabs = 0; + } + ioputc(&out, c); + } + ioputc(&out, '\n'); + ++i; + + for (j = -curoff; j < 0; ++j) + ioputc(&out, j == -2 ? '|' : ' '); + for (begintabs = 1; j < col-1; ++j) { + uchar c = *linep++; + if (c == '\t') { + if (!begintabs) c = ' '; + } else { + c = ' '; + begintabs = 0; + } + ioputc(&out, c); + } + bfmt(&out, color[kind]); + do { + ioputc(&out, mark); + mark = '~'; + } while (--nmark > 0 && ++j < end); + col = 1; + ++line; + bfmt(&out, "%g.\n"); + --nmark; + } + ioputc(&out, '\n'); + } + + if (span && loc == &span->ex && span->sl.len) + if (span->ex.file != span->sl.file || !((uint) span->sl.off - span->ex.off < span->ex.len)) + note(&(struct span){ span->sl }, "expanded from here"); + + if (--depth == 0) ioflush(&out); +} + +void _Noreturn +fatal(const struct span *span, const char *fmt, ...) +{ + if (fmt) { + va_list ap; + va_start(ap, fmt); + vdiag(span, DGERROR, fmt, ap); + va_end(ap); + } + if (!fmt || span) efmt("Aborting due to previous error.\n"); + exit(1); +} + +int nerror, nwarn; +enum { MAXERROR = 20 }; + +void +error(const struct span *span, const char *fmt, ...) +{ + va_list ap; + + ++nerror; + va_start(ap, fmt); + vdiag(span, DGERROR, fmt, ap); + va_end(ap); + if (nerror > MAXERROR) { + efmt("Too many errors emitted, stopping now.\n"); + exit(1); + } +} + +void +warn(const struct span *span, const char *fmt, ...) +{ + va_list ap; + + if (ccopt.wnone) return; + if (ccopt.werror) ++nerror; + else ++nwarn; + va_start(ap, fmt); + vdiag(span, ccopt.werror ? DGERROR : DGWARN, fmt, ap); + va_end(ap); + if (nerror > MAXERROR) { + efmt("Too many errors emitted, stopping now.\n"); + exit(1); + } +} + +void +note(const struct span *span, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vdiag(span, DGNOTE, fmt, ap); + va_end(ap); +} + +/*** UTF util ***/ + +ushort * +utf8to16(uint *ulen, struct arena **arena, const uchar *s, size_t len) +{ + assert(0 && "nyi"); +} + +uint * +utf8to32(uint *ulen, struct arena **arena, const uchar *s, size_t len) +{ + uint *ret, *w; + const uchar *p, *end; + size_t n = 0; + bool istrunc; + + if (!len) return NULL; + + for (p = end = s; p < s + len; ++n) { + end = p; + if ((*p & 0xF8) == 0xF0) /* 11110xxx */ + p += 4; + else if ((*p & 0xF0) == 0xE0) /* 1110xxxx */ + p += 3; + else if ((*p & 0xE0) == 0xC0) /* 110xxxxx */ + p += 2; + else p += 1; + } + istrunc = p > s+len; + if (!istrunc) end += 1; + + ret = allocz(arena, n * sizeof *ret, sizeof *ret); + for (w = ret, p = s; p < end; ++w) { + if ((*p & 0xF8) == 0xF0) { /* 11110xxx */ + *w = (uint)(p[0] & 0x07) << 18 + | (uint)(p[1] & 0x3F) << 12 + | (uint)(p[2] & 0x3F) << 6 + | (uint)(p[3] & 0x3F); + p += 4; + } else if ((*p & 0xF0) == 0xE0) { /* 1110xxxx */ + *w = (uint)(p[0] & 0x07) << 12 + | (uint)(p[1] & 0x3F) << 6 + | (uint)(p[2] & 0x3F); + p += 3; + } else if ((*p & 0xE0) == 0xC0) { /* 110xxxxx */ + *w = (uint)(p[0] & 0x07) << 6 + | (uint)(p[1] & 0x3F); + p += 2; + } else { + *w = *p; + p += 1; + } + } + if (istrunc) *w++ = 0xFFFD; + *ulen = n; + + return ret; +} + +int +utf8enc(char p[4], uint cp) +{ + if ((cp & 0xffffff80) == 0) { + p[0] = cp; + return 1; + } else if ((cp & 0xfffff800) == 0) { + p[0] = 0xC0 | (cp >> 6 & 0x1F); + p[1] = 0x80 | (cp & 0x3F); + return 2; + } else if ((cp & 0xffff0000) == 0) { + p[0] = 0xE0 | (cp >> 12 & 0x0F); + p[1] = 0x80 | (cp >> 6 & 0x3F); + p[2] = 0x80 | (cp & 0x3F); + return 3; + } else { + p[0] = 0xF0 | (cp >> 18 & 0x07); + p[1] = 0x80 | (cp >> 12 & 0x3F); + p[2] = 0x80 | (cp >> 6 & 0x3F); + p[3] = 0x80 | (cp & 0x3F); + return 4; + } +} + +/* vim:set ts=3 sw=3 expandtab: */ |