/*-*- 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/alg/alg.h"
#include "libc/bits/bits.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.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 = false;
++s;
} else {
fprintf(f, "`");
}
bol = false;
break;
case '\n':
if (!pre && !ul0 && !ul2 && !ol0 && !ol2 && *s == '\n') {
fprintf(f, "\n");
bt2 = true;
++s;
} else if (bt1) {
fprintf(f, "
");
bt1 = false;
} else if (bt2 && *s == '`') {
fprintf(f, "
"); 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 += 3; } else if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ' && s[1] == '-' && s[2] == ' ') { ul2 = true; fprintf(f, "
\n\
");
/* // lefthand index: objects */
/* fprintf(f, " macro objects\n"); */ /* fprintf(f, " \n"); */
/* for (i = 0; i < dox->index.n; ++i) { */
/* if (dox->index.p[i].t != kMacro) continue; */
/* m = dox->macros.p + dox->index.p[i].i; */
/* if (m->ignore) continue; */
/* if (!m->is_objlike) continue; */
/* fprintf(f, "%s macro functions\n"); */ /* fprintf(f, " \n"); */
/* for (i = 0; i < dox->index.n; ++i) { */
/* if (dox->index.p[i].t != kMacro) continue; */
/* m = dox->macros.p + dox->index.p[i].i; */
/* if (m->ignore) continue; */
/* if (m->is_objlike) continue; */
/* fprintf(f, "%s objects\n"); fprintf(f, " \n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t != kObject) continue;
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
if (o->is_function) continue;
fprintf(f, "%s functions\n"); fprintf(f, " \n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t != kObject) continue;
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
if (!o->is_function) continue;
fprintf(f, "%s | \n");
for (i = 0; i < dox->index.n; ++i) {
if (dox->index.p[i].t == kObject) {
o = dox->objects.p + dox->index.p[i].i;
if (o->ignore) continue;
fprintf(f, "\n");
if (i) fprintf(f, " "); fprintf(f, " \n", o->name);
fprintf(f, " \n"); /* class=".api" */
} else {
continue;
m = dox->macros.p + dox->index.p[i].i;
if (m->ignore) continue;
fprintf(f, "\n");
if (i) fprintf(f, "%s", o->name, o->name); // title if (o->javadown && *o->javadown->title) { fprintf(f, ""); PrintText(f, o->javadown->title); fprintf(f, "\n"); } // text if (o->javadown && *o->javadown->text) { fprintf(f, " "); PrintText(f, o->javadown->text); fprintf(f, "\n"); } // parameters if (o->is_function && (o->is_variadic || o->params.n || HasTag(o->javadown, "param"))) { fprintf(f, " \n");
fprintf(f, "@param\n");
fprintf(f, " \n"); // .tag
}
// return
if (o->is_function) {
fprintf(f, "
\n");
if (IsNoReturn(o)) {
fprintf(f, "@noreturn\n");
} else {
fprintf(f, "@return\n");
was_outputted = false;
fprintf(f, " \n"); // .tag
}
}
// type
if (!o->is_function) {
fprintf(f, "
\n");
fprintf(f, "@type\n");
fprintf(f, " \n"); // .tag
}
// tags
if (o->javadown) {
for (k = 0; k < o->javadown->tags.n; ++k) {
if (!strcmp(o->javadown->tags.p[k].tag, "param")) continue;
if (!strcmp(o->javadown->tags.p[k].tag, "return")) continue;
if (!strcmp(o->javadown->tags.p[k].tag, "noreturn")) continue;
fprintf(f, "
\n");
fprintf(f, "@");
PrintText(f, o->javadown->tags.p[k].tag);
fprintf(f, "\n");
if (*o->javadown->tags.p[k].text) {
PrintText(f, o->javadown->tags.p[k].text);
fprintf(f, "\n");
}
fprintf(f, " \n"); // .tag
}
}
// sauce
if (strcmp(o->path, "missingno.c")) {
fprintf(f, "\n");
fprintf(f,
"@see %s",
o->path, o->line, o->path);
fprintf(f, " \n"); // .tag
}
fprintf(f, ""); fprintf(f, " \n", m->name);
fprintf(f, " \n"); /* class=".api" */
}
}
fprintf(f, "%s", m->name, m->name); // title if (m->javadown && *m->javadown->title) { fprintf(f, ""); PrintText(f, m->javadown->title); fprintf(f, "\n"); } // text if (m->javadown && *m->javadown->text) { fprintf(f, " "); PrintText(f, m->javadown->text); fprintf(f, "\n"); } // parameters if (!m->is_objlike && (m->params.n || HasTag(m->javadown, "param"))) { fprintf(f, " \n");
fprintf(f, "@param\n");
fprintf(f, " \n"); // .tag
}
fprintf(f, "
|