cosmopolitan/tool/build/xlat.c
Justine Tunney 3f49889841
Make important improvements
- Fix preadv() and pwritev() for old distros
- Introduce _npassert() and _unassert() macros
- Prove that file locks work properly on Windows
- Support fcntl(F_DUPFD_CLOEXEC) on more systems
2022-09-14 22:39:08 -07:00

330 lines
8.9 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 2022 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/fmt/conv.h"
#include "libc/log/check.h"
#include "libc/math.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tab.internal.h"
#include "libc/x/xasprintf.h"
/**
* @fileoverview Tool for generating rldecode'd character sets, e.g.
*
* # generate http token table
* o//tool/build/xlat.com -TiC ' ()<>@,;:\"/[]?={}' -i
*/
int dig;
int xlat[256];
bool identity;
const char *symbol;
static int Bing(int c) {
if (!c) return L'';
if (c == ' ') return L'';
if (c == '$') return L'§';
if (c == '\\') return L'';
return kCp437[c & 255];
}
static void Fill(int f(int)) {
int i;
for (i = 0; i < 256; ++i) {
if (f(i)) {
xlat[i] = identity ? i : 1;
}
}
}
static void Invert(void) {
int i;
for (i = 0; i < 256; ++i) {
xlat[i] = !xlat[i];
}
}
static void Negate(void) {
int i;
for (i = 0; i < 256; ++i) {
xlat[i] = ~xlat[i] & 255;
}
}
static void Negative(void) {
int i;
for (i = 0; i < 256; ++i) {
xlat[i] = -xlat[i] & 255;
}
}
static bool ArgNeedsShellQuotes(const char *s) {
if (*s) {
for (;;) {
switch (*s++ & 255) {
case 0:
return false;
case '-':
case '.':
case '/':
case '_':
case '=':
case ':':
case '0' ... '9':
case 'A' ... 'Z':
case 'a' ... 'z':
break;
default:
return true;
}
}
} else {
return true;
}
}
static char *AddShellQuotes(const char *s) {
char *p, *q;
size_t i, j, n;
n = strlen(s);
p = malloc(1 + n * 5 + 1 + 1);
j = 0;
p[j++] = '\'';
for (i = 0; i < n; ++i) {
if (s[i] != '\'') {
p[j++] = s[i];
} else {
p[j + 0] = '\'';
p[j + 1] = '"';
p[j + 2] = '\'';
p[j + 3] = '"';
p[j + 4] = '\'';
j += 5;
}
}
p[j++] = '\'';
p[j] = 0;
if ((q = realloc(p, j + 1))) p = q;
return p;
}
static const char *GetArg(char *argv[], int i, int *k) {
if (argv[*k][i + 1]) {
return argv[*k] + i + 1;
} else {
return argv[++*k];
}
}
int main(int argc, char *argv[]) {
const char *arg;
int i, j, k, opt;
dig = 1;
symbol = "kXlatTab";
for (k = 1; k < argc; ++k) {
if (argv[k][0] != '-') {
for (i = 0; argv[k][i]; ++i) {
/* xlat[argv[k][i] & 255] = identity ? i : dig; */
xlat[argv[k][i] & 255] = identity ? (argv[k][i] & 255) : dig;
}
} else {
i = 0;
moar:
++i;
if ((opt = argv[k][i])) {
switch (opt) {
case 's':
symbol = GetArg(argv, i, &k);
break;
case 'x':
dig = atoi(GetArg(argv, i, &k)) & 255;
break;
case 'i':
Invert();
goto moar;
case 'I':
identity = !identity;
goto moar;
case 'n':
Negative();
goto moar;
case 'N':
Negate();
goto moar;
case 'T':
Fill(isascii);
goto moar;
case 'C':
Fill(iscntrl);
goto moar;
case 'A':
Fill(isalpha);
goto moar;
case 'B':
Fill(isblank);
goto moar;
case 'G':
Fill(isgraph);
goto moar;
case 'P':
Fill(ispunct);
goto moar;
case 'D':
Fill(isdigit);
goto moar;
case 'U':
Fill(isupper);
goto moar;
case 'L':
Fill(islower);
goto moar;
default:
fprintf(stderr, "error: unrecognized option: %c\n", opt);
return 1;
}
}
}
}
////////////////////////////////////////////////////////////
printf("#include \"libc/macros.internal.h\"\n");
printf("\n");
printf("//\tgenerated by:\n");
printf("//\t");
for (i = 0; i < argc; ++i) {
if (i) printf(" ");
printf("%s", !ArgNeedsShellQuotes(argv[i]) ? argv[i]
: _gc(AddShellQuotes(argv[i])));
}
printf("\n");
////////////////////////////////////////////////////////////
printf("//\n");
printf("//\t present absent\n");
printf("//\t ──────────────── ────────────────\n");
for (i = 0; i < 16; ++i) {
char16_t absent[16];
char16_t present[16];
for (j = 0; j < 16; ++j) {
if (xlat[i * 16 + j]) {
absent[j] = L' ';
present[j] = Bing(i * 16 + j);
} else {
absent[j] = Bing(i * 16 + j);
present[j] = L' ';
}
}
printf("//\t %.16hs %.16hs 0x%02x\n", present, absent, i * 16);
}
////////////////////////////////////////////////////////////
printf("//\n");
printf("//\tconst char %s[256] = {\n//\t", symbol);
for (i = 0; i < 16; ++i) {
printf(" ");
for (j = 0; j < 16; ++j) {
printf("%2d,", (char)xlat[i * 16 + j]);
}
printf(" // 0x%02x\n//\t", i * 16);
}
printf("};\n");
printf("\n");
////////////////////////////////////////////////////////////
printf("\t.initbss 300,_init_%s\n", symbol);
printf("%s:\n", symbol);
printf("\t.zero\t256\n");
printf("\t.endobj\t%s,globl\n", symbol);
printf("\t.previous\n");
printf("\n");
////////////////////////////////////////////////////////////
printf("\t.initro 300,_init_%s\n", symbol);
printf("%s.rom:\n", symbol);
int thebloat = 0;
int thetally = 0;
int thecount = 0;
int runstart = 0;
int runchar = -1;
int runcount = 0;
for (i = 0;; ++i) {
if (i < 256 && xlat[i] == runchar) {
++runcount;
} else {
if (runcount) {
printf("\t.byte\t%-24s# %02x-%02x %hc-%hc\n",
_gc(xasprintf("%3d,%d", runcount, runchar)), runstart,
runstart + runcount - 1, Bing(runstart),
Bing(runstart + runcount - 1));
thetally += 2;
thecount += runcount;
}
if (i < 256) {
runcount = 1;
runchar = xlat[i];
runstart = i;
}
}
if (i == 256) {
break;
}
}
CHECK_EQ(256, thecount);
printf("\t.byte\t%-24s# terminator\n", "0,0");
thetally += 2;
thebloat = thetally;
for (i = 0; (thetally + i) % 8; i += 2) {
printf("\t.byte\t%-24s# padding\n", "0,0");
thebloat += 2;
}
printf("\t.endobj\t%s.rom,globl\n", symbol);
printf("\n");
////////////////////////////////////////////////////////////
printf("\t.init.start 300,_init_%s\n", symbol);
printf("\tcall\trldecode\n");
thebloat += 5;
int padding = 8 - thetally % 8;
if (padding < 8) {
if (padding >= 4) {
thebloat += 1;
printf("\tlodsl\n");
padding -= 4;
}
if (padding >= 2) {
thebloat += 2;
printf("\tlodsw\n");
}
}
printf("\t.init.end 300,_init_%s\n", symbol);
////////////////////////////////////////////////////////////
printf("\n");
printf("//\t%d bytes total (%d%% original size)\n", thebloat,
(int)round((double)thebloat / 256 * 100));
}