aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/io.c')
-rw-r--r--src/io.c1255
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: */