mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
5005f2e446
This change reduces the .bss memory requirement for all executables by O(64kb). The brk system calls are now fully tested and figured out and might be useful for tiny programs that only target System Five.
967 lines
28 KiB
C
967 lines
28 KiB
C
/*-*- 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, "</code>");
|
|
if (pre) fprintf(f, "</pre>");
|
|
if (ul0 || ul2) fprintf(f, "</ul>");
|
|
if (ol0 || ol2) fprintf(f, "</ol>");
|
|
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, "<code>");
|
|
bt1 = true;
|
|
} else if (!pre && !bt1 && !bt2 && *s == '`') {
|
|
fprintf(f, "<code>");
|
|
bt2 = true;
|
|
++s;
|
|
} else if (bt1) {
|
|
fprintf(f, "</code>");
|
|
bt1 = false;
|
|
} else if (bt2 && *s == '`') {
|
|
fprintf(f, "</code>");
|
|
bt2 = false;
|
|
++s;
|
|
} else {
|
|
fprintf(f, "`");
|
|
}
|
|
bol = false;
|
|
break;
|
|
case '\n':
|
|
if (!pre && !ul0 && !ul2 && !ol0 && !ol2 && *s == '\n') {
|
|
fprintf(f, "\n<p>");
|
|
bol = true;
|
|
} else if (pre && s[0] != '\n') {
|
|
if (s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ') {
|
|
fprintf(f, "</pre>\n");
|
|
pre = false;
|
|
bol = true;
|
|
} else {
|
|
fprintf(f, "\n");
|
|
bol = false;
|
|
s += 4;
|
|
}
|
|
} else if (ul0 && s[0] == '-' && s[1] == ' ') {
|
|
fprintf(f, "\n<li>");
|
|
s += 2;
|
|
bol = false;
|
|
} else if (ul2 && s[0] == ' ' && s[1] == ' ' && s[2] == '-' &&
|
|
s[3] == ' ') {
|
|
fprintf(f, "\n<li>");
|
|
s += 4;
|
|
bol = false;
|
|
} else if (ul0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) {
|
|
fprintf(f, "\n</ul>\n");
|
|
bol = true;
|
|
ul0 = false;
|
|
} else if (ul2 && s[0] != '\n' &&
|
|
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
|
fprintf(f, "\n</ul>\n");
|
|
bol = true;
|
|
ul2 = false;
|
|
} else if (ol0 && ('0' <= s[0] && s[0] <= '9') && s[1] == '.' &&
|
|
s[2] == ' ') {
|
|
fprintf(f, "\n<li>");
|
|
s += 3;
|
|
bol = false;
|
|
} else if (ol2 && s[0] == ' ' && s[1] == ' ' &&
|
|
('0' <= s[2] && s[2] <= '9') && s[3] == '.' && s[3] == ' ') {
|
|
fprintf(f, "\n<li>");
|
|
s += 5;
|
|
bol = false;
|
|
} else if (ol0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) {
|
|
fprintf(f, "\n</ol>\n");
|
|
bol = true;
|
|
ol0 = false;
|
|
} else if (ol2 && s[0] != '\n' &&
|
|
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
|
fprintf(f, "\n</ol>\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, "<ul><li>");
|
|
++s;
|
|
} else {
|
|
fprintf(f, "-");
|
|
}
|
|
bol = false;
|
|
break;
|
|
case '1':
|
|
if (bol && !ol0 && !ol2 && !ul0 && !ul2 && s[0] == '.' && s[1] == ' ') {
|
|
ol0 = true;
|
|
fprintf(f, "<ol><li>");
|
|
s += 2;
|
|
} else {
|
|
fprintf(f, "1");
|
|
}
|
|
bol = false;
|
|
break;
|
|
case ' ':
|
|
if (bol && !pre && s[0] == ' ' && s[1] == ' ' && s[2] == ' ') {
|
|
pre = true;
|
|
fprintf(f, "<pre>");
|
|
s += 3;
|
|
} else if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ' &&
|
|
s[1] == '-' && s[2] == ' ') {
|
|
ul2 = true;
|
|
fprintf(f, "<ul><li>");
|
|
s += 3;
|
|
} else if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ' &&
|
|
('0' <= s[1] && s[1] <= '9') && s[2] == '.' && s[3] == ' ') {
|
|
ol2 = true;
|
|
fprintf(f, "<ol><li>");
|
|
s += 4;
|
|
} else {
|
|
fprintf(f, " ");
|
|
}
|
|
bol = false;
|
|
break;
|
|
default:
|
|
fprintf(f, "%c", c);
|
|
bol = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool HasTag(struct Javadown *jd, const char *tag) {
|
|
int k;
|
|
if (jd) {
|
|
for (k = 0; k < jd->tags.n; ++k) {
|
|
if (!strcmp(jd->tags.p[k].tag, tag)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool IsNoReturn(struct DoxObject *o) {
|
|
return o->is_noreturn || HasTag(o->javadown, "noreturn");
|
|
}
|
|
|
|
static void PrintDox(struct Dox *dox, FILE *f) {
|
|
int i, j, k;
|
|
char *prefix;
|
|
bool was_outputted;
|
|
struct DoxMacro *m;
|
|
struct DoxObject *o;
|
|
|
|
// header
|
|
fprintf(f, "\
|
|
<!doctype html>\n\
|
|
<html lang=\"en\">\n\
|
|
<meta charset=\"utf-8\">\n\
|
|
<title>Cosmopolitan C Library</title>\n\
|
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\
|
|
<link rel=\"canonical\" href=\"https://justine.lol/cosmopolitan/documentation.html\">\n\
|
|
<link rel=\"stylesheet\" href=\"style.css\">\n\
|
|
<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\">\n\
|
|
<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n\
|
|
<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\n\
|
|
<link rel=\"manifest\" href=\"/site.webmanifest\">\n\
|
|
<style>\n\
|
|
*, body {\n\
|
|
min-width: 0;\n\
|
|
}\n\
|
|
body {\n\
|
|
width: 100ch;\n\
|
|
max-width: calc(100vw - 3em);\n\
|
|
overflow-wrap: break-word;\n\
|
|
}\n\
|
|
body, .nav, .dox {\n\
|
|
margin: 0 auto;\n\
|
|
}\n\
|
|
.dox, header {\n\
|
|
display: flex;\n\
|
|
}\n\
|
|
header {\n\
|
|
flex-direction: column;\n\
|
|
align-items: center;\n\
|
|
margin: 4em auto 2em;\n\
|
|
}\n\
|
|
header>* {\n\
|
|
min-width: max-content;\n\
|
|
}\n\
|
|
header>img {\n\
|
|
max-width: 196px;\n\
|
|
min-width: 196px;\n\
|
|
max-height: 105px;\n\
|
|
min-height: 105px;\n\
|
|
}\n\
|
|
main {\n\
|
|
width: 80ch;\n\
|
|
}\n\
|
|
.toc {\n\
|
|
display: block;\n\
|
|
overflow-x: auto;\n\
|
|
}\n\
|
|
.toc a {\n\
|
|
text-decoration: none;\n\
|
|
}\n\
|
|
h3 a {\n\
|
|
color: inherit;\n\
|
|
text-decoration: none;\n\
|
|
}\n\
|
|
pre {\n\
|
|
margin-left: 0;\n\
|
|
padding: 12px;\n\
|
|
background: #f6f6f6;\n\
|
|
overflow-x: auto;\n\
|
|
border-radius: 5px;\n\
|
|
}\n\
|
|
hr {\n\
|
|
height: 1px;\n\
|
|
margin-bottom: 16px;\n\
|
|
color: #d6d9dc;\n\
|
|
background: #d6d9dc;\n\
|
|
border: 0;\n\
|
|
}\n\
|
|
.category {\n\
|
|
font-weight: bold;\n\
|
|
}\n\
|
|
.tagname {\n\
|
|
font-size: 12pt;\n\
|
|
font-weight: bold;\n\
|
|
}\n\
|
|
.tag {\n\
|
|
margin-top: .5em;\n\
|
|
}\n\
|
|
.tag dl {\n\
|
|
margin-top: .5em;\n\
|
|
margin-bottom: .5em;\n\
|
|
margin-left: 1em;\n\
|
|
}\n\
|
|
#top {\n\
|
|
position: fixed;\n\
|
|
left: max(0.5rem, calc(50vw - 56ch));\n\
|
|
bottom: 0.5rem;\n\
|
|
width: 2rem;\n\
|
|
height: 2rem;\n\
|
|
opacity: 0.45;\n\
|
|
transition: opacity 0.4s;\n\
|
|
}\n\
|
|
#top:hover {\n\
|
|
opacity: 0.9;\n\
|
|
}\n\
|
|
#search {\n\
|
|
top: 1ch;\n\
|
|
right: 1ch;\n\
|
|
float: right;\n\
|
|
position: sticky;\n\
|
|
margin: 1ch;\n\
|
|
}\n\
|
|
@media (max-width: 60ch) {\n\
|
|
.toc {\n\
|
|
display: none;\n\
|
|
}\n\
|
|
}\n\
|
|
@media print {\n\
|
|
#top, #search, nav, nav.toc { \n\
|
|
display: none;\n\
|
|
}\n\
|
|
}\n\
|
|
</style>\n\
|
|
<noscript><style>\n\
|
|
.toc { display: block; }\n\
|
|
#search { display: none; }\n\
|
|
</style></noscript>\n\
|
|
\n\
|
|
<header>\n\
|
|
<img width=\"196\" height=\"105\"\n\
|
|
src=\"//worker.jart.workers.dev/cosmopolitan/cosmopolitan.png\"\n\
|
|
title=\"cosmopolitan honeybadger\"\n\
|
|
alt=\"honeybadger\">\n\
|
|
<h1>cosmopolitan libc</h1>\n\
|
|
<div>your build-once run-anywhere c library</div>\n\
|
|
</header>\n\
|
|
\n\
|
|
<nav class=\"nav\">\n\
|
|
<ul>\n\
|
|
<li><a href=\"index.html\">Intro</a>\n\
|
|
<li><a href=\"download.html\">Download</a>\n\
|
|
<li><a href=\"functions.html\">Functions</a>\n\
|
|
<li><a class=\"active\" href=\"documentation.html\">Documentation</a>\n\
|
|
<li><a href=\"tutorials.html\">Tutorials</a>\n\
|
|
<li><a href=\"https://github.com/jart/cosmopolitan\">GitHub</a>\n\
|
|
<li><a href=\"license.html\">License</a>\n\
|
|
<li class=\"right\"><a href=\"../index.html\">» jart's web page</a>\n\
|
|
</ul>\n\
|
|
</nav>\n\
|
|
\n\
|
|
<div class=\"dox\">\n\
|
|
<nav class=\"toc\">\n\
|
|
");
|
|
|
|
/* // lefthand index: objects */
|
|
/* fprintf(f, "<p><span class=\"category\">macro objects</span>\n"); */
|
|
/* fprintf(f, "<p>\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, "<a href=\"#%s\">%s</a><br>\n", m->name, m->name); */
|
|
/* } */
|
|
|
|
/* // lefthand index: functions */
|
|
/* fprintf(f, "<p><span class=\"category\">macro functions</span>\n"); */
|
|
/* fprintf(f, "<p>\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, "<a href=\"#%s\">%s</a><br>\n", m->name, m->name); */
|
|
/* } */
|
|
|
|
// lefthand index: objects
|
|
fprintf(f, "<p><span class=\"category\">objects</span>\n");
|
|
fprintf(f, "<p>\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, "<a href=\"#%s\">%s</a><br>\n", o->name, o->name);
|
|
}
|
|
|
|
// lefthand index: functions
|
|
fprintf(f, "<p><span class=\"category\">functions</span>\n");
|
|
fprintf(f, "<p>\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, "<a href=\"#%s\">%s</a><br>\n", o->name, o->name);
|
|
}
|
|
fprintf(f, "</p></nav>\n\n");
|
|
|
|
// jump to top button
|
|
fprintf(f, "\
|
|
<a href=\"#\" id=\"top\"><svg viewBox=\"-60 -60 120 120\" stroke=\"#404040\" stroke-width=\"10\">\n\
|
|
<title>Top of the page</title>\n\
|
|
<circle r=\"50\" fill=\"#f6f6f6\" />\n\
|
|
<path d=\"M-25,18l25,-40l25,40\" fill=\"none\" stroke-linecap=\"round\" />\n\
|
|
</svg></a>");
|
|
|
|
// right hand column
|
|
fprintf(f, "<main>\n");
|
|
|
|
// search bar
|
|
fprintf(f, "\
|
|
<input type=\"search\" id=\"search\" placeholder=\"Search...\" list=\"search-list\" spellcheck=\"false\" />\n\
|
|
<datalist id=\"search-list\"></datalist>\n\
|
|
<script>\n\
|
|
document.addEventListener('DOMContentLoaded', function () {\n\
|
|
var datalist = document.getElementById('search-list')\n\
|
|
document.querySelectorAll('.api').forEach(function (el) {\n\
|
|
var option = document.createElement('option')\n\
|
|
option.setAttribute('value', el.id)\n\
|
|
datalist.appendChild(option)\n\
|
|
})\n\
|
|
function scrollIntoView(event) {\n\
|
|
var value = event.target.value\n\
|
|
var el = document.getElementById(value) \n\
|
|
if (el) {\n\
|
|
location.hash = value\n\
|
|
el.scrollIntoView()\n\
|
|
}\n\
|
|
}\n\
|
|
var search = document.getElementById('search')\n\
|
|
search.addEventListener('change', scrollIntoView)\n\
|
|
search.addEventListener('keypress', function (event) {\n\
|
|
if (event.key === 'Enter') scrollIntoView(event)\n\
|
|
})\n\
|
|
})\n\
|
|
</script>\n\n");
|
|
|
|
// righthand contents
|
|
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, "<hr>");
|
|
fprintf(f, "<div id=\"%s\" class=\"api\">\n", o->name);
|
|
fprintf(f, "<h3><a href=\"#%s\">%s</a></h3>", o->name, o->name);
|
|
|
|
// title
|
|
if (o->javadown && *o->javadown->title) {
|
|
fprintf(f, "<p>");
|
|
PrintText(f, o->javadown->title);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
// text
|
|
if (o->javadown && *o->javadown->text) {
|
|
fprintf(f, "<p>");
|
|
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, "<div class=\"tag\">\n");
|
|
fprintf(f, "<span class=\"tagname\">@param</span>\n");
|
|
fprintf(f, "<dl>\n");
|
|
if (o->params.n) {
|
|
for (j = 0; j < o->params.n; ++j) {
|
|
fprintf(f, "<dt>");
|
|
PrintText(f, o->params.p[j].type);
|
|
fprintf(f, " <em>");
|
|
PrintText(f, o->params.p[j].name);
|
|
fprintf(f, "</em>\n");
|
|
if (o->javadown) {
|
|
prefix = xasprintf("%s ", o->params.p[j].name);
|
|
for (k = 0; k < o->javadown->tags.n; ++k) {
|
|
if (!strcmp(o->javadown->tags.p[k].tag, "param") &&
|
|
_startswith(o->javadown->tags.p[k].text, prefix)) {
|
|
fprintf(f, "<dd>");
|
|
PrintText(f, o->javadown->tags.p[k].text + strlen(prefix));
|
|
fprintf(f, "\n");
|
|
break;
|
|
}
|
|
}
|
|
free(prefix);
|
|
}
|
|
}
|
|
} else {
|
|
for (k = 0; k < o->javadown->tags.n; ++k) {
|
|
if (!strcmp(o->javadown->tags.p[k].tag, "param")) {
|
|
fprintf(f, "<dd>");
|
|
PrintText(f, o->javadown->tags.p[k].text);
|
|
fprintf(f, "\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (o->is_variadic) {
|
|
fprintf(f, "<dt>...\n");
|
|
}
|
|
fprintf(f, "</dl>\n");
|
|
fprintf(f, "</div>\n"); // .tag
|
|
}
|
|
|
|
// return
|
|
if (o->is_function) {
|
|
fprintf(f, "<div class=\"tag\">\n");
|
|
if (IsNoReturn(o)) {
|
|
fprintf(f, "<span class=\"tagname\">@noreturn</span>\n");
|
|
} else {
|
|
fprintf(f, "<span class=\"tagname\">@return</span>\n");
|
|
was_outputted = false;
|
|
fprintf(f, "<dl>\n");
|
|
if (o->javadown) {
|
|
for (k = 0; k < o->javadown->tags.n; ++k) {
|
|
if (strcmp(o->javadown->tags.p[k].tag, "return")) continue;
|
|
if (!was_outputted) {
|
|
fprintf(f, "<dt>");
|
|
PrintText(f, o->type);
|
|
was_outputted = true;
|
|
}
|
|
fprintf(f, "\n<dd>");
|
|
PrintText(f, o->javadown->tags.p[k].text);
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
if (!was_outputted) {
|
|
fprintf(f, "<dt>");
|
|
PrintText(f, o->type);
|
|
}
|
|
fprintf(f, "</dl>\n");
|
|
}
|
|
fprintf(f, "</div>\n"); // .tag
|
|
}
|
|
|
|
// type
|
|
if (!o->is_function) {
|
|
fprintf(f, "<div class=\"tag\">\n");
|
|
fprintf(f, "<span class=\"tagname\">@type</span>\n");
|
|
fprintf(f, "<dl>\n");
|
|
fprintf(f, "<dt>");
|
|
PrintText(f, o->type);
|
|
fprintf(f, "</dl>\n");
|
|
fprintf(f, "</div>\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, "<div class=\"tag\">\n");
|
|
fprintf(f, "<span class=\"tagname\">@");
|
|
PrintText(f, o->javadown->tags.p[k].tag);
|
|
fprintf(f, "</span>\n");
|
|
if (*o->javadown->tags.p[k].text) {
|
|
PrintText(f, o->javadown->tags.p[k].text);
|
|
fprintf(f, "\n");
|
|
}
|
|
fprintf(f, "</div>\n"); // .tag
|
|
}
|
|
}
|
|
|
|
// sauce
|
|
if (strcmp(o->path, "missingno.c")) {
|
|
fprintf(f, "<div class=\"tag\">\n");
|
|
fprintf(f,
|
|
"<span class=\"tagname\">@see</span> <a "
|
|
"href=\"https://github.com/jart/cosmopolitan/blob/master/"
|
|
"%s#L%d\">%s</a>",
|
|
o->path, o->line, o->path);
|
|
fprintf(f, "</div>\n"); // .tag
|
|
}
|
|
|
|
fprintf(f, "</div>\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, "<hr>");
|
|
fprintf(f, "<div id=\"%s\" class=\"api\">\n", m->name);
|
|
fprintf(f, "<h3><a href=\"#%s\">%s</a></h3>", m->name, m->name);
|
|
// title
|
|
if (m->javadown && *m->javadown->title) {
|
|
fprintf(f, "<p>");
|
|
PrintText(f, m->javadown->title);
|
|
fprintf(f, "\n");
|
|
}
|
|
// text
|
|
if (m->javadown && *m->javadown->text) {
|
|
fprintf(f, "<p>");
|
|
PrintText(f, m->javadown->text);
|
|
fprintf(f, "\n");
|
|
}
|
|
// parameters
|
|
if (!m->is_objlike && (m->params.n || HasTag(m->javadown, "param"))) {
|
|
fprintf(f, "<div class=\"tag\">\n");
|
|
fprintf(f, "<span class=\"tagname\">@param</span>\n");
|
|
fprintf(f, "<dl>\n");
|
|
if (m->params.n) {
|
|
for (j = 0; j < m->params.n; ++j) {
|
|
fprintf(f, "<dt>");
|
|
fprintf(f, "<em>");
|
|
PrintText(f, m->params.p[j].name);
|
|
fprintf(f, "</em>\n");
|
|
if (m->javadown) {
|
|
prefix = xasprintf("%s ", m->params.p[j].name);
|
|
for (k = 0; k < m->javadown->tags.n; ++k) {
|
|
if (!strcmp(m->javadown->tags.p[k].tag, "param") &&
|
|
_startswith(m->javadown->tags.p[k].text, prefix)) {
|
|
fprintf(f, "<dd>");
|
|
PrintText(f, m->javadown->tags.p[k].text + strlen(prefix));
|
|
fprintf(f, "\n");
|
|
break;
|
|
}
|
|
}
|
|
free(prefix);
|
|
}
|
|
}
|
|
} else {
|
|
for (k = 0; k < m->javadown->tags.n; ++k) {
|
|
if (!strcmp(m->javadown->tags.p[k].tag, "param")) {
|
|
fprintf(f, "<dd>");
|
|
PrintText(f, m->javadown->tags.p[k].text);
|
|
fprintf(f, "\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fprintf(f, "</dl>\n");
|
|
fprintf(f, "</div>\n"); // .tag
|
|
}
|
|
fprintf(f, "</div>\n"); /* class=".api" */
|
|
}
|
|
}
|
|
fprintf(f, "</main></div>\n");
|
|
|
|
// footer
|
|
fprintf(f, "\
|
|
\n\
|
|
<footer>\n\
|
|
<p>\n\
|
|
<div style=\"float:right;text-align:right\">\n\
|
|
Free Libre & Open Source<br>\n\
|
|
<a href=\"https://github.com/jart\">github.com/jart/cosmopolitan</a>\n\
|
|
</div>\n\
|
|
Feedback<br>\n\
|
|
jtunney@gmail.com\n\
|
|
</p>\n\
|
|
<div style=\"clear:both\"></div>\n\
|
|
</footer>\n\
|
|
");
|
|
}
|
|
|
|
/**
|
|
* Merges documentation data and outputs HTML.
|
|
*/
|
|
void drop_dox(const StringArray *files, const char *path) {
|
|
FILE *f;
|
|
struct Dox *dox;
|
|
dox = NewDox();
|
|
ReadDox(dox, files);
|
|
IndexDox(dox);
|
|
f = fopen(path, "w");
|
|
PrintDox(dox, f);
|
|
fclose(f);
|
|
FreeDox(dox);
|
|
}
|