mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-27 04:50:28 +00:00
Further refine documentation
This commit is contained in:
parent
1bc3a25505
commit
548dcb9f08
27 changed files with 389 additions and 1478 deletions
145
third_party/chibicc/as.c
vendored
145
third_party/chibicc/as.c
vendored
|
@ -27,6 +27,8 @@
|
|||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/chibicc/file.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "tool/build/lib/elfwriter.h"
|
||||
|
||||
|
@ -443,49 +445,6 @@ static unsigned Hash(const void *p, unsigned long n) {
|
|||
return MAX(1, h);
|
||||
}
|
||||
|
||||
static bool StartsWith(const char *s, const char *prefix) {
|
||||
for (;;) {
|
||||
if (!*prefix) return true;
|
||||
if (!*s) return false;
|
||||
if (*s++ != *prefix++) return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool EndsWith(const char *s, const char *suffix) {
|
||||
size_t n, m;
|
||||
n = strlen(s);
|
||||
m = strlen(suffix);
|
||||
if (m > n) return false;
|
||||
return !memcmp(s + n - m, suffix, m);
|
||||
}
|
||||
|
||||
static char *Format(const char *fmt, ...) {
|
||||
char *res;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vasprintf(&res, fmt, va);
|
||||
va_end(va);
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *DirName(const char *path) {
|
||||
return dirname(strdup(path));
|
||||
}
|
||||
|
||||
static char *JoinPaths(const char *path, const char *other) {
|
||||
if (!*other) {
|
||||
return strdup(path);
|
||||
} else if (!*path) {
|
||||
return strdup(other);
|
||||
} else if (StartsWith(other, "/") || !strcmp(path, ".")) {
|
||||
return strdup(other);
|
||||
} else if (EndsWith(path, "/")) {
|
||||
return Format("%s%s", path, other);
|
||||
} else {
|
||||
return Format("%s/%s", path, other);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPunctMergeable(int c) {
|
||||
switch (c) {
|
||||
case ';':
|
||||
|
@ -613,11 +572,11 @@ static void ReadFlags(struct As *a, int argc, char *argv[]) {
|
|||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-o")) {
|
||||
a->outpath = StrDup(a, argv[++i]);
|
||||
} else if (StartsWith(argv[i], "-o")) {
|
||||
} else if (startswith(argv[i], "-o")) {
|
||||
a->outpath = StrDup(a, argv[i] + 2);
|
||||
} else if (!strcmp(argv[i], "-I")) {
|
||||
SaveString(&a->incpaths, strdup(argv[++i]));
|
||||
} else if (StartsWith(argv[i], "-I")) {
|
||||
} else if (startswith(argv[i], "-I")) {
|
||||
SaveString(&a->incpaths, strdup(argv[i] + 2));
|
||||
} else if (!strcmp(argv[i], "-Z")) {
|
||||
a->inhibiterr = true;
|
||||
|
@ -677,72 +636,6 @@ static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) {
|
|||
}
|
||||
}
|
||||
|
||||
static void CanonicalizeNewline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static void RemoveBackslashNewline(char *p) {
|
||||
int i, j, n;
|
||||
for (i = j = n = 0; p[i];) {
|
||||
if (p[i] == '\\' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
n++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static char *ReadFile(const char *path) {
|
||||
char *p;
|
||||
FILE *fp;
|
||||
int buflen, nread, end, n;
|
||||
if (!strcmp(path, "-")) {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
buflen = 4096;
|
||||
nread = 0;
|
||||
p = calloc(1, buflen);
|
||||
for (;;) {
|
||||
end = buflen - 2;
|
||||
n = fread(p + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
p = realloc(p, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
if (nread > 0 && p[nread - 1] == '\\') {
|
||||
p[nread - 1] = '\n';
|
||||
} else if (nread == 0 || p[nread - 1] != '\n') {
|
||||
p[nread++] = '\n';
|
||||
}
|
||||
p[nread] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
static void PrintLocation(struct As *a) {
|
||||
fprintf(stderr,
|
||||
"%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[a->i].s].path],
|
||||
|
@ -768,7 +661,7 @@ static char *FindInclude(struct As *a, const char *file) {
|
|||
char *path;
|
||||
struct stat st;
|
||||
for (i = 0; i < a->incpaths.n; ++i) {
|
||||
path = JoinPaths(a->incpaths.p[i], file);
|
||||
path = xjoinpaths(a->incpaths.p[i], file);
|
||||
if (stat(path, &st) != -1 && S_ISREG(st.st_mode)) return path;
|
||||
free(path);
|
||||
}
|
||||
|
@ -780,10 +673,10 @@ static void Tokenize(struct As *a, int path) {
|
|||
char *p, *path2;
|
||||
struct Slice buf;
|
||||
bool bol, isfloat, isfpu;
|
||||
p = SaveString(&a->strings, ReadFile(a->strings.p[path]));
|
||||
if (!memcmp(p, "\357\273\277", 3)) p += 3;
|
||||
CanonicalizeNewline(p);
|
||||
RemoveBackslashNewline(p);
|
||||
p = SaveString(&a->strings, read_file(a->strings.p[path]));
|
||||
p = skip_bom(p);
|
||||
canonicalize_newline(p);
|
||||
remove_backslash_newline(p);
|
||||
line = 1;
|
||||
bol = true;
|
||||
while ((c = *p)) {
|
||||
|
@ -1031,7 +924,7 @@ static void OnSymbol(struct As *a, int name) {
|
|||
static void OnLocalLabel(struct As *a, int id) {
|
||||
int i;
|
||||
char *name;
|
||||
name = Format(".Label.%d", a->counter++);
|
||||
name = xasprintf(".Label.%d", a->counter++);
|
||||
SaveString(&a->strings, name);
|
||||
AppendSlice(a);
|
||||
a->slices.p[a->slices.n - 1].p = name;
|
||||
|
@ -1796,13 +1689,13 @@ static int GrabSection(struct As *a, int name, int flags, int type) {
|
|||
static void OnSection(struct As *a, struct Slice s) {
|
||||
int name, flags, type;
|
||||
name = SliceDup(a, GetSlice(a));
|
||||
if (StartsWith(a->strings.p[name], ".text")) {
|
||||
if (startswith(a->strings.p[name], ".text")) {
|
||||
flags = SHF_ALLOC | SHF_EXECINSTR;
|
||||
type = SHT_PROGBITS;
|
||||
} else if (StartsWith(a->strings.p[name], ".data")) {
|
||||
} else if (startswith(a->strings.p[name], ".data")) {
|
||||
flags = SHF_ALLOC | SHF_WRITE;
|
||||
type = SHT_PROGBITS;
|
||||
} else if (StartsWith(a->strings.p[name], ".bss")) {
|
||||
} else if (startswith(a->strings.p[name], ".bss")) {
|
||||
flags = SHF_ALLOC | SHF_WRITE;
|
||||
type = SHT_NOBITS;
|
||||
} else {
|
||||
|
@ -2372,7 +2265,7 @@ static noinline void OpBsu(struct As *a, struct Slice opname, int op) {
|
|||
OpBsuImpl(a, opname, op);
|
||||
}
|
||||
|
||||
static int OpF6(struct As *a, struct Slice s, int reg) {
|
||||
static noinline int OpF6Impl(struct As *a, struct Slice s, int reg) {
|
||||
int modrm, imm, disp;
|
||||
modrm = ParseModrm(a, &disp);
|
||||
reg |= GetOpSize(a, s, modrm, 1) << 3;
|
||||
|
@ -2380,6 +2273,10 @@ static int OpF6(struct As *a, struct Slice s, int reg) {
|
|||
return reg;
|
||||
}
|
||||
|
||||
static noinline int OpF6(struct As *a, struct Slice s, int reg) {
|
||||
return OpF6Impl(a, s, reg);
|
||||
}
|
||||
|
||||
static void OnTest(struct As *a, struct Slice s) {
|
||||
int reg, modrm, imm, disp;
|
||||
if (IsPunct(a, a->i, '$')) {
|
||||
|
@ -2571,8 +2468,8 @@ static bool HasXmmOnLine(struct As *a) {
|
|||
int i;
|
||||
for (i = 0; !IsPunct(a, a->i + i, ';'); ++i) {
|
||||
if (IsSlice(a, a->i + i) && a->slices.p[a->things.p[a->i + i].i].n >= 4 &&
|
||||
(StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") ||
|
||||
StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) {
|
||||
(startswith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") ||
|
||||
startswith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4002,7 +3899,7 @@ void Assembler(int argc, char *argv[]) {
|
|||
a = NewAssembler();
|
||||
ReadFlags(a, argc, argv);
|
||||
SaveString(&a->incpaths, strdup("."));
|
||||
SaveString(&a->incpaths, DirName(a->strings.p[a->inpath]));
|
||||
SaveString(&a->incpaths, xdirname(a->strings.p[a->inpath]));
|
||||
Tokenize(a, a->inpath);
|
||||
/* PrintThings(a); */
|
||||
Assemble(a);
|
||||
|
|
4
third_party/chibicc/chibicc.h
vendored
4
third_party/chibicc/chibicc.h
vendored
|
@ -130,9 +130,6 @@ Token *tokenize_string_literal(Token *, Type *);
|
|||
bool consume(Token **, Token *, char *, size_t);
|
||||
bool equal(Token *, char *, size_t);
|
||||
void convert_pp_tokens(Token *);
|
||||
void remove_backslash_newline(char *);
|
||||
void canonicalize_newline(char *);
|
||||
char *read_file(char *);
|
||||
int read_escaped_char(char **, char *);
|
||||
|
||||
#define UNREACHABLE() error("internal error at %s:%d", __FILE__, __LINE__)
|
||||
|
@ -450,6 +447,7 @@ struct Type {
|
|||
int align; // alignment
|
||||
bool is_unsigned; // unsigned or signed
|
||||
bool is_atomic; // true if _Atomic
|
||||
bool is_const; // const
|
||||
bool is_ms_abi; // microsoft abi
|
||||
Type *origin; // for type compatibility check
|
||||
// Pointer-to or array-of type. We intentionally use the same member
|
||||
|
|
7
third_party/chibicc/dox1.c
vendored
7
third_party/chibicc/dox1.c
vendored
|
@ -79,7 +79,8 @@ static void SerializeJavadown(struct Buffer *buf, struct Javadown *jd) {
|
|||
}
|
||||
|
||||
static char *DescribeScalar(struct Type *ty, char *name) {
|
||||
return xasprintf("%s%s%s", ty->is_atomic ? "_Atomic " : "",
|
||||
return xasprintf("%s%s%s%s", ty->is_atomic ? "_Atomic " : "",
|
||||
ty->is_const ? "const " : "",
|
||||
ty->is_unsigned ? "unsigned " : "", name);
|
||||
}
|
||||
|
||||
|
@ -134,7 +135,7 @@ static char *DescribeType(struct Type *ty) {
|
|||
return strdup("ANONYMOUS-UNION");
|
||||
}
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return strdup("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,6 +179,7 @@ static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
|
|||
SerializeStr(&dox->buf, GetFileName(dox->objects.p[i]));
|
||||
SerializeInt(&dox->buf, GetLine(dox->objects.p[i]));
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_function);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->ty->is_variadic);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_weak);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_inline);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_noreturn);
|
||||
|
@ -248,6 +250,7 @@ static void SerializeAsmdown(struct DoxWriter *dox, struct Asmdown *ad,
|
|||
SerializeStr(&dox->buf, filename);
|
||||
SerializeInt(&dox->buf, ad->symbols.p[i].line);
|
||||
SerializeInt(&dox->buf, true); // TODO: is_function
|
||||
SerializeInt(&dox->buf, false); // is_variadic
|
||||
SerializeInt(&dox->buf, false); // TODO: is_weak
|
||||
SerializeInt(&dox->buf, false); // is_inline
|
||||
SerializeInt(&dox->buf, false); // is_noreturn
|
||||
|
|
108
third_party/chibicc/dox2.c
vendored
108
third_party/chibicc/dox2.c
vendored
|
@ -47,6 +47,7 @@ struct Dox {
|
|||
char *path;
|
||||
int line;
|
||||
bool is_function;
|
||||
bool is_variadic;
|
||||
bool is_weak;
|
||||
bool is_inline;
|
||||
bool is_noreturn;
|
||||
|
@ -177,6 +178,7 @@ static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
|
|||
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);
|
||||
|
@ -210,7 +212,7 @@ static void DeserializeMacro(struct Dox *dox, struct DoxMacro *m) {
|
|||
}
|
||||
}
|
||||
|
||||
static void DeserializeDox(struct Dox *dox) {
|
||||
static void DeserializeDox(struct Dox *dox, const char *path) {
|
||||
int i, j, n;
|
||||
i = dox->objects.n;
|
||||
n = DeserializeInt(dox);
|
||||
|
@ -242,7 +244,7 @@ static void ReadDox(struct Dox *dox, const StringArray *files) {
|
|||
CHECK_NE(MAP_FAILED,
|
||||
(map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
dox->p = map;
|
||||
DeserializeDox(dox);
|
||||
DeserializeDox(dox, files->data[i]);
|
||||
munmap(map, st.st_size);
|
||||
}
|
||||
close(fd);
|
||||
|
@ -317,15 +319,19 @@ static void IndexDox(struct Dox *dox) {
|
|||
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, bt1, bt2;
|
||||
for (bt1 = bt2 = ul2 = ul0 = pre = false, bol = true;;) {
|
||||
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, "&");
|
||||
|
@ -368,14 +374,19 @@ static void PrintText(FILE *f, const char *s) {
|
|||
bol = false;
|
||||
break;
|
||||
case '\n':
|
||||
if (!pre && !ul0 && !ul2 && *s == '\n') {
|
||||
if (!pre && !ul0 && !ul2 && !ol0 && !ol2 && *s == '\n') {
|
||||
fprintf(f, "\n<p>");
|
||||
bol = true;
|
||||
} else if (pre && s[0] != '\n' &&
|
||||
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
||||
fprintf(f, "</pre>\n");
|
||||
pre = false;
|
||||
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;
|
||||
|
@ -394,29 +405,65 @@ static void PrintText(FILE *f, const char *s) {
|
|||
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 && s[0] == ' ') {
|
||||
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> ");
|
||||
} else if (bol && !ul0 && !ul2 && s[0] == ' ' && s[1] == '-' &&
|
||||
s[2] == ' ') {
|
||||
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, " ");
|
||||
}
|
||||
|
@ -469,6 +516,9 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
.nav {\n\
|
||||
margin-bottom: 0;\n\
|
||||
}\n\
|
||||
.toc {\n\
|
||||
overflow-x: auto;\n\
|
||||
}\n\
|
||||
.toc a {\n\
|
||||
text-decoration: none;\n\
|
||||
}\n\
|
||||
|
@ -514,7 +564,10 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
</style>\n\
|
||||
\n\
|
||||
<header>\n\
|
||||
<img width=\"196\" height=\"105\" src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\" alt=\"honeybadger\">\n\
|
||||
<img width=\"196\" height=\"105\"\n\
|
||||
src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\"\n\
|
||||
title=\"cosmopolitan honeybadger\"\n\
|
||||
alt=\"honeybadger\">\n\
|
||||
<h1>cosmopolitan libc</h1>\n\
|
||||
<span>build-once run-anywhere c without devops</span>\n\
|
||||
</header>\n\
|
||||
|
@ -533,7 +586,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
\n\
|
||||
<table class=\"dox\" width=\"960\">\n\
|
||||
<tr>\n\
|
||||
<td width=\"320\" valign=\"top\" class=\"toc\">\n\
|
||||
<td width=\"283\" valign=\"top\" class=\"toc\">\n\
|
||||
");
|
||||
|
||||
/* // lefthand index: objects */
|
||||
|
@ -581,7 +634,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
|
||||
// righthand contents
|
||||
fprintf(f, "<td width=\"640\" valign=\"top\">\n");
|
||||
fprintf(f, "<td width=\"667\" valign=\"top\">\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;
|
||||
|
@ -606,7 +659,8 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
|
||||
// parameters
|
||||
if (o->is_function && (o->params.n || HasTag(o->javadown, "param"))) {
|
||||
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");
|
||||
|
@ -641,6 +695,9 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (o->is_variadic) {
|
||||
fprintf(f, "<dt>...\n");
|
||||
}
|
||||
fprintf(f, "</dl>\n");
|
||||
fprintf(f, "</div>\n"); // .tag
|
||||
}
|
||||
|
@ -676,6 +733,17 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -714,21 +782,18 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
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");
|
||||
|
@ -767,7 +832,6 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
fprintf(f, "</dl>\n");
|
||||
fprintf(f, "</div>\n"); // .tag
|
||||
}
|
||||
|
||||
fprintf(f, "</div>\n"); /* class=".api" */
|
||||
}
|
||||
}
|
||||
|
|
106
third_party/chibicc/file.c
vendored
Normal file
106
third_party/chibicc/file.c
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "third_party/chibicc/chibicc.h"
|
||||
|
||||
// Slurps contents of file.
|
||||
char *read_file(const char *path) {
|
||||
char *p;
|
||||
FILE *fp;
|
||||
int buflen, nread, end, n;
|
||||
if (!strcmp(path, "-")) {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
buflen = 4096;
|
||||
nread = 0;
|
||||
p = calloc(1, buflen);
|
||||
for (;;) {
|
||||
end = buflen - 2;
|
||||
n = fread(p + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
p = realloc(p, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
if (nread > 0 && p[nread - 1] == '\\') {
|
||||
p[nread - 1] = '\n';
|
||||
} else if (nread == 0 || p[nread - 1] != '\n') {
|
||||
p[nread++] = '\n';
|
||||
}
|
||||
p[nread] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
char *skip_bom(char *p) {
|
||||
// UTF-8 texts may start with a 3-byte "BOM" marker sequence.
|
||||
// If exists, just skip them because they are useless bytes.
|
||||
// (It is actually not recommended to add BOM markers to UTF-8
|
||||
// texts, but it's not uncommon particularly on Windows.)
|
||||
if (!memcmp(p, "\357\273\277", 3)) p += 3;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Replaces \r or \r\n with \n.
|
||||
void canonicalize_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
// Removes backslashes followed by a newline.
|
||||
void remove_backslash_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
// We want to keep the number of newline characters so that
|
||||
// the logical line number matches the physical one.
|
||||
// This counter maintain the number of newlines we have removed.
|
||||
int n = 0;
|
||||
bool instring = false;
|
||||
while (p[i]) {
|
||||
if (instring) {
|
||||
if (p[i] == '"' && p[i - 1] != '\\') {
|
||||
instring = false;
|
||||
}
|
||||
} else {
|
||||
if (p[i] == '"') {
|
||||
instring = true;
|
||||
} else if (p[i] == '/' && p[i + 1] == '*') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
while (p[i]) {
|
||||
if (p[i] == '*' && p[i + 1] == '/') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
break;
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (p[i] == '\\' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
n++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
13
third_party/chibicc/file.h
vendored
Normal file
13
third_party/chibicc/file.h
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
char *skip_bom(char *);
|
||||
char *read_file(const char *);
|
||||
void remove_backslash_newline(char *);
|
||||
void canonicalize_newline(char *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_ */
|
50
third_party/chibicc/parse.c
vendored
50
third_party/chibicc/parse.c
vendored
|
@ -48,7 +48,6 @@ typedef struct {
|
|||
bool is_static;
|
||||
bool is_extern;
|
||||
bool is_inline;
|
||||
bool is_const;
|
||||
bool is_tls;
|
||||
bool is_weak;
|
||||
bool is_ms_abi;
|
||||
|
@ -233,6 +232,8 @@ static Node *new_ulong(long val, Token *tok) {
|
|||
}
|
||||
|
||||
static Node *new_var_node(Obj *var, Token *tok) {
|
||||
if (!var) DebugBreak();
|
||||
CHECK_NOTNULL(var);
|
||||
Node *node = new_node(ND_VAR, tok);
|
||||
node->var = var;
|
||||
return node;
|
||||
|
@ -647,6 +648,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
};
|
||||
Type *ty = copy_type(ty_int);
|
||||
int counter = 0;
|
||||
bool is_const = false;
|
||||
bool is_atomic = false;
|
||||
while (is_typename(tok)) {
|
||||
// Handle storage class specifiers.
|
||||
|
@ -682,7 +684,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
goto Continue;
|
||||
}
|
||||
if (CONSUME(&tok, tok, "const")) {
|
||||
if (attr) attr->is_const = true;
|
||||
is_const = true;
|
||||
goto Continue;
|
||||
}
|
||||
// These keywords are recognized but ignored.
|
||||
|
@ -841,6 +843,10 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
ty = copy_type(ty);
|
||||
ty->is_atomic = true;
|
||||
}
|
||||
/* if (attr && is_const) { */
|
||||
/* ty = copy_type(ty); */
|
||||
/* ty->is_const = true; */
|
||||
/* } */
|
||||
*rest = tok;
|
||||
return ty;
|
||||
}
|
||||
|
@ -873,6 +879,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
Type head = {};
|
||||
Type *cur = &head;
|
||||
bool is_variadic = false;
|
||||
enter_scope();
|
||||
while (!EQUAL(tok, ")")) {
|
||||
if (cur != &head) tok = skip(tok, ',');
|
||||
if (EQUAL(tok, "...")) {
|
||||
|
@ -884,6 +891,9 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
Type *ty2 = declspec(&tok, tok, NULL);
|
||||
ty2 = declarator(&tok, tok, ty2);
|
||||
Token *name = ty2->name;
|
||||
if (name) {
|
||||
new_lvar(strndup(name->loc, name->len), ty2);
|
||||
}
|
||||
if (ty2->kind == TY_ARRAY) {
|
||||
// "array of T" is converted to "pointer to T" only in the parameter
|
||||
// context. For example, *argv[] is converted to **argv by this.
|
||||
|
@ -897,6 +907,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
}
|
||||
cur = cur->next = copy_type(ty2);
|
||||
}
|
||||
leave_scope();
|
||||
if (cur == &head) is_variadic = true;
|
||||
ty = func_type(ty);
|
||||
ty->params = head.next;
|
||||
|
@ -2471,6 +2482,16 @@ static Node *shift(Token **rest, Token *tok) {
|
|||
}
|
||||
}
|
||||
|
||||
static Node *get_vla_size(Type *ty, Token *tok) {
|
||||
if (ty->vla_size) {
|
||||
return new_var_node(ty->vla_size, tok);
|
||||
} else {
|
||||
Node *lhs = compute_vla_size(ty, tok);
|
||||
Node *rhs = new_var_node(ty->vla_size, tok);
|
||||
return new_binary(ND_COMMA, lhs, rhs, tok);
|
||||
}
|
||||
}
|
||||
|
||||
// In C, `+` operator is overloaded to perform the pointer arithmetic.
|
||||
// If p is a pointer, p+n adds not n but sizeof(*p)*n to the value of p,
|
||||
// so that p+n points to the location n elements (not bytes) ahead of p.
|
||||
|
@ -2491,8 +2512,7 @@ static Node *new_add(Node *lhs, Node *rhs, Token *tok) {
|
|||
}
|
||||
// VLA + num
|
||||
if (lhs->ty->base->kind == TY_VLA) {
|
||||
rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok),
|
||||
tok);
|
||||
rhs = new_binary(ND_MUL, rhs, get_vla_size(lhs->ty->base, tok), tok);
|
||||
return new_binary(ND_ADD, lhs, rhs, tok);
|
||||
}
|
||||
// ptr + num
|
||||
|
@ -2509,8 +2529,7 @@ static Node *new_sub(Node *lhs, Node *rhs, Token *tok) {
|
|||
return new_binary(ND_SUB, lhs, rhs, tok);
|
||||
// VLA + num
|
||||
if (lhs->ty->base->kind == TY_VLA) {
|
||||
rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok),
|
||||
tok);
|
||||
rhs = new_binary(ND_MUL, rhs, get_vla_size(lhs->ty->base, tok), tok);
|
||||
add_type(rhs);
|
||||
Node *node = new_binary(ND_SUB, lhs, rhs, tok);
|
||||
node->ty = lhs->ty;
|
||||
|
@ -3044,7 +3063,9 @@ static Node *primary(Token **rest, Token *tok) {
|
|||
if (EQUAL(tok, "sizeof")) {
|
||||
Node *node = unary(rest, tok->next);
|
||||
add_type(node);
|
||||
if (node->ty->kind == TY_VLA) return new_var_node(node->ty->vla_size, tok);
|
||||
if (node->ty->kind == TY_VLA) {
|
||||
return get_vla_size(node->ty, tok);
|
||||
}
|
||||
return new_ulong(node->ty->size, tok);
|
||||
}
|
||||
if ((EQUAL(tok, "_Alignof") || EQUAL(tok, "__alignof__")) &&
|
||||
|
@ -3416,8 +3437,9 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
|
|||
fn->is_no_instrument_function |= attr->is_no_instrument_function;
|
||||
fn->is_force_align_arg_pointer |= attr->is_force_align_arg_pointer;
|
||||
fn->is_no_caller_saved_registers |= attr->is_no_caller_saved_registers;
|
||||
fn->javadown = fn->javadown ?: current_javadown;
|
||||
fn->is_root = !(fn->is_static && fn->is_inline);
|
||||
fn->javadown = fn->javadown ?: current_javadown;
|
||||
current_javadown = NULL;
|
||||
if (consume_attribute(&tok, tok, "asm")) {
|
||||
tok = skip(tok, '(');
|
||||
fn->asmname = ConsumeStringLiteral(&tok, tok);
|
||||
|
@ -3526,16 +3548,20 @@ static void scan_globals(void) {
|
|||
globals = head.next;
|
||||
}
|
||||
|
||||
static char *prefix_builtin(const char *name) {
|
||||
return xstrcat("__builtin_", name);
|
||||
}
|
||||
|
||||
static Obj *declare0(char *name, Type *ret) {
|
||||
if (!opt_no_builtin) new_gvar(name, func_type(ret));
|
||||
return new_gvar(xstrcat("__builtin_", name), func_type(ret));
|
||||
return new_gvar(prefix_builtin(name), func_type(ret));
|
||||
}
|
||||
|
||||
static Obj *declare1(char *name, Type *ret, Type *p1) {
|
||||
Type *ty = func_type(ret);
|
||||
ty->params = copy_type(p1);
|
||||
if (!opt_no_builtin) new_gvar(name, ty);
|
||||
return new_gvar(xstrcat("__builtin_", name), ty);
|
||||
return new_gvar(prefix_builtin(name), ty);
|
||||
}
|
||||
|
||||
static Obj *declare2(char *name, Type *ret, Type *p1, Type *p2) {
|
||||
|
@ -3543,7 +3569,7 @@ static Obj *declare2(char *name, Type *ret, Type *p1, Type *p2) {
|
|||
ty->params = copy_type(p1);
|
||||
ty->params->next = copy_type(p2);
|
||||
if (!opt_no_builtin) new_gvar(name, ty);
|
||||
return new_gvar(xstrcat("__builtin_", name), ty);
|
||||
return new_gvar(prefix_builtin(name), ty);
|
||||
}
|
||||
|
||||
static Obj *declare3(char *s, Type *r, Type *a, Type *b, Type *c) {
|
||||
|
@ -3552,7 +3578,7 @@ static Obj *declare3(char *s, Type *r, Type *a, Type *b, Type *c) {
|
|||
ty->params->next = copy_type(b);
|
||||
ty->params->next->next = copy_type(c);
|
||||
if (!opt_no_builtin) new_gvar(s, ty);
|
||||
return new_gvar(xstrcat("__builtin_", s), ty);
|
||||
return new_gvar(prefix_builtin(s), ty);
|
||||
}
|
||||
|
||||
static void math0(char *name) {
|
||||
|
|
1
third_party/chibicc/test/asm_test.c
vendored
1
third_party/chibicc/test/asm_test.c
vendored
|
@ -1,3 +1,4 @@
|
|||
asm(".ident\t\"hello\"");
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
|
|
23
third_party/chibicc/test/vla_test.c
vendored
23
third_party/chibicc/test/vla_test.c
vendored
|
@ -1,7 +1,30 @@
|
|||
#include "libc/macros.h"
|
||||
#include "third_party/chibicc/test/test.h"
|
||||
|
||||
int index1d(int xn, int p[xn], int x) {
|
||||
return p[x];
|
||||
}
|
||||
|
||||
int index2d(int yn, int xn, int (*p)[yn][xn], int y, int x) {
|
||||
return (*p)[y][x];
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
ASSERT(30, ({
|
||||
int a[5] = {00, 10, 20, 30, 40, 50};
|
||||
index1d(5, a, 3);
|
||||
}));
|
||||
|
||||
/* ASSERT(210, ({ */
|
||||
/* int a[3][3] = { */
|
||||
/* {000, 010, 020}, */
|
||||
/* {100, 110, 120}, */
|
||||
/* {200, 210, 220}, */
|
||||
/* }; */
|
||||
/* index2d(3, 3, a, 2, 1); */
|
||||
/* })); */
|
||||
|
||||
ASSERT(20, ({
|
||||
int n = 5;
|
||||
int x[n];
|
||||
|
|
103
third_party/chibicc/tokenize.c
vendored
103
third_party/chibicc/tokenize.c
vendored
|
@ -1,4 +1,5 @@
|
|||
#include "third_party/chibicc/chibicc.h"
|
||||
#include "third_party/chibicc/file.h"
|
||||
|
||||
#define LOOKINGAT(TOK, OP) (!memcmp(TOK, OP, sizeof(OP) - 1))
|
||||
|
||||
|
@ -610,40 +611,6 @@ Token *tokenize(File *file) {
|
|||
return head.next;
|
||||
}
|
||||
|
||||
// Returns the contents of a given file.
|
||||
char *read_file(char *path) {
|
||||
FILE *fp;
|
||||
if (strcmp(path, "-") == 0) {
|
||||
// By convention, read from stdin if a given filename is "-".
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
int buflen = 4096;
|
||||
int nread = 0;
|
||||
char *buf = calloc(1, buflen);
|
||||
// Read the entire file.
|
||||
for (;;) {
|
||||
int end = buflen - 2; // extra 2 bytes for the trailing "\n\0"
|
||||
int n = fread(buf + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
buf = realloc(buf, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
// Make sure that the last logical line is properly terminated with '\n'.
|
||||
if (nread > 0 && buf[nread - 1] == '\\')
|
||||
buf[nread - 1] = '\n';
|
||||
else if (nread == 0 || buf[nread - 1] != '\n')
|
||||
buf[nread++] = '\n';
|
||||
buf[nread] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
File **get_input_files(void) {
|
||||
return input_files;
|
||||
}
|
||||
|
@ -657,68 +624,6 @@ File *new_file(char *name, int file_no, char *contents) {
|
|||
return file;
|
||||
}
|
||||
|
||||
// Replaces \r or \r\n with \n.
|
||||
void canonicalize_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
// Removes backslashes followed by a newline.
|
||||
void remove_backslash_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
// We want to keep the number of newline characters so that
|
||||
// the logical line number matches the physical one.
|
||||
// This counter maintain the number of newlines we have removed.
|
||||
int n = 0;
|
||||
bool instring = false;
|
||||
while (p[i]) {
|
||||
if (instring) {
|
||||
if (p[i] == '"' && p[i - 1] != '\\') {
|
||||
instring = false;
|
||||
}
|
||||
} else {
|
||||
if (p[i] == '"') {
|
||||
instring = true;
|
||||
} else if (p[i] == '/' && p[i + 1] == '*') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
while (p[i]) {
|
||||
if (p[i] == '*' && p[i + 1] == '/') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
break;
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (p[i] == '\\' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
n++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static uint32_t read_universal_char(char *p, int len) {
|
||||
uint32_t c = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
@ -761,11 +666,7 @@ static void convert_universal_chars(char *p) {
|
|||
Token *tokenize_file(char *path) {
|
||||
char *p = read_file(path);
|
||||
if (!p) return NULL;
|
||||
// UTF-8 texts may start with a 3-byte "BOM" marker sequence.
|
||||
// If exists, just skip them because they are useless bytes.
|
||||
// (It is actually not recommended to add BOM markers to UTF-8
|
||||
// texts, but it's not uncommon particularly on Windows.)
|
||||
if (!memcmp(p, "\xef\xbb\xbf", 3)) p += 3;
|
||||
p = skip_bom(p);
|
||||
canonicalize_newline(p);
|
||||
remove_backslash_newline(p);
|
||||
convert_universal_chars(p);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue