mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-06 19:28:29 +00:00
Remove some dead code
This commit is contained in:
parent
168d1c157e
commit
73c0faa1b5
66 changed files with 324 additions and 7705 deletions
|
@ -6,7 +6,6 @@ PKGS += TOOL_BUILD
|
|||
TOOL_BUILD_FILES := $(wildcard tool/build/*)
|
||||
TOOL_BUILD_SRCS = $(filter %.c,$(TOOL_BUILD_FILES))
|
||||
TOOL_BUILD_HDRS = $(filter %.h,$(TOOL_BUILD_FILES))
|
||||
TOOL_BUILD_CTESTS = $(filter %.ctest,$(TOOL_BUILD_FILES))
|
||||
|
||||
TOOL_BUILD_BINS = \
|
||||
$(TOOL_BUILD_COMS) \
|
||||
|
@ -21,8 +20,6 @@ TOOL_BUILD_BINS = \
|
|||
o/$(MODE)/tool/build/printf \
|
||||
o/$(MODE)/tool/build/dd
|
||||
|
||||
TOOL_BUILD_CALCULATOR = o/$(MODE)/tool/build/calculator.com
|
||||
|
||||
TOOL_BUILD_OBJS = \
|
||||
$(TOOL_BUILD_SRCS:%.c=o/$(MODE)/%.o)
|
||||
|
||||
|
@ -31,8 +28,7 @@ TOOL_BUILD_COMS = \
|
|||
|
||||
TOOL_BUILD_CHECKS = \
|
||||
$(TOOL_BUILD).pkg \
|
||||
$(TOOL_BUILD_HDRS:%=o/$(MODE)/%.ok) \
|
||||
$(TOOL_BUILD_CTESTS:%=o/$(MODE)/%.ok)
|
||||
$(TOOL_BUILD_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
TOOL_BUILD_DIRECTDEPS = \
|
||||
DSP_CORE \
|
||||
|
@ -80,12 +76,6 @@ o/$(MODE)/tool/build/build.pkg: \
|
|||
$(TOOL_BUILD_OBJS) \
|
||||
$(foreach x,$(TOOL_BUILD_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/%.ctest.ok: \
|
||||
%.ctest \
|
||||
$(TOOL_BUILD_CALCULATOR) \
|
||||
$(VM)
|
||||
@$(COMPILE) -AMKWIDES -wtT$@ $(VM) $(TOOL_BUILD_CALCULATOR) $<
|
||||
|
||||
o/$(MODE)/tool/build/%.com.dbg: \
|
||||
$(TOOL_BUILD_DEPS) \
|
||||
o/$(MODE)/tool/build/build.pkg \
|
||||
|
|
|
@ -1,760 +0,0 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "dsp/tty/tty.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/morton.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/tinymath/emodl.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
#define INT int128_t
|
||||
#define FLOAT long double
|
||||
#define EPSILON 1e-16l
|
||||
|
||||
#define BANNER \
|
||||
"\
|
||||
Reverse Polish Notation Calculator\n\
|
||||
Copyright 2020 Justine Alexandra Roberts Tunney\n\
|
||||
This is fast portable lightweight software. You have the freedom to build\n\
|
||||
software just like it painlessly. See http://github.com/jart/cosmopolitan\n\
|
||||
"
|
||||
|
||||
#define USAGE1 \
|
||||
"SYNOPSIS\n\
|
||||
\n\
|
||||
Reverse Polish Notation Calculator\n\
|
||||
\n\
|
||||
USAGE\n\
|
||||
\n"
|
||||
|
||||
#define USAGE2 \
|
||||
" [FLAGS] [FILE...]\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-h\n\
|
||||
-? shows this information\n\
|
||||
-i force interactive mode\n\
|
||||
\n\
|
||||
KEYBOARD SHORTCUTS\n\
|
||||
\n\
|
||||
CTRL-D Closes standard input\n\
|
||||
CTRL-C Sends SIGINT to program\n\
|
||||
CTRL-U Redraw line\n\
|
||||
CTRL-L Redraw display\n\
|
||||
\n\
|
||||
FUNCTIONS\n\
|
||||
\n"
|
||||
|
||||
#define USAGE3 \
|
||||
"\n\
|
||||
EXAMPLES\n\
|
||||
\n\
|
||||
calculator.com\n\
|
||||
40 2 +\n\
|
||||
42\n\
|
||||
\n\
|
||||
echo '2 2 + . cr' | calculator.com\n\
|
||||
4\n\
|
||||
\n\
|
||||
calculator.com <<EOF\n\
|
||||
true assert\n\
|
||||
-1 ~ 0 = assert\n\
|
||||
3 2 / 1.5 = assert\n\
|
||||
81 3 // 27 = assert\n\
|
||||
2 8 ** 256 = assert\n\
|
||||
pi cos -1 = assert\n\
|
||||
pi sqrt pi sqrt * pi - abs epsilon < assert\n\
|
||||
nan isnormal ! assert\n\
|
||||
0b1010101 0b0110101 ^ 0b1100000 = assert\n\
|
||||
0b1010101 popcnt 4 = assert\n\
|
||||
EOF\n\
|
||||
\n\
|
||||
CONTACT\n\
|
||||
\n\
|
||||
Justine Tunney <jtunney@gmail.com>\n\
|
||||
https://github.com/jart/cosmooplitan\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
#define CTRL(C) ((C) ^ 0100)
|
||||
#define Fatal(...) Log(kFatal, __VA_ARGS__)
|
||||
#define Warning(...) Log(kWarning, __VA_ARGS__)
|
||||
|
||||
enum Severity {
|
||||
kFatal,
|
||||
kWarning,
|
||||
};
|
||||
|
||||
enum Exception {
|
||||
kUnderflow = 1,
|
||||
kDivideError,
|
||||
};
|
||||
|
||||
struct Bytes {
|
||||
size_t i, n;
|
||||
char *p;
|
||||
};
|
||||
|
||||
struct History {
|
||||
size_t i, n;
|
||||
struct Bytes *p;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
enum Type {
|
||||
kInt,
|
||||
kFloat,
|
||||
} t;
|
||||
union {
|
||||
INT i;
|
||||
FLOAT f;
|
||||
};
|
||||
};
|
||||
|
||||
struct Function {
|
||||
const char sym[16];
|
||||
void (*fun)(void);
|
||||
const char *doc;
|
||||
};
|
||||
|
||||
jmp_buf thrower;
|
||||
const char *file;
|
||||
struct Bytes token;
|
||||
struct History history;
|
||||
struct Value stack[128];
|
||||
int sp, comment, line, column, interactive;
|
||||
|
||||
uint32_t gray(uint32_t x) {
|
||||
return x ^ (x >> 1);
|
||||
}
|
||||
|
||||
uint32_t ungray(uint32_t x) {
|
||||
x ^= x >> 16;
|
||||
x ^= x >> 8;
|
||||
x ^= x >> 4;
|
||||
x ^= x >> 2;
|
||||
x ^= x >> 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
INT Popcnt(INT x) {
|
||||
uint128_t word = x;
|
||||
return popcnt(word >> 64) + popcnt(word);
|
||||
}
|
||||
|
||||
char *Repr(struct Value x) {
|
||||
static char buf[64];
|
||||
if (x.t == kFloat) {
|
||||
g_xfmt_p(buf, &x.f, 16, sizeof(buf), 0);
|
||||
} else {
|
||||
sprintf(buf, "%jjd", x.i);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *ReprStack(void) {
|
||||
int i, j, l;
|
||||
char *s, *p;
|
||||
static char buf[80];
|
||||
p = memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; i < sp; ++i) {
|
||||
s = Repr(stack[i]);
|
||||
l = strlen(s);
|
||||
if (p + l + 2 > buf + sizeof(buf)) break;
|
||||
p = mempcpy(p, s, l);
|
||||
*p++ = ' ';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void ShowStack(void) {
|
||||
if (interactive) {
|
||||
printf("\r\e[K");
|
||||
fputs(ReprStack(), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void Cr(FILE *f) {
|
||||
fputs(interactive || IsWindows() ? "\r\n" : "\n", f);
|
||||
}
|
||||
|
||||
void Log(enum Severity l, const char *fmt, ...) {
|
||||
va_list va;
|
||||
const char *severity[] = {"FATAL", "WARNING"};
|
||||
if (interactive) fflush(/* key triggering throw not echo'd yet */ stdout);
|
||||
fprintf(stderr, "%s:%d:%d:%s: ", file, line + 1, column, severity[l & 1]);
|
||||
va_start(va, fmt);
|
||||
vfprintf(stderr, fmt, va);
|
||||
va_end(va);
|
||||
Cr(stderr);
|
||||
if (l == kFatal) exit(EXIT_FAILURE);
|
||||
if (interactive) ShowStack();
|
||||
}
|
||||
|
||||
void __on_arithmetic_overflow(void) {
|
||||
Warning("arithmetic overflow");
|
||||
}
|
||||
|
||||
void OnDivideError(void) {
|
||||
longjmp(thrower, kDivideError);
|
||||
}
|
||||
|
||||
struct Value Push(struct Value x) {
|
||||
if (sp >= ARRAYLEN(stack)) Fatal("stack overflow");
|
||||
return (stack[sp++] = x);
|
||||
}
|
||||
|
||||
struct Value Pop(void) {
|
||||
if (sp) {
|
||||
return stack[--sp];
|
||||
} else {
|
||||
longjmp(thrower, kUnderflow);
|
||||
}
|
||||
}
|
||||
|
||||
INT Popi(void) {
|
||||
struct Value x = Pop();
|
||||
return x.t == kInt ? x.i : x.f;
|
||||
}
|
||||
|
||||
void Pushi(INT i) {
|
||||
struct Value x;
|
||||
x.t = kInt;
|
||||
x.i = i;
|
||||
Push(x);
|
||||
}
|
||||
|
||||
FLOAT Popf(void) {
|
||||
struct Value x = Pop();
|
||||
return x.t == kInt ? x.i : x.f;
|
||||
}
|
||||
|
||||
void Pushf(FLOAT f) {
|
||||
struct Value x;
|
||||
x.t = kFloat;
|
||||
x.f = f;
|
||||
Push(x);
|
||||
}
|
||||
|
||||
void OpDrop(void) {
|
||||
Pop();
|
||||
}
|
||||
|
||||
void OpDup(void) {
|
||||
Push(Push(Pop()));
|
||||
}
|
||||
|
||||
void OpExit(void) {
|
||||
exit(Popi());
|
||||
}
|
||||
|
||||
void OpSrand(void) {
|
||||
srand(Popi());
|
||||
}
|
||||
|
||||
void OpEmit(void) {
|
||||
fputwc(Popi(), stdout);
|
||||
}
|
||||
|
||||
void OpCr(void) {
|
||||
Cr(stdout);
|
||||
}
|
||||
|
||||
void OpPrint(void) {
|
||||
printf("%s ", Repr(Pop()));
|
||||
}
|
||||
|
||||
void OpComment(void) {
|
||||
comment = true;
|
||||
}
|
||||
|
||||
void Glue0f(FLOAT fn(void)) {
|
||||
Pushf(fn());
|
||||
}
|
||||
|
||||
void Glue0i(INT fn(void)) {
|
||||
Pushi(fn());
|
||||
}
|
||||
|
||||
void Glue1f(FLOAT fn(FLOAT)) {
|
||||
Pushf(fn(Popf()));
|
||||
}
|
||||
|
||||
void Glue1i(INT fn(INT)) {
|
||||
Pushi(fn(Popi()));
|
||||
}
|
||||
|
||||
void OpSwap(void) {
|
||||
struct Value a, b;
|
||||
b = Pop();
|
||||
a = Pop();
|
||||
Push(b);
|
||||
Push(a);
|
||||
}
|
||||
|
||||
void OpOver(void) {
|
||||
struct Value a, b;
|
||||
b = Pop();
|
||||
a = Pop();
|
||||
Push(a);
|
||||
Push(b);
|
||||
Push(a);
|
||||
}
|
||||
|
||||
void OpKey(void) {
|
||||
wint_t c;
|
||||
ttyraw(kTtyCursor | kTtySigs | kTtyLfToCrLf);
|
||||
c = fgetwc(stdin);
|
||||
ttyraw(-1);
|
||||
if (c != -1) Pushi(c);
|
||||
}
|
||||
|
||||
void OpAssert(void) {
|
||||
if (!Popi()) Fatal("assert failed");
|
||||
}
|
||||
|
||||
void OpExpect(void) {
|
||||
if (!Popi()) Warning("expect failed");
|
||||
}
|
||||
|
||||
void OpMeminfo(void) {
|
||||
OpCr();
|
||||
OpCr();
|
||||
fflush(stdout);
|
||||
_meminfo(fileno(stdout));
|
||||
}
|
||||
|
||||
void Glue2f(FLOAT fn(FLOAT, FLOAT)) {
|
||||
FLOAT x, y;
|
||||
y = Popf();
|
||||
x = Popf();
|
||||
Pushf(fn(x, y));
|
||||
}
|
||||
|
||||
void Glue2i(INT fn(INT, INT)) {
|
||||
INT x, y;
|
||||
y = Popi();
|
||||
x = Popi();
|
||||
Pushi(fn(x, y));
|
||||
}
|
||||
|
||||
void Glue1g(FLOAT fnf(FLOAT), INT fni(INT)) {
|
||||
struct Value x;
|
||||
x = Pop();
|
||||
switch (x.t) {
|
||||
case kInt:
|
||||
Pushi(fni(x.i));
|
||||
break;
|
||||
case kFloat:
|
||||
Pushf(fnf(x.f));
|
||||
break;
|
||||
default:
|
||||
Warning("type mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
void Glue2g(FLOAT fnf(FLOAT, FLOAT), INT fni(INT, INT)) {
|
||||
struct Value x, y;
|
||||
y = Pop();
|
||||
x = Pop();
|
||||
if (x.t == kInt && y.t == kInt) {
|
||||
Pushi(fni(x.i, y.i));
|
||||
} else if (x.t == kFloat && y.t == kFloat) {
|
||||
Pushf(fnf(x.f, y.f));
|
||||
} else if (x.t == kInt && y.t == kFloat) {
|
||||
Pushf(fnf(x.i, y.f));
|
||||
} else if (x.t == kFloat && y.t == kInt) {
|
||||
Pushf(fnf(x.f, y.i));
|
||||
} else {
|
||||
Warning("type mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
#define LB {
|
||||
#define RB }
|
||||
#define SEMI ;
|
||||
#define FNTYPEi INT
|
||||
#define FNTYPEf FLOAT
|
||||
#define FORM(F) LB F SEMI RB
|
||||
#define FNPL0(T) void
|
||||
#define FNPL1(T) FNTYPE##T x
|
||||
#define FNPL2(T) FNTYPE##T x, FNTYPE##T y
|
||||
#define FNDEF(A, T, S, C) FNTYPE##T Fn##S##T(FNPL##A(T)) FORM(return C)
|
||||
#define FNDEFf(A, S, C) FNDEF(A, f, S, C)
|
||||
#define FNDEFi(A, S, C) FNDEF(A, i, S, C)
|
||||
#define FNDEFg(A, S, C) FNDEF(A, f, S, C) FNDEF(A, i, S, C)
|
||||
#define OPDEF(A, T, S, C) void Op##S(void) FORM(Glue##A##T(Fn##S##T))
|
||||
#define OPDEFf(A, S, C) OPDEF(A, f, S, C)
|
||||
#define OPDEFi(A, S, C) OPDEF(A, i, S, C)
|
||||
#define OPDEFg(A, S, C) void Op##S(void) FORM(Glue##A##g(Fn##S##f, Fn##S##i))
|
||||
#define M(A, T, N, S, C, D) FNDEF##T(A, S, C) OPDEF##T(A, S, C)
|
||||
#include "tool/build/calculator.inc"
|
||||
#undef M
|
||||
|
||||
const struct Function kFunctions[] = {
|
||||
{".", OpPrint, "pops prints value repr"},
|
||||
{"#", OpComment, "line comment"},
|
||||
#define M(A, T, N, S, C, D) {N, Op##S, D},
|
||||
#include "tool/build/calculator.inc"
|
||||
#undef M
|
||||
{"dup", OpDup, "pushes copy of last item on stack"},
|
||||
{"drop", OpDrop, "pops and discards"},
|
||||
{"swap", OpSwap, "swaps last two items on stack"},
|
||||
{"over", OpOver, "pushes second item on stack"},
|
||||
{"cr", OpCr, "prints newline"},
|
||||
{"key", OpKey, "reads and pushes unicode character from keyboard"},
|
||||
{"emit", OpEmit, "pops and writes unicode character to output"},
|
||||
{"assert", OpAssert, "crashes if top of stack isn't nonzero"},
|
||||
{"expect", OpExpect, "prints warning if top of stack isn't nonzero"},
|
||||
{"meminfo", OpMeminfo, "prints memory mappings"},
|
||||
{"exit", OpExit, "exits program with status"},
|
||||
};
|
||||
|
||||
bool CallFunction(const char *sym) {
|
||||
int i;
|
||||
char s[16];
|
||||
strncpy(s, sym, sizeof(s));
|
||||
for (i = 0; i < ARRAYLEN(kFunctions); ++i) {
|
||||
if (memcmp(kFunctions[i].sym, s, sizeof(s)) == 0) {
|
||||
kFunctions[i].fun();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
volatile const char *literal_;
|
||||
bool ConsumeLiteral(const char *literal) {
|
||||
bool r;
|
||||
char *e;
|
||||
struct Value x;
|
||||
literal_ = literal;
|
||||
errno = 0;
|
||||
x.t = kInt;
|
||||
x.i = strtoi128(literal, &e, 0);
|
||||
if (*e) {
|
||||
x.t = kFloat;
|
||||
x.f = strtod(literal, &e);
|
||||
r = !(!e || *e);
|
||||
} else if (errno == ERANGE) {
|
||||
r = false;
|
||||
} else {
|
||||
r = true;
|
||||
}
|
||||
Push(x);
|
||||
return r;
|
||||
}
|
||||
|
||||
void ConsumeToken(void) {
|
||||
enum Exception ex;
|
||||
if (!token.i) return;
|
||||
token.p[token.i] = 0;
|
||||
token.i = 0;
|
||||
if (history.i) history.p[history.i - 1].i = 0;
|
||||
if (comment) return;
|
||||
if (_startswith(token.p, "#!")) return;
|
||||
switch (setjmp(thrower)) {
|
||||
default:
|
||||
if (CallFunction(token.p)) return;
|
||||
if (ConsumeLiteral(token.p)) return;
|
||||
Warning("bad token: %`'s", token.p);
|
||||
break;
|
||||
case kUnderflow:
|
||||
Warning("stack underflow");
|
||||
break;
|
||||
case kDivideError:
|
||||
Warning("divide error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CleanupRepl(void) {
|
||||
if (sp && interactive) {
|
||||
Cr(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendByte(struct Bytes *a, char b) {
|
||||
APPEND(&a->p, &a->i, &a->n, &b);
|
||||
}
|
||||
|
||||
void AppendHistory(void) {
|
||||
struct Bytes line;
|
||||
if (interactive) {
|
||||
bzero(&line, sizeof(line));
|
||||
APPEND(&history.p, &history.i, &history.n, &line);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendLine(char b) {
|
||||
if (interactive) {
|
||||
if (!history.i) AppendHistory();
|
||||
AppendByte(&history.p[history.i - 1], b);
|
||||
}
|
||||
}
|
||||
|
||||
void Echo(int c) {
|
||||
if (interactive) {
|
||||
fputc(c, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void Backspace(int c) {
|
||||
struct Bytes *line;
|
||||
if (interactive) {
|
||||
if (history.i) {
|
||||
line = &history.p[history.i - 1];
|
||||
if (line->i && token.i) {
|
||||
do {
|
||||
line->i--;
|
||||
token.i--;
|
||||
} while (line->i && token.i && (line->p[line->i] & 0x80));
|
||||
fputs("\e[D \e[D", stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NewLine(void) {
|
||||
line++;
|
||||
column = 0;
|
||||
comment = false;
|
||||
if (interactive) {
|
||||
Cr(stdout);
|
||||
AppendHistory();
|
||||
ShowStack();
|
||||
}
|
||||
}
|
||||
|
||||
void KillLine(void) {
|
||||
if (interactive) {
|
||||
if (history.i) {
|
||||
history.p[history.i - 1].i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearLine(void) {
|
||||
if (interactive) {
|
||||
if (token.i) sp = 0;
|
||||
ShowStack();
|
||||
}
|
||||
}
|
||||
|
||||
void RedrawDisplay(void) {
|
||||
int i;
|
||||
struct Bytes *line;
|
||||
if (interactive) {
|
||||
printf("\e[H\e[2J%s", BANNER);
|
||||
ShowStack();
|
||||
if (history.i) {
|
||||
line = &history.p[history.i - 1];
|
||||
for (i = 0; i < line->i; ++i) {
|
||||
fputc(line->p[i], stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GotoStartOfLine(void) {
|
||||
if (interactive) {
|
||||
printf("\r");
|
||||
}
|
||||
}
|
||||
|
||||
void GotoEndOfLine(void) {
|
||||
}
|
||||
|
||||
void GotoPrevLine(void) {
|
||||
}
|
||||
|
||||
void GotoNextLine(void) {
|
||||
}
|
||||
|
||||
void GotoPrevChar(void) {
|
||||
}
|
||||
|
||||
void GotoNextChar(void) {
|
||||
}
|
||||
|
||||
void Calculator(FILE *f) {
|
||||
int c;
|
||||
while (!feof(f)) {
|
||||
switch ((c = getc(f))) {
|
||||
case -1:
|
||||
case CTRL('D'):
|
||||
goto Done;
|
||||
case CTRL('@'):
|
||||
break;
|
||||
case ' ':
|
||||
column++;
|
||||
Echo(c);
|
||||
AppendLine(c);
|
||||
ConsumeToken();
|
||||
break;
|
||||
case '\t':
|
||||
column = ROUNDUP(column, 8);
|
||||
Echo(c);
|
||||
AppendLine(c);
|
||||
ConsumeToken();
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
ConsumeToken();
|
||||
NewLine();
|
||||
break;
|
||||
case CTRL('A'):
|
||||
GotoStartOfLine();
|
||||
break;
|
||||
case CTRL('E'):
|
||||
GotoEndOfLine();
|
||||
break;
|
||||
case CTRL('P'):
|
||||
GotoPrevLine();
|
||||
break;
|
||||
case CTRL('N'):
|
||||
GotoNextLine();
|
||||
break;
|
||||
case CTRL('B'):
|
||||
GotoPrevChar();
|
||||
break;
|
||||
case CTRL('F'):
|
||||
GotoNextChar();
|
||||
break;
|
||||
case CTRL('L'):
|
||||
RedrawDisplay();
|
||||
break;
|
||||
case CTRL('U'):
|
||||
ClearLine();
|
||||
break;
|
||||
case CTRL('K'):
|
||||
KillLine();
|
||||
break;
|
||||
case CTRL('?'):
|
||||
case CTRL('H'):
|
||||
Backspace(c);
|
||||
break;
|
||||
default:
|
||||
column++;
|
||||
/* fallthrough */
|
||||
case 0x80 ... 0xff:
|
||||
Echo(c);
|
||||
AppendLine(c);
|
||||
AppendByte(&token, c);
|
||||
break;
|
||||
}
|
||||
fflush(/* needed b/c this is event loop */ stdout);
|
||||
}
|
||||
Done:
|
||||
CleanupRepl();
|
||||
}
|
||||
|
||||
int Calculate(const char *path) {
|
||||
FILE *f;
|
||||
file = path;
|
||||
line = column = 0;
|
||||
if (!(f = fopen(file, "r"))) Fatal("file not found: %s", file);
|
||||
Calculator(f);
|
||||
return fclose(f);
|
||||
}
|
||||
|
||||
void CleanupTerminal(void) {
|
||||
ttyraw(-1);
|
||||
}
|
||||
|
||||
void StartInteractive(void) {
|
||||
if (!interactive && !__nocolor && isatty(fileno(stdin)) &&
|
||||
isatty(fileno(stdout)) && !__nocolor) {
|
||||
interactive = true;
|
||||
}
|
||||
errno = 0;
|
||||
if (interactive) {
|
||||
fputs(BANNER, stdout);
|
||||
fflush(/* needed b/c entering tty mode */ stdout);
|
||||
ttyraw(kTtyCursor | kTtySigs);
|
||||
atexit(CleanupTerminal);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintUsage(int rc, FILE *f) {
|
||||
char c;
|
||||
int i, j;
|
||||
fprintf(f, "%s %s%s", USAGE1, program_invocation_name, USAGE2);
|
||||
for (i = 0; i < ARRAYLEN(kFunctions); ++i) {
|
||||
fputs(" ", f);
|
||||
for (j = 0; j < ARRAYLEN(kFunctions[i].sym); ++j) {
|
||||
c = kFunctions[i].sym[j];
|
||||
fputc(c ? c : ' ', f);
|
||||
}
|
||||
fprintf(f, " %s\n", kFunctions[i].doc);
|
||||
}
|
||||
fputs(USAGE3, f);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "?hi")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
interactive = true;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
ShowCrashReports();
|
||||
GetOpts(argc, argv);
|
||||
xsigaction(SIGFPE, OnDivideError, 0, 0, 0);
|
||||
if (optind == argc) {
|
||||
file = "/dev/stdin";
|
||||
StartInteractive();
|
||||
Calculator(stdin);
|
||||
return fclose(stdin);
|
||||
} else {
|
||||
for (i = optind; i < argc; ++i) {
|
||||
if (Calculate(argv[i]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
# INTEGER
|
||||
2 3 + 5 = assert
|
||||
3 2 + 5 = assert
|
||||
5 2 - 3 = assert
|
||||
2 5 - -3 = assert
|
||||
81 3 / 27 = assert
|
||||
81 3 // 27 = assert
|
||||
2 8 ** 256 = assert
|
||||
17 10 % 7 = assert
|
||||
17 10 fmod 7 = assert
|
||||
|
||||
# FLOATING POINT
|
||||
.1 .2 + .3 - abs epsilon < assert
|
||||
pi sqrt pi sqrt * pi - abs epsilon < assert
|
||||
3 2 / 1.5 = assert
|
||||
pi pi = assert
|
||||
pi cos -1 = assert
|
||||
pi 2 / sin 1 = assert
|
||||
81 3 / 27 = assert
|
||||
inf isinf assert
|
||||
inf isnormal ! assert
|
||||
nan isnormal ! assert
|
||||
1 0 / isnormal ! assert
|
||||
0 signbit ! assert
|
||||
-.5 round -1 = assert
|
||||
-.5 floor -1 = assert
|
||||
-.5 rint dup 0 = assert signbit assert
|
||||
-.5 nearbyint dup 0 = assert signbit assert
|
||||
-.5 ceil dup 0 = assert signbit assert
|
||||
-.5 trunc dup 0 = assert signbit assert
|
||||
0 0 / dup isnan assert signbit assert # is this right?
|
||||
1 0 / dup isinf assert signbit ! assert # is this right?
|
||||
nan nan != assert # is this right?
|
||||
# -nan -nan != assert # is this right?
|
||||
inf inf = assert # is this right?
|
||||
-inf -inf = assert # is this right?
|
||||
|
||||
# BIT ARITHMETIC
|
||||
-1 ~ 0 = assert
|
||||
0 0x7fffffffffffffffffffffffffffffff - -0x7fffffffffffffffffffffffffffffff = assert
|
||||
0b1010101 popcnt 4 = assert
|
||||
0b1010101 0b0110101 ^ 0b1100000 = assert
|
||||
0b1010101 0b0110101 | 0b1110101 = assert
|
||||
0b1010101 0b0110101 & 0b0010101 = assert
|
||||
0b1010101 1 >> 0b000101010 = assert
|
||||
0b1010101 2 >> 0b000010101 = assert
|
||||
0b1010101 1 << 0b010101010 = assert
|
||||
0b1010101 2 << 0b101010100 = assert
|
||||
|
||||
# BOOLEAN
|
||||
true assert
|
||||
false ! assert
|
||||
true ! ! assert
|
||||
true true && assert
|
||||
true false && ! assert
|
||||
false true && ! assert
|
||||
true false && ! assert
|
||||
false true && ! assert
|
||||
true true || assert
|
||||
false true || assert
|
||||
true false || assert
|
||||
false false || ! assert
|
||||
4 5 < assert
|
||||
5 4 < ! assert
|
||||
-5 4 < assert
|
||||
5 5 < ! assert
|
||||
5 5 <= assert
|
||||
5 4 > assert
|
||||
4 5 > ! assert
|
||||
4 -5 > assert
|
||||
5 5 > ! assert
|
||||
5 5 >= assert
|
||||
|
||||
# MISC
|
||||
1 abs 1 = assert
|
||||
-1 abs 1 = assert
|
||||
-1 1 max 1 = assert
|
||||
1 -1 max 1 = assert
|
||||
1 2 max 2 = assert
|
||||
-1 1 min -1 = assert
|
||||
1 -1 min -1 = assert
|
||||
1 2 min 1 = assert
|
||||
rand64 rand64 rand64 rand64 != != && assert
|
||||
|
||||
# HEX SIGN
|
||||
-0x80000000 -2147483648 = assert
|
||||
0x80000000 2147483648 = assert
|
||||
0x80000001 2147483649 = assert
|
||||
0xffffffff 4294967295 = assert
|
||||
0x100000000 4294967296 = assert
|
||||
-0x100000000 -4294967296 = assert
|
|
@ -1,100 +0,0 @@
|
|||
M(2, g, "+", Add, x + y, "add")
|
||||
M(2, g, "-", Sub, x - y, "sub")
|
||||
M(2, g, "*", Mul, x *y, "multiply")
|
||||
M(2, f, "/", Div, x / y, "division")
|
||||
M(2, i, "%", Rem, x % y, "integer remainder")
|
||||
M(2, i, "//", Idiv, x / y, "integer division")
|
||||
M(2, g, "**", Expo, powl(x, y), "exponentiation")
|
||||
|
||||
M(1, i, "~", Not, ~x, "bitwise not")
|
||||
M(2, i, "^", Xor, x ^ y, "bitwise xor")
|
||||
M(2, i, "|", Or, x | y, "bitwise or")
|
||||
M(2, i, "&", And, x &y, "bitwise and")
|
||||
M(2, i, ">>", Shr, x >> y, "shift right")
|
||||
M(2, i, "<<", Shl, x << y, "shift left")
|
||||
|
||||
M(1, i, "!", LogicalNot, !x, "logical not")
|
||||
M(2, i, "||", LogicalOr, x || y, "logical or")
|
||||
M(2, i, "&&", LogicalAnd, x &&y, "logical and")
|
||||
|
||||
M(2, g, "=", Equal1, x == y, "equal to")
|
||||
M(2, g, "!=", Notequal, x != y, "not equal to")
|
||||
M(2, g, "<", LessThan, x < y, "less than")
|
||||
M(2, g, ">", GreaterThan, x > y, "greater than")
|
||||
M(2, g, "<=", LessThanEqual, x <= y, "less than or equal to")
|
||||
M(2, g, ">=", GreaterThanEqual, x >= y, "greater than or equal to")
|
||||
|
||||
M(1, i, "gray", Gray, gray(x), "gray coding")
|
||||
M(1, i, "ungray", Ungray, ungray(x), "inverse gray coding")
|
||||
M(1, i, "popcnt", Popcnt, Popcnt(x), "count bits")
|
||||
|
||||
M(1, g, "abs", Abs, fabsl(x), "absolute value")
|
||||
M(2, g, "min", Min, fminl(x, y), "pops two values and pushes minimum")
|
||||
M(2, g, "max", Max, fmaxl(x, y), "pops two values and pushes maximum")
|
||||
|
||||
M(2, g, "fmod", Fmod, fmodl(x, y), "trunc remainder")
|
||||
M(2, g, "emod", Emod, emodl(x, y), "euclidean remainder")
|
||||
M(2, g, "remainder", Remainder, remainderl(x, y), "rint remainder")
|
||||
M(2, g, "hypot", Hypot, hypotl(x, y), "euclidean distance")
|
||||
|
||||
M(0, i, "false", False, 0, "0")
|
||||
M(0, i, "true", True, 1, "1")
|
||||
M(0, i, "intmin", IntMin, INT128_MIN, "native integer minimum")
|
||||
M(0, i, "intmax", IntMax, INT128_MAX, "native integer maximum")
|
||||
M(0, f, "e", Euler, M_E, "𝑒")
|
||||
M(0, f, "pi", Fldpi, M_PI, "π")
|
||||
M(0, f, "epsilon", Epsilon, EPSILON, "ɛ")
|
||||
M(0, f, "inf", Inf, INFINITY, "∞")
|
||||
M(0, f, "nan", Nan, NAN, "NAN")
|
||||
M(0, f, "-0", Negzero, -0., "wut")
|
||||
M(0, f, "l2t", Fldl2t, M_LOG2_10, "log₂10")
|
||||
M(0, f, "lg2", Fldlg2, M_LOG10_2, "log₁₀2")
|
||||
M(0, f, "ln2", Fldln2, M_LN2, "logₑ2")
|
||||
M(0, f, "l2e", Fldl2e, M_LOG2E, "logₑ10")
|
||||
M(2, f, "nextafter", Nextafter, nextafterl(x, y), "next ulp")
|
||||
M(1, f, "significand", Significand, significandl(x), "mantissa")
|
||||
|
||||
M(1, f, "sqrt", Sqrt, sqrtl(x), "√𝑥")
|
||||
M(1, f, "exp", Exp, expl(x), "𝑒ˣ")
|
||||
M(1, g, "expm1", Expm1, expm1l(x), "𝑒ˣ-1")
|
||||
M(1, g, "exp2", Exp2, exp2l(x), "2ˣ")
|
||||
M(1, g, "exp10", Exp10, exp10l(x), "10ˣ")
|
||||
M(2, g, "ldexp", Ldexp, ldexpl(x, y), "𝑥×2ʸ")
|
||||
|
||||
M(1, f, "log", Log, logl(x), "logₑ𝑥")
|
||||
M(1, g, "log2", Log2, log2l(x), "log₂𝑥")
|
||||
M(1, g, "log10", Log10, log10l(x), "log₁₀𝑥")
|
||||
M(1, g, "ilogb", Ilogb, ilogbl(x), "exponent")
|
||||
|
||||
M(1, g, "sin", Sin, sinl(x), "sine")
|
||||
M(1, g, "cos", Cos, cosl(x), "cosine")
|
||||
M(1, g, "tan", Tan, tanl(x), "tangent")
|
||||
M(1, g, "asin", Asin, asinl(x), "arcsine")
|
||||
M(1, g, "acos", Acos, acosl(x), "arccosine")
|
||||
M(1, g, "atan", Atan, atanl(x), "arctangent")
|
||||
M(2, g, "atan2", Atan2, atan2l(x, y), "arctangent of 𝑥/𝑦")
|
||||
|
||||
M(1, g, "sinh", Sinh, sinhl(x), "hyperbolic sine")
|
||||
M(1, g, "cosh", Cosh, coshl(x), "hyperbolic cosine")
|
||||
M(1, g, "tanh", Tanh, tanhl(x), "hyperbolic tangent")
|
||||
M(1, g, "asinh", Asinh, asinhl(x), "hyperbolic arcsine")
|
||||
M(1, g, "acosh", Acosh, acoshl(x), "hyperbolic arccosine")
|
||||
M(1, g, "atanh", Atanh, atanhl(x), "hyperbolic arctangent")
|
||||
|
||||
M(1, g, "round", Round, roundl(x), "round away from zero")
|
||||
M(1, g, "trunc", Trunc, truncl(x), "round towards zero")
|
||||
M(1, g, "rint", Rint, rintl(x), "round to even")
|
||||
M(1, g, "nearbyint", Nearbyint, nearbyintl(x), "round to nearest integer")
|
||||
M(1, g, "ceil", Ceil, ceill(x), "smallest integral not less than 𝑥")
|
||||
M(1, g, "floor", Floor, floorl(x), "largest integral not greater than 𝑥")
|
||||
|
||||
M(1, f, "isnan", Isnan, isnan(x), "returns true if 𝑥=NAN")
|
||||
M(1, f, "isinf", Isinf, isinf(x), "returns true if 𝑥=INFINITY")
|
||||
M(1, f, "signbit", Signbit, signbit(x), "clears all bits but sign bit")
|
||||
M(1, f, "isfinite", Isfinite, isfinite(x), "returns true if 𝑥≠INFINITY")
|
||||
M(1, f, "isnormal", Isnormal, isnormal(x), "returns true if not denormal")
|
||||
M(1, f, "fpclassify", Fpclassify, fpclassify(x),
|
||||
"nan=0,inf=1,zero=2,subnorm=3,normal=4")
|
||||
|
||||
M(0, i, "rand", Rand, rand(), "deterministic random number")
|
||||
M(0, i, "rand64", _Rand64, _rand64(), "64-bit random number")
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
|
@ -33,42 +34,21 @@ SYNOPSIS\n\
|
|||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-?\n\
|
||||
-h help\n\
|
||||
\n"
|
||||
|
||||
const char *prog;
|
||||
|
||||
nullterminated() static void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static wontreturn void SysExit(const char *path, const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int rc) {
|
||||
Print(fd, "USAGE\n\n ", program_invocation_name, USAGE, NULL);
|
||||
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "?h")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(1, 0);
|
||||
default:
|
||||
PrintUsage(2, 1);
|
||||
|
@ -79,18 +59,22 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
int main(int argc, char *argv[]) {
|
||||
int i, mode;
|
||||
char buf[PATH_MAX], *endptr;
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "chmod";
|
||||
GetOpts(argc, argv);
|
||||
if (argc - optind < 2) {
|
||||
PrintUsage(2, 1);
|
||||
tinyprint(2, prog, ": missing operand\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
mode = strtol(argv[optind], &endptr, 8) & 07777;
|
||||
if (*endptr) {
|
||||
Print(2, "chmod: invalid mode octal\n", NULL);
|
||||
tinyprint(2, prog, ": invalid mode octal\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
for (i = optind + 1; i < argc; ++i) {
|
||||
if (chmod(argv[i], mode) == -1) {
|
||||
SysExit(argv[i], "chmod");
|
||||
perror(argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
@ -91,16 +92,14 @@ bool IsSymlink(const char *path) {
|
|||
return res;
|
||||
}
|
||||
|
||||
wontreturn void PrintUsage(int rc, FILE *f) {
|
||||
fputs("usage: ", f);
|
||||
fputs(prog, f);
|
||||
fputs(USAGE, f);
|
||||
wontreturn void PrintUsage(int rc, int fd) {
|
||||
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "?hfnaprR")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hfnaprR")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
force = true;
|
||||
|
@ -118,10 +117,9 @@ void GetOpts(int argc, char *argv[]) {
|
|||
flags |= COPYFILE_PRESERVE_TIMESTAMPS;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
PrintUsage(0, 1);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
PrintUsage(1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,8 +144,7 @@ int Visit(const char *fpath, const struct stat *sb, int tflag,
|
|||
Cp(srcfile, dstfile);
|
||||
return 0;
|
||||
default:
|
||||
fputs(fpath, stderr);
|
||||
fputs(": can't handle file type\n", stderr);
|
||||
tinyprint(2, fpath, ": bad file type\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +154,7 @@ char *Join(const char *a, const char *b) {
|
|||
n = strlen(a);
|
||||
m = strlen(b);
|
||||
if (n + 1 + m + 1 > sizeof(dstfile)) {
|
||||
fputs("error: cp: path too long\n", stderr);
|
||||
tinyprint(2, prog, ": path too long\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
stpcpy(stpcpy(stpcpy(dstfile, a), "/"), b);
|
||||
|
@ -191,8 +188,7 @@ void Cp(char *src, char *dst) {
|
|||
basename(dst);
|
||||
if (IsDirectory(src)) {
|
||||
if (!recursive) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": won't copy directory without -r flag.\n", stderr);
|
||||
tinyprint(2, prog, ": won't copy directory without -r flag\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(dstdir, dst);
|
||||
|
@ -208,10 +204,7 @@ void Cp(char *src, char *dst) {
|
|||
strcpy(srcdir, "");
|
||||
}
|
||||
if (nftw(src, Visit, 20, 0) == -1) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": nftw failed: ", stderr);
|
||||
fputs(_strerdoc(errno), stderr);
|
||||
fputs("\n", stderr);
|
||||
perror(src);
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
|
@ -219,37 +212,49 @@ void Cp(char *src, char *dst) {
|
|||
if (IsDirectory(dst)) {
|
||||
dst = Join(dst, basename(src));
|
||||
}
|
||||
if (!force && access(dst, W_OK) == -1 && errno != ENOENT) goto OnFail;
|
||||
strcpy(mkbuf, dst);
|
||||
if (makedirs(dirname(mkbuf), 0755) == -1) goto OnFail;
|
||||
if (IsSymlink(src)) {
|
||||
if ((rc = readlink(src, linkbuf, sizeof(linkbuf) - 1)) == -1) goto OnFail;
|
||||
linkbuf[rc] = 0;
|
||||
if (symlink(linkbuf, dst) == -1) goto OnFail;
|
||||
} else {
|
||||
if (!MovePreservingDestinationInode(src, dst)) goto OnFail;
|
||||
if (!force && access(dst, W_OK) == -1 && errno != ENOENT) {
|
||||
perror(dst);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(mkbuf, dst);
|
||||
if (makedirs((s = dirname(mkbuf)), 0755) == -1) {
|
||||
perror(s);
|
||||
exit(1);
|
||||
}
|
||||
if (IsSymlink(src)) {
|
||||
if ((rc = readlink(src, linkbuf, sizeof(linkbuf) - 1)) == -1) {
|
||||
perror(src);
|
||||
exit(1);
|
||||
}
|
||||
linkbuf[rc] = 0;
|
||||
if (symlink(linkbuf, dst) == -1) {
|
||||
perror(dst);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (!MovePreservingDestinationInode(src, dst)) {
|
||||
perror(src);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
OnFail:
|
||||
s = _strerdoc(errno);
|
||||
fputs(prog, stderr);
|
||||
fputs(": ", stderr);
|
||||
fputs(src, stderr);
|
||||
fputs(" ", stderr);
|
||||
fputs(dst, stderr);
|
||||
fputs(": ", stderr);
|
||||
fputs(s, stderr);
|
||||
fputs("\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
prog = argc > 0 ? argv[0] : "cp.com";
|
||||
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "cp";
|
||||
|
||||
GetOpts(argc, argv);
|
||||
if (argc - optind < 2) PrintUsage(EX_USAGE, stderr);
|
||||
|
||||
if (argc - optind < 2) {
|
||||
tinyprint(2, prog, ": missing operand\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = optind; i < argc - 1; ++i) {
|
||||
Cp(argv[i], argv[argc - 1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -73,32 +73,20 @@ static const char *epath;
|
|||
static Elf64_Xword symcount;
|
||||
static const Elf64_Ehdr *elf;
|
||||
|
||||
nullterminated() static void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static wontreturn void Die(const char *reason) {
|
||||
Print(2, epath, ": ", reason, "\n", NULL);
|
||||
tinyprint(2, epath, ": ", reason, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void SysExit(const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, epath, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
tinyprint(2, epath, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int exitcode) {
|
||||
Print(fd, "\n\
|
||||
tinyprint(fd, "\n\
|
||||
NAME\n\
|
||||
\n\
|
||||
Cosmopolitan Object Fixer\n\
|
||||
|
@ -106,7 +94,7 @@ NAME\n\
|
|||
SYNOPSIS\n\
|
||||
\n\
|
||||
",
|
||||
program_invocation_name, " [FLAGS] OBJECT...\n\
|
||||
program_invocation_name, " [FLAGS] OBJECT...\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
|
@ -124,7 +112,7 @@ FLAGS\n\
|
|||
-c checks only mode\n\
|
||||
\n\
|
||||
",
|
||||
NULL);
|
||||
NULL);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
|
@ -143,10 +131,10 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
Print(2,
|
||||
"error: no elf object files specified\n"
|
||||
"run ",
|
||||
program_invocation_name, " -h for usage\n", NULL);
|
||||
tinyprint(2,
|
||||
"error: no elf object files specified\n"
|
||||
"run ",
|
||||
program_invocation_name, " -h for usage\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -184,11 +172,11 @@ static void CheckPrivilegedCrossReferences(void) {
|
|||
if (~shdr->sh_flags & SHF_EXECINSTR) continue; // data reference
|
||||
if ((secname = GetElfString(elf, esize, secstrs, shdr->sh_name)) &&
|
||||
strcmp(".privileged", secname)) {
|
||||
Print(2, epath,
|
||||
": code in .privileged section "
|
||||
"references symbol '",
|
||||
GetElfString(elf, esize, symstrs, syms[x].st_name),
|
||||
"' in unprivileged code section '", secname, "'\n", NULL);
|
||||
tinyprint(2, epath,
|
||||
": code in .privileged section "
|
||||
"references symbol '",
|
||||
GetElfString(elf, esize, symstrs, syms[x].st_name),
|
||||
"' in unprivileged code section '", secname, "'\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,7 +289,8 @@ void Decompress(const char *inpath) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
prog = argc > 0 ? argv[0] : "cp.com";
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "gzip";
|
||||
GetOpts(argc, argv);
|
||||
if (opt_decompress) {
|
||||
if (optind == argc) {
|
||||
|
|
|
@ -52,7 +52,7 @@ void PrintUsage(int rc, FILE *f) {
|
|||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
bits_ = 64;
|
||||
while ((opt = getopt(argc, argv, "?hbs")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hbs")) != -1) {
|
||||
switch (opt) {
|
||||
case 's':
|
||||
succinct_ = true;
|
||||
|
@ -60,7 +60,6 @@ void GetOpts(int argc, char *argv[]) {
|
|||
case 'b':
|
||||
bits_ = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
|
|
|
@ -8,14 +8,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
#define USAGE \
|
||||
|
@ -30,19 +25,19 @@ FLAGS\n\
|
|||
|
||||
const char *prog;
|
||||
|
||||
wontreturn void PrintUsage(int rc, FILE *f) {
|
||||
fputs("Usage: ", f);
|
||||
fputs(prog, f);
|
||||
fputs(USAGE, f);
|
||||
wontreturn void PrintUsage(int rc, int fd) {
|
||||
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, mode = 0755;
|
||||
int (*mkdirp)(const char *, unsigned) = mkdir;
|
||||
prog = argc > 0 ? argv[0] : "mkdir.com";
|
||||
|
||||
while ((i = getopt(argc, argv, "?hpm:")) != -1) {
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "mkdir";
|
||||
|
||||
while ((i = getopt(argc, argv, "hpm:")) != -1) {
|
||||
switch (i) {
|
||||
case 'p':
|
||||
mkdirp = makedirs;
|
||||
|
@ -50,31 +45,21 @@ int main(int argc, char *argv[]) {
|
|||
case 'm':
|
||||
mode = strtol(optarg, 0, 8);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
PrintUsage(0, stdout);
|
||||
PrintUsage(0, 1);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
PrintUsage(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": missing argument\n", stderr);
|
||||
fputs("Try '", stderr);
|
||||
fputs(prog, stderr);
|
||||
fputs(" -h' for more information.\n", stderr);
|
||||
tinyprint(2, prog, ": missing operand\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = optind; i < argc; ++i) {
|
||||
if (mkdirp(argv[i], mode) == -1) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": cannot create directory '", stderr);
|
||||
fputs(argv[i], stderr);
|
||||
fputs("' ", stderr);
|
||||
fputs(_strerdoc(errno), stderr);
|
||||
fputc('\n', stderr);
|
||||
perror(argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,27 +62,15 @@ char linkbuf[PATH_MAX];
|
|||
|
||||
void Mv(char *, char *);
|
||||
|
||||
nullterminated() void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
wontreturn void Die(const char *path, const char *reason) {
|
||||
Print(2, path, ": ", reason, "\n", NULL);
|
||||
tinyprint(2, path, ": ", reason, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wontreturn void SysExit(const char *path, const char *func) {
|
||||
wontreturn void SysDie(const char *path, const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, path, ": ", func, "() failed with ", errstr, "\n", NULL);
|
||||
tinyprint(2, path, ": ", func, ": ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -108,7 +96,7 @@ bool IsSymlink(const char *path) {
|
|||
}
|
||||
|
||||
wontreturn void PrintUsage(int rc, int fd) {
|
||||
Print(fd, "usage: ", prog, USAGE, NULL);
|
||||
tinyprint(fd, "usage: ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
|
@ -161,7 +149,7 @@ char *Join(const char *a, const char *b) {
|
|||
n = strlen(a);
|
||||
m = strlen(b);
|
||||
if (n + 1 + m + 1 > sizeof(dstfile)) {
|
||||
Print(2, "error: mv: path too long\n", NULL);
|
||||
tinyprint(2, "error: mv: path too long\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
stpcpy(stpcpy(stpcpy(dstfile, a), "/"), b);
|
||||
|
@ -192,7 +180,7 @@ void Mv(char *src, char *dst) {
|
|||
strcpy(srcdir, "");
|
||||
}
|
||||
if (nftw(src, Visit, 20, 0) == -1) {
|
||||
SysExit(src, "nftw");
|
||||
SysDie(src, "nftw");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -200,30 +188,31 @@ void Mv(char *src, char *dst) {
|
|||
dst = Join(dst, basename(src));
|
||||
}
|
||||
if (!force && access(dst, W_OK) == -1 && errno != ENOENT) {
|
||||
SysExit(dst, "access");
|
||||
SysDie(dst, "access");
|
||||
}
|
||||
strcpy(mkbuf, dst);
|
||||
if (makedirs((d = dirname(mkbuf)), 0755) == -1) {
|
||||
SysExit(d, "makedirs");
|
||||
SysDie(d, "makedirs");
|
||||
}
|
||||
if (IsSymlink(src)) {
|
||||
if ((rc = readlink(src, linkbuf, sizeof(linkbuf) - 1)) == -1) {
|
||||
SysExit(src, "readlink");
|
||||
SysDie(src, "readlink");
|
||||
}
|
||||
linkbuf[rc] = 0;
|
||||
if (symlink(linkbuf, dst)) {
|
||||
SysExit(dst, "symlink");
|
||||
SysDie(dst, "symlink");
|
||||
}
|
||||
} else {
|
||||
if (rename(src, dst)) {
|
||||
SysExit(src, "rename");
|
||||
SysDie(src, "rename");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
prog = argc > 0 ? argv[0] : "mv.com";
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "mv";
|
||||
GetOpts(argc, argv);
|
||||
if (argc - optind < 2) PrintUsage(1, 2);
|
||||
for (i = optind; i < argc - 1; ++i) {
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*-*- 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/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
char buf[512];
|
||||
|
||||
static void Write(const char *s, ...) {
|
||||
va_list va;
|
||||
va_start(va, s);
|
||||
do {
|
||||
write(2, s, strlen(s));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
wontreturn void SysExit(int rc, const char *call, const char *thing) {
|
||||
int err;
|
||||
char ibuf[12];
|
||||
const char *estr;
|
||||
err = errno;
|
||||
FormatInt32(ibuf, err);
|
||||
estr = _strerdoc(err);
|
||||
if (!estr) estr = "EUNKNOWN";
|
||||
Write(thing, ": ", call, "() failed: ", estr, " (", ibuf, ")\n", 0);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, opt;
|
||||
const char *outpath = "/dev/stdout";
|
||||
while ((opt = getopt(argc, argv, "o:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
outpath = optarg;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int out = open(outpath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (out == -1) SysExit(2, "open", outpath);
|
||||
for (i = optind; i < argc; ++i) {
|
||||
int in = open(argv[i], O_RDONLY);
|
||||
if (in == -1) SysExit(3, "open", argv[i]);
|
||||
for (;;) {
|
||||
ssize_t rc = read(in, buf, 512);
|
||||
if (rc == -1) SysExit(3, "read", argv[i]);
|
||||
if (!rc) break;
|
||||
ssize_t rc2 = write(out, buf, rc);
|
||||
if (rc2 != rc) SysExit(4, "write", outpath);
|
||||
}
|
||||
if (close(in) == -1) SysExit(5, "close", argv[i]);
|
||||
}
|
||||
if (close(out) == -1) SysExit(6, "close", outpath);
|
||||
}
|
|
@ -153,27 +153,15 @@ struct Relas {
|
|||
} * p;
|
||||
} prtu;
|
||||
|
||||
nullterminated() static void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static wontreturn void Die(const char *path, const char *reason) {
|
||||
Print(2, path, ": ", reason, "\n", NULL);
|
||||
tinyprint(2, path, ": ", reason, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void SysExit(const char *path, const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerrno(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
tinyprint(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -321,7 +309,7 @@ static void WritePackage(struct Package *pkg) {
|
|||
}
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int exitcode) {
|
||||
Print(fd, "\n\
|
||||
tinyprint(fd, "\n\
|
||||
NAME\n\
|
||||
\n\
|
||||
Cosmopolitan Monorepo Packager\n\
|
||||
|
@ -329,7 +317,7 @@ NAME\n\
|
|||
SYNOPSIS\n\
|
||||
\n\
|
||||
",
|
||||
program_invocation_name, " [FLAGS] OBJECT...\n\
|
||||
program_invocation_name, " [FLAGS] OBJECT...\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
|
@ -345,7 +333,7 @@ FLAGS\n\
|
|||
-d PATH package dependency path [repeatable]\n\
|
||||
\n\
|
||||
",
|
||||
NULL);
|
||||
NULL);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
|
@ -370,14 +358,15 @@ static void GetOpts(struct Package *pkg, struct Packages *deps, int argc,
|
|||
}
|
||||
}
|
||||
if (pkg->path == -1) {
|
||||
Print(2, "error: no packages passed to package.com\n", NULL);
|
||||
tinyprint(2, "error: no packages passed to package.com\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (optind == argc) {
|
||||
Print(2,
|
||||
"no objects passed to package.com; is your foo.mk $(FOO_OBJS) glob "
|
||||
"broken?\n",
|
||||
NULL);
|
||||
tinyprint(
|
||||
2,
|
||||
"no objects passed to package.com; is your foo.mk $(FOO_OBJS) glob "
|
||||
"broken?\n",
|
||||
NULL);
|
||||
exit(1);
|
||||
}
|
||||
getargs_init(&ga, argv + optind);
|
||||
|
@ -495,7 +484,7 @@ static void LoadPriviligedRefsToUndefs(struct Package *pkg,
|
|||
if (obj->syms[x].st_shndx) continue; // symbol is defined
|
||||
if (ELF64_ST_BIND(obj->syms[x].st_info) != STB_WEAK &&
|
||||
ELF64_ST_BIND(obj->syms[x].st_info) != STB_GLOBAL) {
|
||||
Print(2, "warning: undefined symbol not global\n", NULL);
|
||||
tinyprint(2, "warning: undefined symbol not global\n", NULL);
|
||||
continue;
|
||||
}
|
||||
if (!(s = GetElfString(obj->elf, obj->size, obj->strs,
|
||||
|
@ -602,13 +591,13 @@ static void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
|
|||
undef = &pkg->undefs.p[i];
|
||||
if (undef->bind_ == STB_WEAK) continue;
|
||||
if (!FindSymbol(pkg->strings.p + undef->name, pkg, deps, NULL, NULL)) {
|
||||
Print(2, pkg->strings.p + pkg->path, ": undefined symbol '",
|
||||
pkg->strings.p + undef->name, "' (",
|
||||
pkg->strings.p + pkg->objects.p[undef->object].path,
|
||||
") not defined by direct dependencies:\n", NULL);
|
||||
tinyprint(2, pkg->strings.p + pkg->path, ": undefined symbol '",
|
||||
pkg->strings.p + undef->name, "' (",
|
||||
pkg->strings.p + pkg->objects.p[undef->object].path,
|
||||
") not defined by direct dependencies:\n", NULL);
|
||||
for (j = 0; j < deps->i; ++j) {
|
||||
dep = deps->p[j];
|
||||
Print(2, "\t", dep->strings.p + dep->path, "\n", NULL);
|
||||
tinyprint(2, "\t", dep->strings.p + dep->path, "\n", NULL);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
@ -626,10 +615,11 @@ static void CheckYourPrivilege(struct Package *pkg, struct Packages *deps) {
|
|||
name = prtu.p[i].symbol_name;
|
||||
if (FindSymbol(name, pkg, deps, &dep, &sym) &&
|
||||
dep->sections.p[sym->section].kind == kText) {
|
||||
Print(2, prtu.p[i].object_path,
|
||||
": privileged code referenced unprivileged symbol '", name,
|
||||
"' in section '",
|
||||
dep->strings.p + dep->sections.p[sym->section].name, "'\n", NULL);
|
||||
tinyprint(2, prtu.p[i].object_path,
|
||||
": privileged code referenced unprivileged symbol '", name,
|
||||
"' in section '",
|
||||
dep->strings.p + dep->sections.p[sym->section].name, "'\n",
|
||||
NULL);
|
||||
++f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,9 +119,7 @@ int main(int argc, char *argv[]) {
|
|||
printf(U(argv[1]), argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
|
||||
return 0;
|
||||
default:
|
||||
if (argc > 0) {
|
||||
fprintf(stderr, "%s: %s format [arguments]\n", argv[0], argv[0]);
|
||||
}
|
||||
fprintf(stderr, "%s: %s format [arguments]\n", argv[0], argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,990 +0,0 @@
|
|||
/*-*- 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 2021 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/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "libc/x/xgetline.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Pythonic System Call Trace
|
||||
*
|
||||
* This program invokes `strace` as a subprocess and turns its output
|
||||
* into Python data structures. It is useful because strace output is
|
||||
* this weird plaintext format that's so famously difficult to parse.
|
||||
*
|
||||
* For example, you can run this command:
|
||||
*
|
||||
* pstrace -o trace.pylog echo hello world
|
||||
*
|
||||
* After which you may parse the output with Python:
|
||||
*
|
||||
* for line in open('trace.pylog'):
|
||||
* pid,time,elap,kind,x = eval(line)
|
||||
* if kind == 1:
|
||||
* name,ret,args = x
|
||||
* print "%s%r -> %d" % (name, args, ret)
|
||||
*
|
||||
* This program traces the subset of system calls governing processes
|
||||
* and files. To do that we must track file descriptor lifetimes too.
|
||||
* We also track system calls that are problematic for build configs,
|
||||
* such as sockets, since compiling code shouldn't need the Internet.
|
||||
*
|
||||
* @note this tool is linux only
|
||||
* @note freebsd: truss PROC ARGS
|
||||
* @note appleos: sudo dtruss PROC ARGS
|
||||
* @note openbsd: ktrace PROC ARGS && kdump -f ktrace.out
|
||||
* @note windows: https://github.com/rogerorr/NtTrace
|
||||
*/
|
||||
|
||||
#define DEBUG "%ld: %s", lineno, line
|
||||
#define READ128BE(S) ((uint128_t)READ64BE(S) << 64 | READ64BE((S) + 8))
|
||||
#define APPEND(L) \
|
||||
do { \
|
||||
if (++L.n > L.c) { \
|
||||
L.c = MAX(11, L.c); \
|
||||
L.c += L.c >> 1; \
|
||||
L.p = realloc(L.p, L.c * sizeof(*L.p)); \
|
||||
} \
|
||||
bzero(L.p + L.n - 1, sizeof(*L.p)); \
|
||||
} while (0)
|
||||
|
||||
struct Trace {
|
||||
struct Slices {
|
||||
long n, c;
|
||||
struct Slice {
|
||||
int n, c;
|
||||
char *p;
|
||||
} * p;
|
||||
} slices;
|
||||
struct HashTable {
|
||||
long i, n;
|
||||
struct HashEntry {
|
||||
long h;
|
||||
long i;
|
||||
} * p;
|
||||
} sliceindex;
|
||||
struct Strlists {
|
||||
long n, c;
|
||||
struct Strlist {
|
||||
long n, c;
|
||||
long *p; // slices.p[p[i]]
|
||||
} * p;
|
||||
} strlists;
|
||||
struct Events {
|
||||
long n, c;
|
||||
struct Event {
|
||||
enum EventKind {
|
||||
EK_NONE,
|
||||
EK_CALL,
|
||||
EK_EXIT, // ret is kernel code
|
||||
EK_SIGNAL, // ret is signal code
|
||||
EK_KILLED, // ret is signal code
|
||||
} kind;
|
||||
unsigned char arity;
|
||||
unsigned char syscall_;
|
||||
bool is_interrupted;
|
||||
int us;
|
||||
int elap;
|
||||
int pid;
|
||||
long sec;
|
||||
long ret;
|
||||
long lineno;
|
||||
struct Arg {
|
||||
enum ArgKind {
|
||||
AK_LONG, // x
|
||||
AK_STR, // slices.p[x]
|
||||
AK_STRLIST, // strlists.p[x]
|
||||
AK_INTPAIR, // (x&0xffffffff, x>>32)
|
||||
} kind;
|
||||
long name;
|
||||
long x;
|
||||
} arg[6];
|
||||
} * p;
|
||||
} events;
|
||||
};
|
||||
|
||||
static const struct Syscall {
|
||||
char name[16];
|
||||
} kSyscalls[] = {
|
||||
{"accept"}, //
|
||||
{"accept4"}, //
|
||||
{"access"}, //
|
||||
{"bind"}, //
|
||||
{"chdir"}, //
|
||||
{"chmod"}, //
|
||||
{"chown"}, //
|
||||
{"chroot"}, //
|
||||
{"clone"}, //
|
||||
{"close"}, //
|
||||
{"connect"}, //
|
||||
{"creat"}, //
|
||||
{"dup"}, //
|
||||
{"dup2"}, //
|
||||
{"dup3"}, //
|
||||
{"epoll_create"}, //
|
||||
{"epoll_create1"}, //
|
||||
{"eventfd"}, //
|
||||
{"eventfd2"}, //
|
||||
{"execve"}, //
|
||||
{"execveat"}, //
|
||||
{"faccessat"}, //
|
||||
{"fchmodat"}, //
|
||||
{"fchownat"}, //
|
||||
{"fdatasync"}, //
|
||||
{"fcntl"}, //
|
||||
{"flock"}, //
|
||||
{"fork"}, //
|
||||
{"fsync"}, //
|
||||
{"lchown"}, //
|
||||
{"link"}, //
|
||||
{"linkat"}, //
|
||||
{"listen"}, //
|
||||
{"memfd_create"}, //
|
||||
{"mkdir"}, //
|
||||
{"mkdirat"}, //
|
||||
{"mknod"}, //
|
||||
{"mknodat"}, //
|
||||
{"open"}, //
|
||||
{"openat"}, //
|
||||
{"pipe"}, //
|
||||
{"pipe2"}, //
|
||||
{"readlink"}, //
|
||||
{"readlinkat"}, //
|
||||
{"rename"}, //
|
||||
{"renameat"}, //
|
||||
{"renameat2"}, //
|
||||
{"rmdir"}, //
|
||||
{"signalfd"}, //
|
||||
{"signalfd4"}, //
|
||||
{"socket"}, //
|
||||
{"socketpair"}, //
|
||||
{"statfs"}, //
|
||||
{"symlink"}, //
|
||||
{"symlinkat"}, //
|
||||
{"sync"}, //
|
||||
{"syncfs"}, //
|
||||
{"timerfd_create"}, //
|
||||
{"truncate"}, //
|
||||
{"unlink"}, //
|
||||
{"unlinkat"}, //
|
||||
{"utimensat"}, //
|
||||
{"vfork"}, //
|
||||
};
|
||||
|
||||
static const struct Signal {
|
||||
char name[8];
|
||||
unsigned char number;
|
||||
} kSignals[] = {
|
||||
{"SIGABRT", 6}, //
|
||||
{"SIGALRM", 14}, //
|
||||
{"SIGBUS", 7}, //
|
||||
{"SIGCHLD", 17}, //
|
||||
{"SIGCONT", 18}, //
|
||||
{"SIGFPE", 8}, //
|
||||
{"SIGHUP", 1}, //
|
||||
{"SIGILL", 4}, //
|
||||
{"SIGINT", 2}, //
|
||||
{"SIGIO", 29}, //
|
||||
{"SIGIOT", 6}, //
|
||||
{"SIGKILL", 9}, //
|
||||
{"SIGPIPE", 13}, //
|
||||
{"SIGPOLL", 29}, //
|
||||
{"SIGPROF", 27}, //
|
||||
{"SIGPWR", 30}, //
|
||||
{"SIGQUIT", 3}, //
|
||||
{"SIGSEGV", 11}, //
|
||||
{"SIGSTOP", 19}, //
|
||||
{"SIGSYS", 31}, //
|
||||
{"SIGTERM", 15}, //
|
||||
{"SIGTRAP", 5}, //
|
||||
{"SIGTSTP", 20}, //
|
||||
{"SIGTTIN", 21}, //
|
||||
{"SIGTTOU", 22}, //
|
||||
{"SIGURG", 23}, //
|
||||
{"SIGUSR1", 10}, //
|
||||
{"SIGUSR2", 12}, //
|
||||
{"SIGWINCH", 28}, //
|
||||
{"SIGXCPU", 24}, //
|
||||
{"SIGXFSZ", 25}, //
|
||||
};
|
||||
|
||||
static const struct Errno {
|
||||
char name[16];
|
||||
unsigned char number;
|
||||
} kErrnos[] = {
|
||||
{"E2BIG", 7}, //
|
||||
{"EACCES", 13}, //
|
||||
{"EADDRINUSE", 98}, //
|
||||
{"EADDRNOTAVAIL", 99}, //
|
||||
{"EADV", 68}, //
|
||||
{"EAFNOSUPPORT", 97}, //
|
||||
{"EAGAIN", 11}, //
|
||||
{"EALREADY", 114}, //
|
||||
{"EBADE", 52}, //
|
||||
{"EBADF", 9}, //
|
||||
{"EBADFD", 77}, //
|
||||
{"EBADMSG", 74}, //
|
||||
{"EBADR", 53}, //
|
||||
{"EBADRQC", 56}, //
|
||||
{"EBADSLT", 57}, //
|
||||
{"EBFONT", 59}, //
|
||||
{"EBUSY", 16}, //
|
||||
{"ECANCELED", 125}, //
|
||||
{"ECHILD", 10}, //
|
||||
{"ECHRNG", 44}, //
|
||||
{"ECOMM", 70}, //
|
||||
{"ECONNABORTED", 103}, //
|
||||
{"ECONNREFUSED", 111}, //
|
||||
{"ECONNRESET", 104}, //
|
||||
{"EDEADLK", 35}, //
|
||||
{"EDESTADDRREQ", 89}, //
|
||||
{"EDOM", 33}, //
|
||||
{"EDOTDOT", 73}, //
|
||||
{"EDQUOT", 122}, //
|
||||
{"EEXIST", 17}, //
|
||||
{"EFAULT", 14}, //
|
||||
{"EFBIG", 27}, //
|
||||
{"EHOSTDOWN", 112}, //
|
||||
{"EHOSTUNREACH", 113}, //
|
||||
{"EHWPOISON", 133}, //
|
||||
{"EIDRM", 43}, //
|
||||
{"EILSEQ", 84}, //
|
||||
{"EINPROGRESS", 115}, //
|
||||
{"EINTR", 4}, //
|
||||
{"EINVAL", 22}, //
|
||||
{"EIO", 5}, //
|
||||
{"EISCONN", 106}, //
|
||||
{"EISDIR", 21}, //
|
||||
{"EISNAM", 120}, //
|
||||
{"EKEYEXPIRED", 127}, //
|
||||
{"EKEYREJECTED", 129}, //
|
||||
{"EKEYREVOKED", 128}, //
|
||||
{"EL2HLT", 51}, //
|
||||
{"EL2NSYNC", 45}, //
|
||||
{"EL3HLT", 46}, //
|
||||
{"EL3RST", 47}, //
|
||||
{"ELIBACC", 79}, //
|
||||
{"ELIBBAD", 80}, //
|
||||
{"ELIBEXEC", 83}, //
|
||||
{"ELIBMAX", 82}, //
|
||||
{"ELIBSCN", 81}, //
|
||||
{"ELNRNG", 48}, //
|
||||
{"ELOOP", 40}, //
|
||||
{"EMEDIUMTYPE", 124}, //
|
||||
{"EMFILE", 24}, //
|
||||
{"EMLINK", 31}, //
|
||||
{"EMSGSIZE", 90}, //
|
||||
{"EMULTIHOP", 72}, //
|
||||
{"ENAMETOOLONG", 36}, //
|
||||
{"ENAVAIL", 119}, //
|
||||
{"ENETDOWN", 100}, //
|
||||
{"ENETRESET", 102}, //
|
||||
{"ENETUNREACH", 101}, //
|
||||
{"ENFILE", 23}, //
|
||||
{"ENOANO", 55}, //
|
||||
{"ENOBUFS", 105}, //
|
||||
{"ENOCSI", 50}, //
|
||||
{"ENODATA", 61}, //
|
||||
{"ENODEV", 19}, //
|
||||
{"ENOENT", 2}, //
|
||||
{"ENOEXEC", 8}, //
|
||||
{"ENOKEY", 126}, //
|
||||
{"ENOLCK", 37}, //
|
||||
{"ENOLINK", 67}, //
|
||||
{"ENOMEDIUM", 123}, //
|
||||
{"ENOMEM", 12}, //
|
||||
{"ENOMSG", 42}, //
|
||||
{"ENONET", 64}, //
|
||||
{"ENOPKG", 65}, //
|
||||
{"ENOPROTOOPT", 92}, //
|
||||
{"ENOSPC", 28}, //
|
||||
{"ENOSR", 63}, //
|
||||
{"ENOSTR", 60}, //
|
||||
{"ENOSYS", 38}, //
|
||||
{"ENOTBLK", 15}, //
|
||||
{"ENOTCONN", 107}, //
|
||||
{"ENOTDIR", 20}, //
|
||||
{"ENOTEMPTY", 39}, //
|
||||
{"ENOTNAM", 118}, //
|
||||
{"ENOTRECOVERABLE", 131}, //
|
||||
{"ENOTSOCK", 88}, //
|
||||
{"ENOTSUP", 95}, //
|
||||
{"ENOTTY", 25}, //
|
||||
{"ENOTUNIQ", 76}, //
|
||||
{"ENXIO", 6}, //
|
||||
{"EOPNOTSUPP", 95}, //
|
||||
{"EOVERFLOW", 75}, //
|
||||
{"EOWNERDEAD", 130}, //
|
||||
{"EPERM", 1}, //
|
||||
{"EPFNOSUPPORT", 96}, //
|
||||
{"EPIPE", 32}, //
|
||||
{"EPROTO", 71}, //
|
||||
{"EPROTONOSUPPORT", 93}, //
|
||||
{"EPROTOTYPE", 91}, //
|
||||
{"ERANGE", 34}, //
|
||||
{"EREMCHG", 78}, //
|
||||
{"EREMOTE", 66}, //
|
||||
{"EREMOTEIO", 121}, //
|
||||
{"ERESTART", 85}, //
|
||||
{"ERFKILL", 132}, //
|
||||
{"EROFS", 30}, //
|
||||
{"ESHUTDOWN", 108}, //
|
||||
{"ESOCKTNOSUPPORT", 94}, //
|
||||
{"ESPIPE", 29}, //
|
||||
{"ESRCH", 3}, //
|
||||
{"ESRMNT", 69}, //
|
||||
{"ESTALE", 116}, //
|
||||
{"ESTRPIPE", 86}, //
|
||||
{"ETIME", 62}, //
|
||||
{"ETIMEDOUT", 110}, //
|
||||
{"ETOOMANYREFS", 109}, //
|
||||
{"ETXTBSY", 26}, //
|
||||
{"EUCLEAN", 117}, //
|
||||
{"EUNATCH", 49}, //
|
||||
{"EUSERS", 87}, //
|
||||
{"EWOULDBLOCK", 11}, //
|
||||
{"EXDEV", 18}, //
|
||||
{"EXFULL", 54}, //
|
||||
};
|
||||
|
||||
static char **strace_args;
|
||||
static size_t strace_args_len;
|
||||
static volatile bool interrupted;
|
||||
|
||||
static long Hash(const void *p, size_t n) {
|
||||
unsigned h, i;
|
||||
for (h = i = 0; i < n; i++) {
|
||||
h += ((unsigned char *)p)[i];
|
||||
h *= 0x9e3779b1;
|
||||
}
|
||||
return MAX(1, h);
|
||||
}
|
||||
|
||||
static uint64_t MakeKey64(const char *p, size_t n) {
|
||||
char k[8] = {0};
|
||||
memcpy(k, p, n);
|
||||
return READ64BE(k);
|
||||
}
|
||||
|
||||
static uint128_t MakeKey128(const char *p, size_t n) {
|
||||
char k[16] = {0};
|
||||
memcpy(k, p, n);
|
||||
return READ128BE(k);
|
||||
}
|
||||
|
||||
static int GetSyscall(const char *name, size_t namelen) {
|
||||
int m, l, r;
|
||||
uint128_t x, y;
|
||||
char *endofname;
|
||||
if (namelen && namelen <= 16) {
|
||||
x = MakeKey128(name, namelen);
|
||||
l = 0;
|
||||
r = ARRAYLEN(kSyscalls) - 1;
|
||||
while (l <= r) {
|
||||
m = (l + r) >> 1;
|
||||
y = READ128BE(kSyscalls[m].name);
|
||||
if (x < y) {
|
||||
r = m - 1;
|
||||
} else if (x > y) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int GetErrno(const char *name, size_t namelen) {
|
||||
int m, l, r;
|
||||
uint128_t x, y;
|
||||
char *endofname;
|
||||
if (namelen && namelen <= 16) {
|
||||
x = MakeKey128(name, namelen);
|
||||
l = 0;
|
||||
r = ARRAYLEN(kErrnos) - 1;
|
||||
while (l <= r) {
|
||||
m = (l + r) >> 1;
|
||||
y = READ128BE(kErrnos[m].name);
|
||||
if (x < y) {
|
||||
r = m - 1;
|
||||
} else if (x > y) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
return kErrnos[m].number;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int GetSignal(const char *name, size_t namelen) {
|
||||
int m, l, r;
|
||||
uint64_t x, y;
|
||||
char *endofname;
|
||||
if (namelen && namelen <= 8) {
|
||||
x = MakeKey64(name, namelen);
|
||||
l = 0;
|
||||
r = ARRAYLEN(kSignals) - 1;
|
||||
while (l <= r) {
|
||||
m = (l + r) >> 1;
|
||||
y = READ64BE(kSignals[m].name);
|
||||
if (x < y) {
|
||||
r = m - 1;
|
||||
} else if (x > y) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
return kSignals[m].number;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct Trace *NewTrace(void) {
|
||||
return calloc(1, sizeof(struct Trace));
|
||||
}
|
||||
|
||||
static void FreeTrace(struct Trace *t) {
|
||||
long i;
|
||||
if (t) {
|
||||
for (i = 0; i < t->slices.n; ++i) {
|
||||
free(t->slices.p[i].p);
|
||||
}
|
||||
free(t->slices.p);
|
||||
free(t->sliceindex.p);
|
||||
for (i = 0; i < t->strlists.n; ++i) {
|
||||
free(t->strlists.p[i].p);
|
||||
}
|
||||
free(t->strlists.p);
|
||||
free(t->events.p);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendStrlists(struct Trace *t) {
|
||||
APPEND(t->strlists);
|
||||
}
|
||||
|
||||
static void AppendStrlist(struct Strlist *l) {
|
||||
APPEND((*l));
|
||||
}
|
||||
|
||||
static void AppendEvent(struct Trace *t) {
|
||||
APPEND(t->events);
|
||||
}
|
||||
|
||||
static void AppendSlices(struct Trace *t) {
|
||||
APPEND(t->slices);
|
||||
}
|
||||
|
||||
static void AppendSlice(struct Slice *s, int c) {
|
||||
APPEND((*s));
|
||||
s->p[s->n - 1] = c;
|
||||
}
|
||||
|
||||
static long Intern(struct Trace *t, char *data, long size) {
|
||||
struct HashEntry *p;
|
||||
long i, j, k, n, m, h, n2;
|
||||
h = Hash(data, size);
|
||||
n = t->sliceindex.n;
|
||||
i = 0;
|
||||
if (n) {
|
||||
k = 0;
|
||||
do {
|
||||
i = (h + k + ((k + 1) >> 1)) & (n - 1);
|
||||
if (t->sliceindex.p[i].h == h &&
|
||||
t->slices.p[t->sliceindex.p[i].i].n == size &&
|
||||
!memcmp(t->slices.p[t->sliceindex.p[i].i].p, data, size)) {
|
||||
free(data);
|
||||
return t->sliceindex.p[i].i;
|
||||
}
|
||||
++k;
|
||||
} while (t->sliceindex.p[i].h);
|
||||
}
|
||||
if (++t->sliceindex.i >= (n >> 1)) {
|
||||
m = n ? n << 1 : 16;
|
||||
p = calloc(m, sizeof(struct HashEntry));
|
||||
for (j = 0; j < n; ++j) {
|
||||
if (t->sliceindex.p[j].h) {
|
||||
k = 0;
|
||||
do {
|
||||
i = (t->sliceindex.p[j].h + k + ((k + 1) >> 1)) & (m - 1);
|
||||
++k;
|
||||
} while (p[i].h);
|
||||
p[i].h = t->sliceindex.p[j].h;
|
||||
p[i].i = t->sliceindex.p[j].i;
|
||||
}
|
||||
}
|
||||
k = 0;
|
||||
do {
|
||||
i = (h + k + ((k + 1) >> 1)) & (m - 1);
|
||||
++k;
|
||||
} while (p[i].h);
|
||||
free(t->sliceindex.p);
|
||||
t->sliceindex.p = p;
|
||||
t->sliceindex.n = m;
|
||||
}
|
||||
AppendSlices(t);
|
||||
t->slices.p[t->slices.n - 1].p = data;
|
||||
t->slices.p[t->slices.n - 1].n = size;
|
||||
t->sliceindex.p[i].i = t->slices.n - 1;
|
||||
t->sliceindex.p[i].h = h;
|
||||
return t->slices.n - 1;
|
||||
}
|
||||
|
||||
static long ReadCharLiteral(struct Slice *buf, long c, char *p, long *i) {
|
||||
if (c != '\\') return c;
|
||||
switch ((c = p[(*i)++])) {
|
||||
case 'a':
|
||||
return '\a';
|
||||
case 'b':
|
||||
return '\b';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case 'f':
|
||||
return '\f';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 'e':
|
||||
return 033;
|
||||
case 'x':
|
||||
if (isxdigit(p[*i])) {
|
||||
c = hextoint(p[(*i)++]);
|
||||
if (isxdigit(p[*i])) {
|
||||
c = c * 16 + hextoint(p[(*i)++]);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
c -= '0';
|
||||
if ('0' <= p[*i] && p[*i] <= '7') {
|
||||
c = c * 8 + (p[(*i)++] - '0');
|
||||
if ('0' <= p[*i] && p[*i] <= '7') {
|
||||
c = c * 8 + (p[(*i)++] - '0');
|
||||
}
|
||||
}
|
||||
return c;
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
static long GetDuration(long sec1, long us1, long sec2, long us2) {
|
||||
long elap;
|
||||
if ((elap = (sec2 - sec1) * 1000000)) {
|
||||
return elap + 1000000 - us1 + us2;
|
||||
} else {
|
||||
return elap + us2 - us1;
|
||||
}
|
||||
}
|
||||
|
||||
static void Parse(struct Trace *t, const char *line, long lineno) {
|
||||
char *p, *q;
|
||||
struct Slice b;
|
||||
long c, i, j, k, arg, pid, event, sec, us;
|
||||
p = line;
|
||||
pid = strtol(p, &p, 10);
|
||||
while (*p == ' ') ++p;
|
||||
sec = strtol(p, &p, 10);
|
||||
CHECK_EQ('.', *p++, DEBUG);
|
||||
us = strtol(p, &p, 10);
|
||||
CHECK_EQ(' ', *p++, DEBUG);
|
||||
if (_startswith(p, "<... ")) {
|
||||
CHECK_NOTNULL((p = strchr(p, '>')));
|
||||
++p;
|
||||
for (event = t->events.n; event--;) {
|
||||
if (t->events.p[event].pid == pid) {
|
||||
CHECK(t->events.p[event].is_interrupted, DEBUG);
|
||||
t->events.p[event].is_interrupted = false;
|
||||
t->events.p[event].elap =
|
||||
GetDuration(t->events.p[event].sec, t->events.p[event].us, sec, us);
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_GE(event, 0);
|
||||
} else {
|
||||
AppendEvent(t);
|
||||
event = t->events.n - 1;
|
||||
t->events.p[event].pid = pid;
|
||||
t->events.p[event].sec = sec;
|
||||
t->events.p[event].us = us;
|
||||
t->events.p[event].lineno = lineno;
|
||||
if (_startswith(p, "+++ exited with ")) {
|
||||
p += strlen("+++ exited with ");
|
||||
t->events.p[event].kind = EK_EXIT;
|
||||
t->events.p[event].ret = atoi(p);
|
||||
return;
|
||||
} else if (_startswith(p, "+++ killed by ")) {
|
||||
p += strlen("+++ killed by ");
|
||||
CHECK((q = strchr(p, ' ')), DEBUG);
|
||||
t->events.p[event].kind = EK_KILLED;
|
||||
t->events.p[event].ret = GetSignal(p, q - p);
|
||||
return;
|
||||
} else if (_startswith(p, "--- ")) {
|
||||
p += 4;
|
||||
CHECK(isalpha(*p), DEBUG);
|
||||
CHECK((q = strchr(p, ' ')), DEBUG);
|
||||
t->events.p[event].kind = EK_SIGNAL;
|
||||
t->events.p[event].ret = GetSignal(p, q - p);
|
||||
return;
|
||||
} else if (isalpha(*p) && (q = strchr(p, '('))) {
|
||||
t->events.p[event].kind = EK_CALL;
|
||||
CHECK_NE(-1, (t->events.p[event].syscall_ = GetSyscall(p, q - p)), DEBUG);
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (*p == ',') ++p;
|
||||
while (*p == ' ') ++p;
|
||||
CHECK(*p, DEBUG);
|
||||
if (_startswith(p, "<unfinished ...>")) {
|
||||
t->events.p[event].is_interrupted = true;
|
||||
break;
|
||||
} else if (*p == ')') {
|
||||
++p;
|
||||
while (isspace(*p)) ++p;
|
||||
CHECK_EQ('=', *p++, DEBUG);
|
||||
while (isspace(*p)) ++p;
|
||||
CHECK(isdigit(*p) || *p == '-', DEBUG);
|
||||
t->events.p[event].ret = strtol(p, &p, 0);
|
||||
if (t->events.p[event].ret == -1) {
|
||||
while (isspace(*p)) ++p;
|
||||
CHECK((q = strchr(p, ' ')), DEBUG);
|
||||
if ((t->events.p[event].ret = GetErrno(p, q - p)) != -1) {
|
||||
t->events.p[event].ret = -t->events.p[event].ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
CHECK_LT((arg = t->events.p[event].arity++), 6);
|
||||
if (isalpha(*p) && !_startswith(p, "NULL")) {
|
||||
bzero(&b, sizeof(b));
|
||||
for (; isalpha(*p) || *p == '_'; ++p) {
|
||||
AppendSlice(&b, *p);
|
||||
}
|
||||
t->events.p[event].arg[arg].name = Intern(t, b.p, b.n);
|
||||
CHECK_EQ('=', *p++, DEBUG);
|
||||
} else {
|
||||
t->events.p[event].arg[arg].name = -1;
|
||||
}
|
||||
if (_startswith(p, "NULL")) {
|
||||
p += 4;
|
||||
t->events.p[event].arg[arg].kind = AK_LONG;
|
||||
t->events.p[event].arg[arg].x = 0;
|
||||
} else if (*p == '-' || isdigit(*p)) {
|
||||
t->events.p[event].arg[arg].kind = AK_LONG;
|
||||
for (;;) {
|
||||
t->events.p[event].arg[arg].x |= strtol(p, &p, 0);
|
||||
if (*p == '|') {
|
||||
++p;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (*p == '{') {
|
||||
CHECK_NOTNULL((p = strchr(p, '}')), DEBUG);
|
||||
++p;
|
||||
} else if (*p == '"') {
|
||||
bzero(&b, sizeof(b));
|
||||
for (j = 0; (c = p[++j]);) {
|
||||
if (c == '"') {
|
||||
p += j + 1;
|
||||
break;
|
||||
}
|
||||
c = ReadCharLiteral(&b, c, p, &j);
|
||||
AppendSlice(&b, c);
|
||||
}
|
||||
t->events.p[event].arg[arg].kind = AK_STR;
|
||||
t->events.p[event].arg[arg].x = Intern(t, b.p, b.n);
|
||||
} else if (*p == '[') {
|
||||
++p;
|
||||
if (isdigit(*p)) {
|
||||
t->events.p[event].arg[arg].kind = AK_INTPAIR;
|
||||
t->events.p[event].arg[arg].x = strtol(p, &p, 0) & 0xffffffff;
|
||||
CHECK_EQ(',', *p++, DEBUG);
|
||||
CHECK_EQ(' ', *p++, DEBUG);
|
||||
t->events.p[event].arg[arg].x |= strtol(p, &p, 0) << 32;
|
||||
CHECK_EQ(']', *p++, DEBUG);
|
||||
} else {
|
||||
AppendStrlists(t);
|
||||
for (j = 0;; ++j) {
|
||||
if (*p == ']') {
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
if (*p == ',') ++p;
|
||||
if (*p == ' ') ++p;
|
||||
CHECK_EQ('"', *p, DEBUG);
|
||||
bzero(&b, sizeof(b));
|
||||
for (k = 0; (c = p[++k]);) {
|
||||
if (c == '"') {
|
||||
p += k + 1;
|
||||
break;
|
||||
}
|
||||
c = ReadCharLiteral(&b, c, p, &k);
|
||||
AppendSlice(&b, c);
|
||||
}
|
||||
AppendStrlist(&t->strlists.p[t->strlists.n - 1]);
|
||||
t->strlists.p[t->strlists.n - 1]
|
||||
.p[t->strlists.p[t->strlists.n - 1].n - 1] = Intern(t, b.p, b.n);
|
||||
}
|
||||
t->events.p[event].arg[arg].kind = AK_STRLIST;
|
||||
t->events.p[event].arg[arg].x = t->strlists.n - 1;
|
||||
}
|
||||
} else {
|
||||
CHECK(false, DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintArg(FILE *f, struct Trace *t, long ev, long arg) {
|
||||
long i, x;
|
||||
x = t->events.p[ev].arg[arg].name;
|
||||
if (x != -1) {
|
||||
fprintf(f, "b%`'.*s:", t->slices.p[x].n, t->slices.p[x].p);
|
||||
}
|
||||
x = t->events.p[ev].arg[arg].x;
|
||||
switch (t->events.p[ev].arg[arg].kind) {
|
||||
case AK_LONG:
|
||||
fprintf(f, "%ld", x);
|
||||
break;
|
||||
case AK_STR:
|
||||
fprintf(f, "b%`'.*s", t->slices.p[x].n, t->slices.p[x].p);
|
||||
break;
|
||||
case AK_INTPAIR:
|
||||
fprintf(f, "(%d,%d)", x >> 32, x);
|
||||
break;
|
||||
case AK_STRLIST:
|
||||
fprintf(f, "(");
|
||||
for (i = 0; i < t->strlists.p[x].n; ++i) {
|
||||
fprintf(f, "b%`'.*s,", t->slices.p[t->strlists.p[x].p[i]].n,
|
||||
t->slices.p[t->strlists.p[x].p[i]].p);
|
||||
}
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintEvent(FILE *f, struct Trace *t, long ev) {
|
||||
long arg;
|
||||
fprintf(f, "(%d,%ld,%d,%d,", t->events.p[ev].pid,
|
||||
t->events.p[ev].sec * 1000000 + t->events.p[ev].us,
|
||||
t->events.p[ev].elap, t->events.p[ev].kind);
|
||||
switch (t->events.p[ev].kind) {
|
||||
case EK_EXIT:
|
||||
case EK_SIGNAL:
|
||||
case EK_KILLED:
|
||||
fprintf(f, "%d", t->events.p[ev].ret);
|
||||
break;
|
||||
case EK_CALL:
|
||||
CHECK_LT(t->events.p[ev].syscall_, ARRAYLEN(kSyscalls));
|
||||
fprintf(f, "(b%`'s,%ld,", kSyscalls[t->events.p[ev].syscall_].name,
|
||||
t->events.p[ev].ret);
|
||||
fprintf(f, "%c",
|
||||
t->events.p[ev].arity && t->events.p[ev].arg[0].name != -1 ? '{'
|
||||
: '(');
|
||||
for (arg = 0; arg < t->events.p[ev].arity; ++arg) {
|
||||
PrintArg(f, t, ev, arg);
|
||||
fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "%c)",
|
||||
t->events.p[ev].arity && t->events.p[ev].arg[0].name != -1 ? '}'
|
||||
: ')');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(f, ")");
|
||||
}
|
||||
|
||||
static void AppendArg(char *arg) {
|
||||
strace_args = realloc(strace_args, ++strace_args_len * sizeof(*strace_args));
|
||||
strace_args[strace_args_len - 1] = arg;
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(FILE *f, int rc) {
|
||||
fprintf(f, "Usage: %s [-o OUT] PROG [ARGS...]\n", program_invocation_name);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
int ws;
|
||||
int opt;
|
||||
int pid;
|
||||
long ev;
|
||||
FILE *fin;
|
||||
FILE *fout;
|
||||
char *line;
|
||||
long lineno;
|
||||
char *strace;
|
||||
int pipefds[2];
|
||||
struct Trace *t;
|
||||
sigset_t block, mask;
|
||||
struct sigaction ignore, saveint, savequit;
|
||||
|
||||
/*
|
||||
* parse prefix arguments
|
||||
*/
|
||||
fout = stderr;
|
||||
while ((opt = getopt(argc, argv, "?ho:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
fout = fopen(optarg, "w");
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(stdout, EXIT_SUCCESS);
|
||||
default:
|
||||
PrintUsage(stderr, EX_USAGE);
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
PrintUsage(stderr, EX_USAGE);
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve full paths of dependencies
|
||||
*/
|
||||
if ((strace = commandvenv("STRACE", "strace"))) {
|
||||
strace = strdup(strace);
|
||||
} else {
|
||||
fprintf(stderr, "error: please install strace\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* create strace argument list
|
||||
*/
|
||||
AppendArg("strace");
|
||||
AppendArg("-q"); // don't log attach/detach noise
|
||||
AppendArg("-v"); // don't abbreviate arrays
|
||||
AppendArg("-f"); // follow subprocesses
|
||||
AppendArg("-ttt"); // print unixseconds.micros
|
||||
AppendArg("-X"); // print numbers instead of symbols
|
||||
AppendArg("raw"); // e.g. 2 vs. O_RDWR
|
||||
AppendArg("-s"); // don't abbreviate data
|
||||
AppendArg("805306368"); // strace won't let us go higher
|
||||
AppendArg("-e"); // system calls that matter
|
||||
AppendArg(
|
||||
"open,close,access,pipe,dup,dup2,socket,connect,accept,bind,listen,"
|
||||
"socketpair,fork,vfork,execve,clone,flock,fsync,fdatasync,truncate,chdir,"
|
||||
"rename,mkdir,rmdir,creat,link,unlink,symlink,readlink,chmod,chown,fcntl,"
|
||||
"lchown,mknod,mknodat,statfs,chroot,sync,epoll_create,openat,mkdirat,"
|
||||
"fchownat,unlinkat,renameat,linkat,symlinkat,readlinkat,fchmodat,fchdir,"
|
||||
"faccessat,utimensat,accept4,dup3,pipe2,epoll_create1,signalfd,signalfd4,"
|
||||
"eventfd,eventfd2,timerfd_create,syncfs,renameat2,memfd_create,execveat");
|
||||
CHECK_NE(-1, pipe(pipefds));
|
||||
AppendArg("-o");
|
||||
AppendArg(xasprintf("/dev/fd/%d", pipefds[1]));
|
||||
for (i = optind; i < argc; ++i) {
|
||||
AppendArg(argv[i]);
|
||||
}
|
||||
AppendArg(NULL);
|
||||
|
||||
/*
|
||||
* spawn strace
|
||||
*/
|
||||
ignore.sa_flags = 0;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
sigfillset(&block);
|
||||
sigprocmask(SIG_BLOCK, &block, &mask);
|
||||
CHECK_NE(-1, (pid = vfork()));
|
||||
if (!pid) {
|
||||
close(pipefds[0]);
|
||||
sigaction(SIGINT, &saveint, NULL);
|
||||
sigaction(SIGQUIT, &savequit, NULL);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
execv(strace, strace_args);
|
||||
_exit(127);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
|
||||
/*
|
||||
* read output of strace until eof
|
||||
*/
|
||||
fin = fdopen(pipefds[0], "r");
|
||||
t = NewTrace();
|
||||
for (ev = 0, lineno = 1; !interrupted && (line = xgetline(fin)); ++lineno) {
|
||||
_chomp(line);
|
||||
Parse(t, line, lineno);
|
||||
free(line);
|
||||
for (; ev < t->events.n && !t->events.p[ev].is_interrupted; ++ev) {
|
||||
PrintEvent(fout, t, ev);
|
||||
fprintf(fout, "\n");
|
||||
}
|
||||
}
|
||||
FreeTrace(t);
|
||||
CHECK_NE(-1, fclose(fout));
|
||||
|
||||
/*
|
||||
* wait for strace to exit
|
||||
*/
|
||||
while (waitpid(pid, &ws, 0) == -1) {
|
||||
CHECK_EQ(EINTR, errno);
|
||||
}
|
||||
CHECK_NE(-1, fclose(fin));
|
||||
|
||||
/*
|
||||
* propagate exit
|
||||
*/
|
||||
if (WIFEXITED(ws)) {
|
||||
return WEXITSTATUS(ws);
|
||||
} else {
|
||||
return 128 + WTERMSIG(ws);
|
||||
}
|
||||
}
|
|
@ -17,21 +17,27 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Tool for printing current directory.
|
||||
*/
|
||||
|
||||
char path[PATH_MAX];
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *p;
|
||||
if ((p = getcwd(path, sizeof(path)))) {
|
||||
fputs(p, stdout);
|
||||
fputc('\n', stdout);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
char path[PATH_MAX + 2];
|
||||
|
||||
if (!getcwd(path, PATH_MAX)) {
|
||||
perror(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
strcat(path, "\n");
|
||||
if (write(1, path, strlen(path)) == -1) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/dt.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Pretty fast substring refactor tool.
|
||||
*/
|
||||
|
||||
static const char kBefore[] = "\
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │\n\
|
||||
│ │\n\
|
||||
│ This program is free software; you can redistribute it and/or modify │\n\
|
||||
│ it under the terms of the GNU General Public License as published by │\n\
|
||||
│ the Free Software Foundation; version 2 of the License. │\n\
|
||||
│ │\n\
|
||||
│ This program is distributed in the hope that it will be useful, but │\n\
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │\n\
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │\n\
|
||||
│ General Public License for more details. │\n\
|
||||
│ │\n\
|
||||
│ You should have received a copy of the GNU General Public License │\n\
|
||||
│ along with this program; if not, write to the Free Software │\n\
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │\n\
|
||||
│ 02110-1301 USA │\n\
|
||||
";
|
||||
const char kAfter[] = "\
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │\n\
|
||||
│ │\n\
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │\n\
|
||||
│ any purpose with or without fee is hereby granted, provided that the │\n\
|
||||
│ above copyright notice and this permission notice appear in all copies. │\n\
|
||||
│ │\n\
|
||||
│ THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL │\n\
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │\n\
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │\n\
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │\n\
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │\n\
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │\n\
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │\n\
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │\n\
|
||||
";
|
||||
|
||||
#if 0
|
||||
static const char kBefore[] = "\
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│";
|
||||
const char kAfter[] = "\
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│";
|
||||
#endif
|
||||
|
||||
void RefactorFile(const char *path) {
|
||||
int fd;
|
||||
struct stat st;
|
||||
size_t len, partlen, len1, len2;
|
||||
char *mem, *spot = NULL, *part1, *part2;
|
||||
CHECK_NE(-1, (fd = open(path, O_RDONLY)));
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
len2 = 0;
|
||||
if ((len = st.st_size)) {
|
||||
CHECK_NE(MAP_FAILED,
|
||||
(mem = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
partlen = sizeof(kBefore) - 1;
|
||||
if ((spot = memmem(mem, len, kBefore, partlen))) {
|
||||
part1 = gc(xmalloc((len1 = spot - mem)));
|
||||
part2 = gc(xmalloc((len2 = len - partlen - (spot - mem))));
|
||||
memcpy(part1, mem, len1);
|
||||
memcpy(part2, spot + partlen, len2);
|
||||
}
|
||||
CHECK_NE(-1, munmap(mem, len));
|
||||
}
|
||||
CHECK_NE(-1, close(fd));
|
||||
if (spot) {
|
||||
fprintf(stderr, "found! %s\n", path);
|
||||
CHECK_NE(-1, (fd = open(path, O_RDWR | O_TRUNC)));
|
||||
CHECK_EQ(len1, write(fd, part1, len1));
|
||||
CHECK_EQ(sizeof(kAfter) - 1, write(fd, kAfter, sizeof(kAfter) - 1));
|
||||
CHECK_EQ(len2, write(fd, part2, len2));
|
||||
CHECK_NE(-1, close(fd));
|
||||
}
|
||||
}
|
||||
|
||||
void RefactorDir(const char *dpath) {
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
char *path = gc(xmalloc(4096));
|
||||
CHECK_NOTNULL(dir = opendir(firstnonnull(dpath, ".")));
|
||||
while ((ent = readdir(dir))) {
|
||||
if (_startswith(ent->d_name, ".")) continue;
|
||||
if (strcmp(ent->d_name, "o") == 0) continue;
|
||||
snprintf(path, 4096, "%s%s%s", dpath ? dpath : "", dpath ? "/" : "",
|
||||
ent->d_name);
|
||||
if (isdirectory(path)) {
|
||||
RefactorDir(path);
|
||||
} else if (isregularfile(path)) {
|
||||
RefactorFile(path);
|
||||
}
|
||||
}
|
||||
CHECK_NE(-1, closedir(dir));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RefactorDir(NULL);
|
||||
return 0;
|
||||
}
|
251
tool/build/rle.c
251
tool/build/rle.c
|
@ -1,251 +0,0 @@
|
|||
/*-*- 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/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
#define USAGE1 \
|
||||
"NAME\n\
|
||||
\n\
|
||||
rle - Run Length Encoder\n\
|
||||
\n\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
#define USAGE2 \
|
||||
" [FLAGS] [FILE...]\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
This is a primitive compression algorithm. Its advantage is that\n\
|
||||
the concomitant rldecode() library is seventeen bytes, and works\n\
|
||||
on IA-16, IA-32, and NexGen32e without needing to be recompiled.\n\
|
||||
\n\
|
||||
This CLI is consistent with gzip, bzip2, lzma, etc.\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-1 .. -9 ignored\n\
|
||||
-a ignored\n\
|
||||
-c send to stdout\n\
|
||||
-d decompress\n\
|
||||
-f ignored\n\
|
||||
-t test integrity\n\
|
||||
-S SUFFIX overrides .rle extension\n\
|
||||
-h shows this information\n"
|
||||
|
||||
FILE *fin_, *fout_;
|
||||
bool decompress_, test_;
|
||||
const char *suffix_, *hint_;
|
||||
|
||||
void StartErrorMessage(void) {
|
||||
fputs("error: ", stderr);
|
||||
fputs(hint_, stderr);
|
||||
fputs(": ", stderr);
|
||||
}
|
||||
|
||||
void PrintIoErrorMessage(void) {
|
||||
int err;
|
||||
err = errno;
|
||||
StartErrorMessage();
|
||||
fputs(strerror(err), stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void PrintUsage(int rc, FILE *f) {
|
||||
fputs(USAGE1, f);
|
||||
fputs(program_invocation_name, f);
|
||||
fputs(USAGE2, f);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
fin_ = stdin;
|
||||
suffix_ = ".rle";
|
||||
while ((opt = getopt(argc, argv, "123456789S:acdfho:t")) != -1) {
|
||||
switch (opt) {
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'f':
|
||||
break;
|
||||
case 'c':
|
||||
fout_ = stdout;
|
||||
break;
|
||||
case 'd':
|
||||
decompress_ = true;
|
||||
break;
|
||||
case 't':
|
||||
test_ = true;
|
||||
break;
|
||||
case 'o':
|
||||
fclose(fout_);
|
||||
if (!(fout_ = fopen((hint_ = optarg), "w"))) {
|
||||
PrintIoErrorMessage();
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
suffix_ = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RunLengthEncode1(void) {
|
||||
int byte1, byte2, runlength;
|
||||
byte2 = -1;
|
||||
runlength = 0;
|
||||
if ((byte1 = fgetc(fin_)) == -1) return -1;
|
||||
do {
|
||||
while (++runlength < 255) {
|
||||
if ((byte2 = fgetc(fin_)) != byte1) break;
|
||||
}
|
||||
if (fputc(runlength, fout_) == -1 || fputc(byte1, fout_) == -1) {
|
||||
return -1;
|
||||
}
|
||||
runlength = 0;
|
||||
} while ((byte1 = byte2) != -1);
|
||||
return feof(fin_) ? 0 : -1;
|
||||
}
|
||||
|
||||
int RunLengthEncode2(void) {
|
||||
return fputc(0, fout_) | fputc(0, fout_);
|
||||
}
|
||||
|
||||
int EmitRun(unsigned char count, unsigned char byte) {
|
||||
do {
|
||||
if (fputc(byte, fout_) == -1) return -1;
|
||||
} while (--count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunLengthDecode(void) {
|
||||
int byte1, byte2;
|
||||
if ((byte1 = fgetc(fin_)) == -1) return einval();
|
||||
if ((byte2 = fgetc(fin_)) == -1) return einval();
|
||||
while (byte1) {
|
||||
if (!test_ && EmitRun(byte1, byte2) == -1) return -1;
|
||||
if ((byte1 = fgetc(fin_)) == -1) break;
|
||||
if ((byte2 = fgetc(fin_)) == -1) return einval();
|
||||
}
|
||||
if (byte1 != 0 || byte2 != 0) return einval();
|
||||
fgetc(fin_);
|
||||
return feof(fin_) ? 0 : -1;
|
||||
}
|
||||
|
||||
int RunLengthCode(void) {
|
||||
if (test_ || decompress_) {
|
||||
return RunLengthDecode();
|
||||
} else {
|
||||
return RunLengthEncode1();
|
||||
}
|
||||
}
|
||||
|
||||
int Run(char **paths, size_t count) {
|
||||
int rc;
|
||||
char *p;
|
||||
size_t i, suffixlen;
|
||||
const char pathbuf[PATH_MAX];
|
||||
if (!count) {
|
||||
hint_ = "/dev/stdin";
|
||||
if (!fout_) fout_ = stdout;
|
||||
rc = RunLengthCode();
|
||||
rc |= fclose(fin_);
|
||||
fin_ = 0;
|
||||
} else {
|
||||
rc = fclose(fin_);
|
||||
fin_ = 0;
|
||||
for (i = 0; i < count && rc != -1; ++i) {
|
||||
rc = -1;
|
||||
if ((fin_ = fopen((hint_ = paths[i]), "r"))) {
|
||||
if (test_ || fout_) {
|
||||
rc = RunLengthCode();
|
||||
} else {
|
||||
suffixlen = strlen(suffix_);
|
||||
if (!IsTrustworthy() &&
|
||||
strlen(paths[i]) + suffixlen >= ARRAYLEN(pathbuf)) {
|
||||
return eoverflow();
|
||||
}
|
||||
p = stpcpy(pathbuf, paths[i]);
|
||||
if (!decompress_) {
|
||||
strcpy(p, suffix_);
|
||||
} else if (p - pathbuf > suffixlen &&
|
||||
memcmp(p - suffixlen, suffix_, suffixlen) == 0) {
|
||||
p[-suffixlen] = '\0';
|
||||
} else {
|
||||
return enotsup();
|
||||
}
|
||||
if ((fout_ = fopen((hint_ = pathbuf), "w"))) {
|
||||
rc = RunLengthCode();
|
||||
if (rc != -1 && !decompress_) {
|
||||
rc = RunLengthEncode2();
|
||||
}
|
||||
if ((rc |= fclose(fout_)) != -1) {
|
||||
unlink(paths[i]);
|
||||
}
|
||||
fout_ = 0;
|
||||
}
|
||||
}
|
||||
rc |= fclose(fin_);
|
||||
fin_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != -1 && fout_) {
|
||||
rc = RunLengthEncode2();
|
||||
rc |= fclose(fout_);
|
||||
fout_ = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
GetOpts(argc, argv);
|
||||
if (Run(argv + optind, argc - optind) != -1) {
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
PrintIoErrorMessage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
|
@ -22,8 +22,6 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
|
@ -36,63 +34,53 @@ SYNOPSIS\n\
|
|||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-?\n\
|
||||
-h help\n\
|
||||
-f force\n\
|
||||
\n"
|
||||
|
||||
bool force;
|
||||
const char *prog;
|
||||
static bool force;
|
||||
static const char *prog;
|
||||
|
||||
wontreturn void PrintUsage(int rc, FILE *f) {
|
||||
fputs("usage: ", f);
|
||||
fputs(prog, f);
|
||||
fputs(USAGE, f);
|
||||
static wontreturn void PrintUsage(int rc, int fd) {
|
||||
tinyprint(fd, "USAGE\n\n ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "?hf")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hf")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
force = true;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
PrintUsage(0, 1);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
PrintUsage(1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Remove(const char *path) {
|
||||
const char *s;
|
||||
if (!force && access(path, W_OK) == -1) goto OnFail;
|
||||
if (unlink(path) == -1) goto OnFail;
|
||||
return;
|
||||
OnFail:
|
||||
if (force && errno == ENOENT) return;
|
||||
s = _strerdoc(errno);
|
||||
fputs(prog, stderr);
|
||||
fputs(": cannot remove '", stderr);
|
||||
fputs(path, stderr);
|
||||
fputs("': ", stderr);
|
||||
fputs(s, stderr);
|
||||
fputs("\n", stderr);
|
||||
exit(1);
|
||||
static void Remove(const char *path) {
|
||||
if (!force && access(path, W_OK) == -1) {
|
||||
perror(path);
|
||||
exit(1);
|
||||
}
|
||||
if (unlink(path) == -1) {
|
||||
if (force && errno == ENOENT) return;
|
||||
perror(path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
prog = argc > 0 ? argv[0] : "rm.com";
|
||||
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "rm";
|
||||
|
||||
if (argc < 2) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": missing operand\n"
|
||||
"Try 'rm -h' for more information.\n",
|
||||
stderr);
|
||||
tinyprint(2, prog, ": missing operand\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,17 +28,15 @@
|
|||
#include "third_party/getopt/getopt.internal.h"
|
||||
#include "third_party/mbedtls/sha256.h"
|
||||
|
||||
#define PROG "sha256sum"
|
||||
#define USAGE \
|
||||
"\
|
||||
Usage: " PROG " [-?hbctw] [PATH...]\n\
|
||||
"[-?hbctw] [PATH...]\n\
|
||||
-h help\n\
|
||||
-c check mode\n\
|
||||
-b binary mode\n\
|
||||
-t textual mode\n\
|
||||
-w warning mode\n\
|
||||
\n\
|
||||
cosmopolitan " PROG " v1.0\n\
|
||||
cosmopolitan sha256sum v1.1\n\
|
||||
copyright 2022 justine alexandra roberts tunney\n\
|
||||
notice licenses are embedded in the binary\n\
|
||||
https://twitter.com/justinetunney\n\
|
||||
|
@ -56,11 +54,17 @@ static bool g_warn;
|
|||
static char g_mode;
|
||||
static bool g_check;
|
||||
static int g_mismatches;
|
||||
static const char *prog;
|
||||
|
||||
static wontreturn void PrintUsage(int rc, int fd) {
|
||||
tinyprint(fd, "Usage: ", prog, USAGE, NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
g_mode = ' ';
|
||||
while ((opt = getopt(argc, argv, "?hbctw")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hbctw")) != -1) {
|
||||
switch (opt) {
|
||||
case 'w':
|
||||
g_warn = true;
|
||||
|
@ -75,28 +79,13 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
g_mode = '*';
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
write(1, USAGE, sizeof(USAGE) - 1);
|
||||
exit(0);
|
||||
PrintUsage(0, 1);
|
||||
default:
|
||||
write(2, USAGE, sizeof(USAGE) - 1);
|
||||
exit(64);
|
||||
PrintUsage(1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t Write(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[512];
|
||||
buf[0] = 0;
|
||||
va_start(va, s);
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
va_end(va);
|
||||
return write(fd, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static bool IsModeCharacter(char c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
|
@ -117,7 +106,7 @@ static bool IsSupportedPath(const char *path) {
|
|||
case '\r':
|
||||
case '\n':
|
||||
case '\\':
|
||||
Write(2, PROG, ": ", path, ": unsupported path\n", NULL);
|
||||
tinyprint(2, prog, ": ", path, ": unsupported path\n", NULL);
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
|
@ -135,7 +124,7 @@ static bool GetDigest(const char *path, FILE *f, unsigned char digest[32]) {
|
|||
_unassert(!mbedtls_sha256_update_ret(&ctx, buf, got));
|
||||
}
|
||||
if (ferror(f)) {
|
||||
Write(2, PROG, ": ", path, ": ", _strerdoc(errno), "\n", NULL);
|
||||
tinyprint(2, prog, ": ", path, ": ", strerror(errno), "\n", NULL);
|
||||
return false;
|
||||
}
|
||||
_unassert(!mbedtls_sha256_finish_ret(&ctx, digest));
|
||||
|
@ -150,7 +139,7 @@ static bool ProduceDigest(const char *path, FILE *f) {
|
|||
if (!IsSupportedPath(path)) return false;
|
||||
if (!GetDigest(path, f, digest)) return false;
|
||||
hexpcpy(hexdigest, digest, 32);
|
||||
Write(1, hexdigest, " ", mode, path, "\n", NULL);
|
||||
tinyprint(1, hexdigest, " ", mode, path, "\n", NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -182,13 +171,13 @@ static bool CheckDigests(const char *path, FILE *f) {
|
|||
++g_mismatches;
|
||||
k = false;
|
||||
}
|
||||
Write(1, path2, ": ", status, "\n", NULL);
|
||||
tinyprint(1, path2, ": ", status, "\n", NULL);
|
||||
} else {
|
||||
k = false;
|
||||
}
|
||||
fclose(f2);
|
||||
} else {
|
||||
Write(2, PROG, ": ", path2, ": ", _strerdoc(errno), "\n", NULL);
|
||||
tinyprint(2, prog, ": ", path2, ": ", strerror(errno), "\n", NULL);
|
||||
k = false;
|
||||
}
|
||||
continue;
|
||||
|
@ -196,12 +185,12 @@ static bool CheckDigests(const char *path, FILE *f) {
|
|||
if (g_warn) {
|
||||
char linestr[12];
|
||||
FormatInt32(linestr, line + 1);
|
||||
Write(2, PROG, ": ", path, ":", linestr, ": ",
|
||||
"improperly formatted checksum line", "\n", NULL);
|
||||
tinyprint(2, prog, ": ", path, ":", linestr, ": ",
|
||||
"improperly formatted checksum line", "\n", NULL);
|
||||
}
|
||||
}
|
||||
if (ferror(f)) {
|
||||
Write(2, PROG, ": ", path, ": ", _strerdoc(errno), "\n", NULL);
|
||||
tinyprint(2, prog, ": ", path, ": ", strerror(errno), "\n", NULL);
|
||||
k = false;
|
||||
}
|
||||
return k;
|
||||
|
@ -219,6 +208,8 @@ int main(int argc, char *argv[]) {
|
|||
int i;
|
||||
FILE *f;
|
||||
bool k = true;
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "sha256sum";
|
||||
GetOpts(argc, argv);
|
||||
if (optind == argc) {
|
||||
f = stdin;
|
||||
|
@ -229,7 +220,7 @@ int main(int argc, char *argv[]) {
|
|||
k &= Process(argv[i], f);
|
||||
fclose(f);
|
||||
} else {
|
||||
Write(2, PROG, ": ", argv[i], ": ", _strerdoc(errno), "\n", NULL);
|
||||
tinyprint(2, prog, ": ", argv[i], ": ", strerror(errno), "\n", NULL);
|
||||
k = false;
|
||||
}
|
||||
}
|
||||
|
@ -237,8 +228,8 @@ int main(int argc, char *argv[]) {
|
|||
if (g_mismatches) {
|
||||
char ibuf[12];
|
||||
FormatInt32(ibuf, g_mismatches);
|
||||
Write(2, PROG, ": WARNING: ", ibuf, " computed checksum did NOT match\n",
|
||||
NULL);
|
||||
tinyprint(2, prog, ": WARNING: ", ibuf,
|
||||
" computed checksum did NOT match\n", NULL);
|
||||
}
|
||||
return !k;
|
||||
}
|
||||
|
|
|
@ -17,31 +17,31 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Command for updating timestamps on files.
|
||||
* @fileoverview file timestamp update command
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
const char *s, *prog;
|
||||
prog = argc > 0 ? argv[0] : "touch.com";
|
||||
const char *prog;
|
||||
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "touch";
|
||||
|
||||
if (argc < 2) {
|
||||
tinyprint(2, prog, ": missing operand\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (touch(argv[i], 0666) == -1) {
|
||||
s = _strerdoc(errno);
|
||||
fputs(prog, stderr);
|
||||
fputs(": cannot touch '", stderr);
|
||||
fputs(argv[i], stderr);
|
||||
fputs("': ", stderr);
|
||||
fputs(s, stderr);
|
||||
fputs("\n", stderr);
|
||||
perror(argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ int Visit(const char *fpath, const struct stat *sb, int tflag,
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!IsLinux()) return 0;
|
||||
prog = argc > 0 ? argv[0] : "unbundle.com";
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "unbundle";
|
||||
if (IsDirectory("o/third_party/gcc")) return 0;
|
||||
makedirs("o/third_party", 0755);
|
||||
FormatInt32(stpcpy(tmpdir, "o/third_party/gcc."), getpid());
|
||||
|
|
|
@ -1,330 +0,0 @@
|
|||
/*-*- 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));
|
||||
}
|
|
@ -38,32 +38,20 @@ static const char *inpath;
|
|||
static const char *outpath;
|
||||
static unsigned char *inmap;
|
||||
|
||||
nullterminated() static void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static wontreturn void Die(const char *path, const char *reason) {
|
||||
Print(2, path, ": ", reason, "\n", NULL);
|
||||
tinyprint(2, path, ": ", reason, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void SysExit(const char *path, const char *func) {
|
||||
static wontreturn void SysDie(const char *path, const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
tinyprint(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int exitcode) {
|
||||
Print(fd, "\
|
||||
tinyprint(fd, "\
|
||||
NAME\n\
|
||||
\n\
|
||||
Cosmopolitan Zip Copier\n\
|
||||
|
@ -71,7 +59,7 @@ NAME\n\
|
|||
SYNOPSIS\n\
|
||||
\n\
|
||||
",
|
||||
program_invocation_name, " [FLAGS] SRC DST\n\
|
||||
program_invocation_name, " [FLAGS] SRC DST\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
|
@ -96,7 +84,7 @@ EXAMPLE\n\
|
|||
\n\
|
||||
\n\
|
||||
",
|
||||
NULL);
|
||||
NULL);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
|
@ -173,10 +161,10 @@ static void CopyZip(void) {
|
|||
|
||||
// write output
|
||||
if ((outfd = open(outpath, O_WRONLY | O_CREAT, 0644)) == -1) {
|
||||
SysExit(outpath, "open");
|
||||
SysDie(outpath, "open");
|
||||
}
|
||||
if ((outsize = lseek(outfd, 0, SEEK_END)) == -1) {
|
||||
SysExit(outpath, "lseek");
|
||||
SysDie(outpath, "lseek");
|
||||
}
|
||||
ldest = outsize;
|
||||
cdest = outsize + ltotal;
|
||||
|
@ -186,23 +174,23 @@ static void CopyZip(void) {
|
|||
// write local file
|
||||
length = ZIP_LFILE_SIZE(lfile);
|
||||
if (pwrite(outfd, lfile, length, ldest) != length) {
|
||||
SysExit(outpath, "lfile pwrite");
|
||||
SysDie(outpath, "lfile pwrite");
|
||||
}
|
||||
ldest += length;
|
||||
// write directory entry
|
||||
length = ZIP_CFILE_HDRSIZE(cfile);
|
||||
if (pwrite(outfd, cfile, length, cdest) != length) {
|
||||
SysExit(outpath, "lfile pwrite");
|
||||
SysDie(outpath, "lfile pwrite");
|
||||
}
|
||||
cdest += length;
|
||||
}
|
||||
WRITE32LE(eocd + kZipCdirOffsetOffset, outsize + ltotal);
|
||||
length = ZIP_CDIR_HDRSIZE(eocd);
|
||||
if (pwrite(outfd, eocd, length, cdest) != length) {
|
||||
SysExit(outpath, "eocd pwrite");
|
||||
SysDie(outpath, "eocd pwrite");
|
||||
}
|
||||
if (close(outfd)) {
|
||||
SysExit(outpath, "close");
|
||||
SysDie(outpath, "close");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,23 +201,23 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
GetOpts(argc, argv);
|
||||
if ((infd = open(inpath, O_RDONLY)) == -1) {
|
||||
SysExit(inpath, "open");
|
||||
SysDie(inpath, "open");
|
||||
}
|
||||
if ((insize = lseek(infd, 0, SEEK_END)) == -1) {
|
||||
SysExit(inpath, "lseek");
|
||||
SysDie(inpath, "lseek");
|
||||
}
|
||||
if (!insize) {
|
||||
Die(inpath, "file is empty");
|
||||
}
|
||||
if ((inmap = mmap(0, insize, PROT_READ | PROT_WRITE, MAP_PRIVATE, infd, 0)) ==
|
||||
MAP_FAILED) {
|
||||
SysExit(inpath, "mmap");
|
||||
SysDie(inpath, "mmap");
|
||||
}
|
||||
CopyZip();
|
||||
if (munmap(inmap, insize)) {
|
||||
SysExit(inpath, "munmap");
|
||||
SysDie(inpath, "munmap");
|
||||
}
|
||||
if (close(infd)) {
|
||||
SysExit(inpath, "close");
|
||||
SysDie(inpath, "close");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "libc/elf/def.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
|
@ -52,15 +51,16 @@ int strip_components_;
|
|||
const char *path_prefix_;
|
||||
struct timespec timestamp;
|
||||
|
||||
wontreturn void PrintUsage(int rc) {
|
||||
kprintf("\n\
|
||||
wontreturn void PrintUsage(int fd, int rc) {
|
||||
tinyprint(fd, "\n\
|
||||
NAME\n\
|
||||
\n\
|
||||
Cosmpolitan Zip File Compiler\n\
|
||||
\n\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
%s [FLAGS] FILE...\n\
|
||||
",
|
||||
program_invocation_name, " [FLAGS] FILE...\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
|
@ -80,7 +80,7 @@ FLAGS\n\
|
|||
-y SYMBOL generate yoink for symbol (default __zip_eocd)\n\
|
||||
\n\
|
||||
",
|
||||
program_invocation_name);
|
||||
NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
|
@ -117,17 +117,18 @@ void GetOpts(int *argc, char ***argv) {
|
|||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
PrintUsage(EXIT_SUCCESS);
|
||||
PrintUsage(1, EXIT_SUCCESS);
|
||||
default:
|
||||
PrintUsage(EX_USAGE);
|
||||
PrintUsage(2, EX_USAGE);
|
||||
}
|
||||
}
|
||||
*argc -= optind;
|
||||
*argv += optind;
|
||||
if (!outpath_) {
|
||||
kprintf("error: no output path specified\n"
|
||||
"run %s -h for usage\n",
|
||||
program_invocation_name);
|
||||
tinyprint(2,
|
||||
"error: no output path specified\n"
|
||||
"run ",
|
||||
program_invocation_name, " -h for usage\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue