/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/bits.h" #include "libc/mem/alg.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/x/xasprintf.h" #include "third_party/chibicc/chibicc.h" #define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p)) struct Dox { unsigned char *p; struct Freelist { size_t n; void **p; } freelist; struct Set { size_t n; struct SetEntry { unsigned h; char *s; } * p; } names; struct DoxObjects { size_t n; struct DoxObject { bool ignore; char *type; char *name; char *path; int line; bool is_function; bool is_variadic; bool is_weak; bool is_inline; bool is_noreturn; bool is_destructor; bool is_constructor; bool is_force_align_arg_pointer; bool is_no_caller_saved_registers; char *visibility; struct Javadown *javadown; struct DoxObjectParams { size_t n; struct DoxObjectParam { char *type; char *name; } * p; } params; } * p; } objects; struct DoxMacros { size_t n; struct DoxMacro { bool ignore; char *name; char *path; int line; bool is_objlike; char *va_args_name; struct Javadown *javadown; struct DoxMacroParams { size_t n; struct DoxMacroParam { char *name; } * p; } params; } * p; } macros; struct DoxIndex { size_t n; struct DoxIndexEntry { enum DoxIndexType { kObject, kMacro, } t; int i; } * p; } index; }; static unsigned Hash(const void *p, unsigned long n) { unsigned h, i; for (h = i = 0; i < n; i++) { h += ((unsigned char *)p)[i]; h *= 0x9e3779b1; } return MAX(1, h); } static struct Dox *NewDox(void) { return calloc(1, sizeof(struct Dox)); } static void FreeDox(struct Dox *dox) { int i; if (dox) { for (i = 0; i < dox->freelist.n; ++i) { free(dox->freelist.p[i]); } free(dox->names.p); free(dox->freelist.p); free(dox->objects.p); free(dox->macros.p); free(dox->index.p); free(dox); } } static void *FreeLater(struct Dox *dox, void *p) { APPEND(dox->freelist); dox->freelist.p[dox->freelist.n - 1] = p; return p; } static int DeserializeInt(struct Dox *dox) { int x; x = (unsigned)dox->p[0] << 000 | (unsigned)dox->p[1] << 010 | (unsigned)dox->p[2] << 020 | (unsigned)dox->p[3] << 030; dox->p += 4; return x; } static char *DeserializeStr(struct Dox *dox) { char *s; size_t n; n = DeserializeInt(dox); s = malloc(n + 1); memcpy(s, dox->p, n); s[n] = '\0'; dox->p += n; return FreeLater(dox, s); } static struct Javadown *DeserializeJavadown(struct Dox *dox) { int i; bool present; struct Javadown *jd; if (DeserializeInt(dox)) { jd = FreeLater(dox, calloc(1, sizeof(struct Javadown))); jd->isfileoverview = DeserializeInt(dox); jd->title = DeserializeStr(dox); jd->text = DeserializeStr(dox); jd->tags.n = DeserializeInt(dox); jd->tags.p = FreeLater(dox, malloc(jd->tags.n * sizeof(*jd->tags.p))); for (i = 0; i < jd->tags.n; ++i) { jd->tags.p[i].tag = DeserializeStr(dox); jd->tags.p[i].text = DeserializeStr(dox); } return jd; } else { return NULL; } } static void DeserializeObject(struct Dox *dox, struct DoxObject *o) { int i; o->ignore = false; o->type = DeserializeStr(dox); o->name = DeserializeStr(dox); o->path = DeserializeStr(dox); o->line = DeserializeInt(dox); o->is_function = DeserializeInt(dox); o->is_variadic = DeserializeInt(dox); o->is_weak = DeserializeInt(dox); o->is_inline = DeserializeInt(dox); o->is_noreturn = DeserializeInt(dox); o->is_destructor = DeserializeInt(dox); o->is_constructor = DeserializeInt(dox); o->is_force_align_arg_pointer = DeserializeInt(dox); o->is_no_caller_saved_registers = DeserializeInt(dox); o->visibility = DeserializeStr(dox); o->javadown = DeserializeJavadown(dox); o->params.n = DeserializeInt(dox); o->params.p = FreeLater(dox, malloc(o->params.n * sizeof(*o->params.p))); for (i = 0; i < o->params.n; ++i) { o->params.p[i].type = DeserializeStr(dox); o->params.p[i].name = DeserializeStr(dox); } } static void DeserializeMacro(struct Dox *dox, struct DoxMacro *m) { int i; m->ignore = false; m->name = DeserializeStr(dox); m->path = DeserializeStr(dox); m->line = DeserializeInt(dox); m->is_objlike = DeserializeInt(dox); m->va_args_name = DeserializeStr(dox); m->javadown = DeserializeJavadown(dox); m->params.n = DeserializeInt(dox); m->params.p = FreeLater(dox, malloc(m->params.n * sizeof(*m->params.p))); for (i = 0; i < m->params.n; ++i) { m->params.p[i].name = DeserializeStr(dox); } } static void DeserializeDox(struct Dox *dox, const char *path) { int i, j, n; i = dox->objects.n; n = DeserializeInt(dox); dox->objects.p = realloc(dox->objects.p, (dox->objects.n + n) * sizeof(*dox->objects.p)); for (j = 0; j < n; ++j) { DeserializeObject(dox, dox->objects.p + i + j); } i = dox->macros.n; dox->objects.n += n; n = DeserializeInt(dox); dox->macros.p = realloc(dox->macros.p, (dox->macros.n + n) * sizeof(*dox->macros.p)); for (j = 0; j < n; ++j) { DeserializeMacro(dox, dox->macros.p + i + j); } dox->macros.n += n; CHECK_EQ(31337, DeserializeInt(dox)); } static void ReadDox(struct Dox *dox, const StringArray *files) { int i, fd; void *map; struct stat st; for (i = 0; i < files->len; ++i) { CHECK_NE(-1, (fd = open(files->data[i], O_RDONLY))); CHECK_NE(-1, fstat(fd, &st)); if (st.st_size) { CHECK_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0))); dox->p = map; DeserializeDox(dox, files->data[i]); munmap(map, st.st_size); } close(fd); } } static bool AddSet(struct Set *set, char *s) { unsigned i, h, k; h = Hash(s, strlen(s)); k = 0; for (k = 0;; ++k) { i = (h + k + ((k + 1) >> 1)) & (set->n - 1); if (!set->p[i].h) { set->p[i].h = h; set->p[i].s = s; return true; } if (h == set->p[i].h && !strcmp(s, set->p[i].s)) { return false; } } } static int CompareDoxIndexEntry(const void *p1, const void *p2, void *arg) { struct Dox *dox; const char *s1, *s2; struct DoxIndexEntry *a, *b; dox = arg, a = p1, b = p2; s1 = a->t == kObject ? dox->objects.p[a->i].name : dox->macros.p[a->i].name; s2 = b->t == kObject ? dox->objects.p[b->i].name : dox->macros.p[b->i].name; while (*s1 == '_') ++s1; while (*s2 == '_') ++s2; return strcasecmp(s1, s2); } static void IndexDox(struct Dox *dox) { size_t i, j, n; dox->names.n = _roundup2pow(dox->objects.n + dox->macros.n) << 1; dox->names.p = calloc(dox->names.n, sizeof(*dox->names.p)); n = 0; for (i = 0; i < dox->objects.n; ++i) { if (AddSet(&dox->names, dox->objects.p[i].name)) { ++n; } else { dox->objects.p[i].ignore = true; } } for (i = 0; i < dox->macros.n; ++i) { if (AddSet(&dox->names, dox->macros.p[i].name)) { ++n; } else { dox->macros.p[i].ignore = true; } } dox->index.n = n; dox->index.p = malloc(n * sizeof(*dox->index.p)); j = 0; for (i = 0; i < dox->objects.n; ++i) { if (dox->objects.p[i].ignore) continue; dox->index.p[j].t = kObject; dox->index.p[j].i = i; ++j; } for (i = 0; i < dox->macros.n; ++i) { if (dox->macros.p[i].ignore) continue; dox->index.p[j].t = kMacro; dox->index.p[j].i = i; ++j; } CHECK_EQ(n, j); qsort_r(dox->index.p, dox->index.n, sizeof(*dox->index.p), CompareDoxIndexEntry, dox); } /** * Escapes HTML entities and interprets basic Markdown syntax. */ static void PrintText(FILE *f, const char *s) { int c; bool bol, pre, ul0, ul2, ol0, ol2, bt1, bt2; for (bt1 = bt2 = ul2 = ul0 = ol2 = ol0 = pre = false, bol = true;;) { switch ((c = *s++)) { case '\0': if (bt1 || bt2) fprintf(f, ""); if (pre) fprintf(f, ""); if (ul0 || ul2) fprintf(f, ""); if (ol0 || ol2) fprintf(f, ""); return; case '&': fprintf(f, "&"); bol = false; break; case '<': fprintf(f, "<"); bol = false; break; case '>': fprintf(f, ">"); bol = false; break; case '"': fprintf(f, """); bol = false; break; case '\'': fprintf(f, "'"); bol = false; break; case '`': if (!pre && !bt1 && !bt2 && *s != '`') { fprintf(f, ""); bt1 = true; } else if (!pre && !bt1 && !bt2 && *s == '`') { fprintf(f, ""); bt2 = true; ++s; } else if (bt1) { fprintf(f, ""); bt1 = false; } else if (bt2 && *s == '`') { fprintf(f, ""); bt2 = false; ++s; } else { fprintf(f, "`"); } bol = false; break; case '\n': if (!pre && !ul0 && !ul2 && !ol0 && !ol2 && *s == '\n') { fprintf(f, "\n

"); bol = true; } else if (pre && s[0] != '\n') { if (s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ') { fprintf(f, "\n"); pre = false; bol = true; } else { fprintf(f, "\n"); bol = false; s += 4; } } else if (ul0 && s[0] == '-' && s[1] == ' ') { fprintf(f, "\n

  • "); s += 2; bol = false; } else if (ul2 && s[0] == ' ' && s[1] == ' ' && s[2] == '-' && s[3] == ' ') { fprintf(f, "\n
  • "); s += 4; bol = false; } else if (ul0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) { fprintf(f, "\n\n"); bol = true; ul0 = false; } else if (ul2 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) { fprintf(f, "\n\n"); bol = true; ul2 = false; } else if (ol0 && ('0' <= s[0] && s[0] <= '9') && s[1] == '.' && s[2] == ' ') { fprintf(f, "\n
  • "); s += 3; bol = false; } else if (ol2 && s[0] == ' ' && s[1] == ' ' && ('0' <= s[2] && s[2] <= '9') && s[3] == '.' && s[3] == ' ') { fprintf(f, "\n
  • "); s += 5; bol = false; } else if (ol0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) { fprintf(f, "\n\n"); bol = true; ol0 = false; } else if (ol2 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) { fprintf(f, "\n\n"); bol = true; ol2 = false; } else { fprintf(f, "\n"); bol = true; } break; case '-': if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ') { ul0 = true; fprintf(f, "