Make build hermetic without shell scripts

- Fix some minor issues in ar.com
- Have execve() look for `ape` command
- Rewrite NT paths using /c/ rather /??/c:/
- Replace broken GCC symlinks with .sym files
- Rewrite $PATH environment variables on startup
- Make $(APE_NO_MODIFY_SELF) the default bootloader
- Add all build command dependencies to build/bootstrap
- Get the repository mostly building from source on non-Linux
This commit is contained in:
Justine Tunney 2022-05-25 11:31:08 -07:00
parent d44ff6ce1f
commit d230a01222
160 changed files with 2754 additions and 1342 deletions

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/arraylist2.internal.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
@ -26,12 +27,16 @@
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
@ -53,6 +58,8 @@
* This tool also adds a feature: it ignores directory parameters. This
* is important because good Makefiles on Linux will generally have the
* directory be a .a prerequisite so archives rebuild on file deletion.
*
* @see https://www.unix.com/man-page/opensolaris/3head/ar.h/
*/
struct Args {
@ -80,29 +87,68 @@ struct Header {
char fmag[2];
};
static void *Realloc(void *p, size_t n) {
void *q;
if (!(q = realloc(p, n))) {
fputs("error: ar: out of memory\n", stderr);
exit(1);
}
return q;
}
static void *Malloc(size_t n) {
return Realloc(0, n);
}
static void NewArgs(struct Args *l, size_t n) {
l->i = 0;
l->n = MAX(2, n);
l->p = Malloc(l->n * sizeof(*l->p));
l->p[0] = 0;
}
static void NewInts(struct Ints *l, size_t n) {
l->i = 0;
l->n = n;
l->p = xmalloc(n * sizeof(int));
l->n = MAX(2, n);
l->p = Malloc(l->n * sizeof(*l->p));
l->p[0] = 0;
}
static void NewString(struct String *s, size_t n) {
s->i = 0;
s->n = n;
s->p = xmalloc(n);
s->n = MAX(2, n);
s->p = Malloc(s->n * sizeof(*s->p));
s->p[0] = 0;
}
static void AppendInt(struct Ints *l, int i) {
APPEND(&l->p, &l->i, &l->n, &i);
assert(l->n > 1);
if (l->i + 1 >= l->n) {
do {
l->n += l->n >> 1;
} while (l->i + 1 >= l->n);
l->p = Realloc(l->p, l->n * sizeof(*l->p));
}
l->p[l->i++] = i;
l->p[l->i] = 0;
}
static void AppendArg(struct Args *l, char *s) {
APPEND(&l->p, &l->i, &l->n, &s);
assert(l->n > 1);
if (l->i + 1 >= l->n) {
do {
l->n += l->n >> 1;
} while (l->i + 1 >= l->n);
l->p = Realloc(l->p, l->n * sizeof(*l->p));
}
l->p[l->i++] = s;
l->p[l->i] = 0;
}
static void MakeHeader(struct Header *h, const char *name, int ref, int mode,
int size) {
size_t n;
char ibuf[13], *p;
memset(h, ' ', sizeof(*h));
n = strlen(name);
memcpy(h->name, name, n);
@ -113,11 +159,15 @@ static void MakeHeader(struct Header *h, const char *name, int ref, int mode,
h->date[0] = '0';
h->uid[0] = '0';
h->gid[0] = '0';
FormatOctal32(h->mode, mode & 0777, false);
p = FormatOctal32(ibuf, mode & 0777, false);
CHECK_LE(p - ibuf, sizeof(h->mode));
memcpy(h->mode, ibuf, p - ibuf);
}
h->fmag[0] = '`';
h->fmag[1] = '\n';
FormatUint32(h->size, size);
p = FormatUint32(ibuf, size);
CHECK_LE(p - ibuf, sizeof(h->size));
memcpy(h->size, ibuf, p - ibuf);
}
int main(int argc, char *argv[]) {
@ -151,15 +201,24 @@ int main(int argc, char *argv[]) {
struct Header *header1, *header2;
int i, j, fd, err, name, outfd, tablebufsize;
if (argc == 2 && !strcmp(argv[1], "-n")) exit(0);
// TODO(jart): Delete this.
if (argc == 2 && !strcmp(argv[1], "-n")) {
exit(0);
}
// we only support one mode of operation, which is creating a new
// deterministic archive. this tool is so fast that we don't need
// database-like tools when editing static archives
if (!(argc > 2 && strcmp(argv[1], "rcsD") == 0)) {
fprintf(stderr, "%s%s%s\n", "Usage: ", argv[0], " rcsD ARCHIVE FILE...");
return 1;
fputs("usage: ", stderr);
if (argc > 0) fputs(argv[0], stderr);
fputs(" rcsD ARCHIVE FILE...", stderr);
exit(EX_USAGE);
}
outpath = argv[2];
bzero(&args, sizeof(args));
st = xmalloc(sizeof(struct stat));
NewArgs(&args, 4);
st = Malloc(sizeof(struct stat));
NewInts(&modes, 128);
NewInts(&names, 128);
NewInts(&sizes, 128);
@ -206,10 +265,10 @@ int main(int argc, char *argv[]) {
// compute length of output archive
outsize = 0;
tablebufsize = 4 + symnames.i * 4;
tablebuf = xmalloc(tablebufsize);
offsets = xmalloc(args.i * 4);
header1 = xmalloc(sizeof(struct Header));
header2 = xmalloc(sizeof(struct Header));
tablebuf = Malloc(tablebufsize);
offsets = Malloc(args.i * 4);
header1 = Malloc(sizeof(struct Header));
header2 = Malloc(sizeof(struct Header));
iov[0].iov_base = "!<arch>\n";
outsize += (iov[0].iov_len = 8);
iov[1].iov_base = header1;

View file

@ -56,9 +56,11 @@ TOOL_BUILD_DIRECTDEPS = \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MBEDTLS \
THIRD_PARTY_MUSL \
THIRD_PARTY_STB \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
TOOL_BUILD_LIB
TOOL_BUILD_DEPS := \
@ -78,7 +80,7 @@ o/$(MODE)/tool/build/%.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/%.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/blinkenlights.com.dbg: \
@ -105,7 +107,7 @@ o/$(MODE)/tool/build/ar.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/ar.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/package.com.dbg: \
@ -113,7 +115,7 @@ o/$(MODE)/tool/build/package.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/package.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/mkdeps.com.dbg: \
@ -121,7 +123,7 @@ o/$(MODE)/tool/build/mkdeps.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/mkdeps.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/compile.com.dbg: \
@ -129,7 +131,7 @@ o/$(MODE)/tool/build/compile.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/compile.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/zipobj.com.dbg: \
@ -137,7 +139,7 @@ o/$(MODE)/tool/build/zipobj.com.dbg: \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/zipobj.o \
$(CRT) \
$(APE)
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/build/emulator.o: \

220
tool/build/cocmd.c Normal file
View file

@ -0,0 +1,220 @@
/*-*- 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/macros.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/o.h"
/**
* @fileoverview Cosmopolitan Command Interpreter
*
* This is a lightweight command interpreter for GNU Make. It has just
* enough shell script language support to support our build config.
*/
#define STATE_SHELL 0
#define STATE_STR 1
char *p;
char *q;
char *cmd;
char *args[8192];
const char *prog;
char argbuf[ARG_MAX];
bool unsupported[256];
wontreturn void UnsupportedSyntax(unsigned char c) {
char ibuf[13];
FormatOctal32(ibuf, c, true);
fputs(prog, stderr);
fputs(": unsupported shell syntax '", stderr);
fputc(c, stderr);
fputs("' (", stderr);
fputs(ibuf, stderr);
fputs("): ", stderr);
fputs(cmd, stderr);
fputs("\n", stderr);
exit(1);
}
void Open(const char *path, int fd, int flags) {
const char *err;
close(fd);
if (open(path, flags, 0644) == -1) {
err = strerdoc(errno);
fputs(prog, stderr);
fputs(": failed to open '", stderr);
fputs(path, stderr);
fputs("': ", stderr);
fputs(err, stderr);
fputs("\n", stderr);
exit(1);
}
}
char *Tokenize(void) {
int t;
char *r;
while (*p == ' ' || *p == '\t' || *p == '\n' ||
(p[0] == '\\' && p[1] == '\n')) {
++p;
}
if (!*p) return 0;
t = STATE_SHELL;
for (r = q;; ++p) {
switch (t) {
case STATE_SHELL:
if (unsupported[*p & 255]) {
UnsupportedSyntax(*p);
}
if (!*p || *p == ' ' || *p == '\t') {
*q++ = 0;
return r;
} else if (*p == '\'') {
t = STATE_STR;
} else if (*p == '\\') {
if (!p[1]) UnsupportedSyntax(*p);
*q++ = *++p;
} else {
*q++ = *p;
}
break;
case STATE_STR:
if (!*p) {
fputs("cmd: error: unterminated string\n", stderr);
exit(1);
}
if (*p == '\'') {
t = STATE_SHELL;
} else {
*q++ = *p;
}
break;
default:
unreachable;
}
}
}
int main(int argc, char *argv[]) {
char *s, *arg;
size_t i, j, n;
prog = argc > 0 ? argv[0] : "cocmd.com";
for (i = 1; i < 32; ++i) {
unsupported[i] = true;
}
unsupported['\t'] = false;
unsupported[0177] = true;
unsupported['~'] = true;
unsupported['`'] = true;
unsupported['#'] = true;
unsupported['$'] = true;
unsupported['*'] = true;
unsupported['('] = true;
unsupported[')'] = true;
unsupported['|'] = true;
unsupported['['] = true;
unsupported[']'] = true;
unsupported['{'] = true;
unsupported['}'] = true;
unsupported[';'] = true;
unsupported['"'] = true;
unsupported['?'] = true;
unsupported['!'] = true;
if (argc != 3) {
fputs(prog, stderr);
fputs(": error: wrong number of args\n", stderr);
return 1;
}
if (strcmp(argv[1], "-c")) {
fputs(prog, stderr);
fputs(": error: argv[1] should -c\n", stderr);
return 1;
}
p = cmd = argv[2];
if (strlen(cmd) >= ARG_MAX) {
fputs(prog, stderr);
fputs(": error: cmd too long: ", stderr);
fputs(cmd, stderr);
fputs("\n", stderr);
return 1;
}
n = 0;
q = argbuf;
while ((arg = Tokenize())) {
if (n + 1 < ARRAYLEN(args)) {
if (!strcmp(arg, "2>&1")) {
close(1);
dup(2);
} else if (!strcmp(arg, ">&2")) {
close(2);
dup(1);
} else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') {
Open(arg + 3, 2, O_WRONLY | O_CREAT | O_APPEND);
} else if (arg[0] == '>' && arg[1] == '>') {
Open(arg + 2, 1, O_WRONLY | O_CREAT | O_APPEND);
} else if (arg[0] == '2' && arg[1] == '>') {
Open(arg + 2, 2, O_WRONLY | O_CREAT | O_TRUNC);
} else if (arg[0] == '>') {
Open(arg + 1, 1, O_WRONLY | O_CREAT | O_TRUNC);
} else if (arg[0] == '<') {
Open(arg + 1, 0, O_RDONLY);
} else {
args[n++] = arg;
}
} else {
fputs(prog, stderr);
fputs(": error: too many args\n", stderr);
return 1;
}
}
if (!n) {
fputs(prog, stderr);
fputs(": error: too few args\n", stderr);
return 1;
}
execv(args[0], args);
if (!n) {
s = strerdoc(errno);
fputs(prog, stderr);
fputs(": execve '", stderr);
fputs(args[0], stderr);
fputs("' failed: ", stderr);
fputs(s, stderr);
fputs("\n", stderr);
return 1;
}
return 127;
}

View file

@ -32,6 +32,7 @@
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
@ -170,9 +171,7 @@ char *output;
char *outpath;
char *command;
char *shortened;
char *cachedcmd;
char *colorflag;
char *originalcmd;
char ccpath[PATH_MAX];
struct stat st;
@ -582,7 +581,7 @@ int Launch(void) {
timer.it_interval.tv_sec = timeout;
setitimer(ITIMER_REAL, &timer, 0);
}
pid = fork();
pid = vfork();
#if 0
int fd;
@ -665,6 +664,8 @@ int Launch(void) {
kill(pid, SIGKILL);
gotalrm = 1;
}
} else if (errno == ECHILD) {
break;
} else {
/* this should never happen */
PrintRed();
@ -1040,44 +1041,6 @@ int main(int argc, char *argv[]) {
}
}
/*
* help error reporting code find symbol table
*/
if (startswith(cmd, "o/")) {
if (endswith(cmd, ".com")) {
s = xstrcat(cmd, ".dbg");
} else {
s = xstrcat(cmd, ".com.dbg");
}
if (fileexists(s)) {
AddEnv(xstrcat("COMDBG=", getcwd(0, 0), '/', s));
}
}
/*
* create assimilated atomic copies of ape binaries
*/
if (!IsWindows() && endswith(cmd, ".com")) {
if (!startswith(cmd, "o/")) {
cachedcmd = xstrcat("o/", cmd);
} else {
cachedcmd = xstrcat(xstripext(cmd), ".elf");
}
if (FileExistsAndIsNewerThan(cachedcmd, cmd)) {
cmd = cachedcmd;
} else {
originalcmd = cmd;
FormatInt64(buf, getpid());
cmd = xstrcat(originalcmd, ".tmp.", buf);
if (copyfile(originalcmd, cmd, COPYFILE_PRESERVE_TIMESTAMPS) == -1) {
fputs("error: compile.com failed to copy ape executable\n", stderr);
unlink(cmd);
exit(97);
}
}
args.p[0] = cmd;
}
/*
* make sense of standard i/o file descriptors
* we want to permit pipelines but prevent talking to terminal
@ -1117,26 +1080,6 @@ int main(int argc, char *argv[]) {
* run command
*/
ws = Launch();
if (ws != -1 && WIFEXITED(ws) && WEXITSTATUS(ws) == 127) {
if (startswith(cmd, "o/third_party/gcc") &&
fileexists("third_party/gcc/unbundle.sh")) {
system("third_party/gcc/unbundle.sh");
ws = Launch();
}
}
/*
* cleanup temporary copy of ape executable
*/
if (originalcmd) {
if (cachedcmd && WIFEXITED(ws) && !WEXITSTATUS(ws) &&
IsNativeExecutable(cmd)) {
makedirs(xdirname(cachedcmd), 0755);
rename(cmd, cachedcmd);
} else {
unlink(cmd);
}
}
/*
* propagate exit

234
tool/build/cp.c Normal file
View file

@ -0,0 +1,234 @@
/*-*- 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/calls/copyfile.h"
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/ok.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
#include "third_party/musl/ftw.h"
#define USAGE \
" SRC... DST\n\
\n\
SYNOPSIS\n\
\n\
Copies Files\n\
\n\
FLAGS\n\
\n\
-?\n\
-h help\n\
-f force\n\
-r recursive\n\
-n no clobber\n\
-a preserve all\n\
-p preserve owner and timestamps\n\
\n"
int flags;
bool force;
int striplen;
bool recursive;
const char *prog;
char mkbuf[PATH_MAX];
char srcdir[PATH_MAX];
char dstdir[PATH_MAX];
char srcfile[PATH_MAX];
char dstfile[PATH_MAX];
char linkbuf[PATH_MAX];
void Cp(char *, char *);
bool IsDirectory(const char *path) {
int e;
bool res;
struct stat st;
e = errno;
res = stat(path, &st) != -1 && S_ISDIR(st.st_mode);
errno = e;
return res;
}
bool IsSymlink(const char *path) {
int e;
bool res;
struct stat st;
e = errno;
res = fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1 &&
S_ISLNK(st.st_mode);
errno = e;
return res;
}
wontreturn void PrintUsage(int rc, FILE *f) {
fputs("usage: ", f);
fputs(prog, f);
fputs(USAGE, f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hfnaprR")) != -1) {
switch (opt) {
case 'f':
force = true;
break;
case 'r':
case 'R':
recursive = true;
break;
case 'n':
flags |= COPYFILE_NOCLOBBER;
break;
case 'a':
case 'p':
flags |= COPYFILE_PRESERVE_OWNER;
flags |= COPYFILE_PRESERVE_TIMESTAMPS;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
int Visit(const char *fpath, const struct stat *sb, int tflag,
struct FTW *ftwbuf) {
char *src;
strcpy(srcfile, fpath);
src = srcfile + striplen;
strcpy(dstfile, dstdir);
if (!endswith(dstfile, "/")) {
strcat(dstfile, "/");
}
strcat(dstfile, src);
strcpy(srcfile, fpath);
switch (tflag) {
case FTW_D:
return 0;
case FTW_F:
case FTW_SL:
case FTW_SLN:
Cp(srcfile, dstfile);
return 0;
default:
fputs(fpath, stderr);
fputs(": can't handle file type\n", stderr);
exit(1);
}
}
char *Join(const char *a, const char *b) {
size_t n, m;
n = strlen(a);
m = strlen(b);
if (n + 1 + m + 1 > sizeof(dstfile)) {
fputs("error: cp: path too long\n", stderr);
exit(1);
}
stpcpy(stpcpy(stpcpy(dstfile, a), "/"), b);
return dstfile;
}
void Cp(char *src, char *dst) {
ssize_t rc;
const char *s;
if (strlen(src) + 1 > PATH_MAX) _Exit(2);
if (strlen(dst) + 1 > PATH_MAX) _Exit(2);
basename(src);
basename(dst);
if (IsDirectory(src)) {
if (!recursive) {
fputs(prog, stderr);
fputs(": won't copy directory without -r flag.\n", stderr);
exit(1);
}
strcpy(dstdir, dst);
if (IsDirectory(dst)) {
strcpy(srcdir, src);
basename(srcdir);
striplen = 0;
strcpy(srcdir, basename(src));
} else {
strcpy(srcdir, src);
basename(srcdir);
striplen = strlen(srcdir);
strcpy(srcdir, "");
}
if (nftw(src, Visit, 20, 0) == -1) {
fputs(prog, stderr);
fputs(": nftw failed: ", stderr);
fputs(strerdoc(errno), stderr);
fputs("\n", stderr);
exit(1);
}
return;
}
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 (copyfile(src, dst, flags) == -1) goto OnFail;
}
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";
GetOpts(argc, argv);
if (argc - optind < 2) PrintUsage(EX_USAGE, stderr);
for (i = optind; i < argc - 1; ++i) {
Cp(argv[i], argv[argc - 1]);
}
return 0;
}

46
tool/build/echo.c Normal file
View file

@ -0,0 +1,46 @@
/*-*- 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/stdio/stdio.h"
#include "libc/str/str.h"
int main(int argc, char *argv[]) {
int i, j;
FILE *stream = stdout;
bool wantnewline = true;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-n")) {
wantnewline = false;
} else if (!strcmp(argv[i], "-2")) {
stream = stderr;
} else {
break;
}
}
for (j = 0; i + j < argc; ++j) {
if (j) fputc(' ', stream);
fputs(argv[i + j], stream);
}
if (wantnewline) {
fputc('\n', stream);
}
return 0;
}

310
tool/build/gzip.c Normal file
View file

@ -0,0 +1,310 @@
/*-*- 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/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/ok.h"
#include "third_party/getopt/getopt.h"
#include "third_party/zlib/zlib.h"
#define USAGE \
" PATH...\n\
\n\
SYNOPSIS\n\
\n\
Compress Files\n\
\n\
FLAGS\n\
\n\
-?\n\
-h help\n\
-f force\n\
-c use stdout\n\
-d decompress\n\
-A append mode\n\
-x exclusive mode\n\
-k keep input file\n\
-0 disable compression\n\
-1 fastest compression\n\
-4 coolest compression\n\
-9 maximum compression\n\
-a ascii mode (ignored)\n\
-F fixed strategy (advanced)\n\
-L filtered strategy (advanced)\n\
-R run length strategy (advanced)\n\
-H huffman only strategy (advanced)\n\
\n"
bool opt_keep;
bool opt_force;
char opt_level;
bool opt_append;
char opt_strategy;
bool opt_exclusive;
bool opt_usestdout;
bool opt_decompress;
const char *prog;
char databuf[32768];
char pathbuf[PATH_MAX];
wontreturn void PrintUsage(int rc, FILE *f) {
fputs("usage: ", f);
fputs(prog, f);
fputs(USAGE, f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hfcdakxALFRHF0123456789")) != -1) {
switch (opt) {
case 'k':
opt_keep = true;
break;
case 'f':
opt_force = true;
break;
case 'A':
opt_append = true;
break;
case 'c':
opt_usestdout = true;
break;
case 'x':
opt_exclusive = true;
break;
case 'd':
opt_decompress = true;
break;
case 'F':
opt_strategy = 'F'; // Z_FIXED
break;
case 'L':
opt_strategy = 'f'; // Z_FILTERED
break;
case 'R':
opt_strategy = 'R'; // Z_RLE
break;
case 'H':
opt_strategy = 'h'; // Z_HUFFMAN_ONLY
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
opt_level = opt;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
void Compress(const char *inpath) {
FILE *input;
gzFile output;
int rc, n, errnum;
const char *outpath;
char *p, openflags[5];
outpath = 0;
if (inpath) {
input = fopen(inpath, "rb");
} else if (opt_usestdout && (opt_force || !isatty(1))) {
opt_usestdout = true;
inpath = "/dev/stdin";
input = stdin;
} else {
fputs(prog, stderr);
fputs(": compressed data not written to a terminal."
" Use -f to force compression.\n",
stderr);
exit(1);
}
p = openflags;
*p++ = opt_append ? 'a' : 'w';
*p++ = 'b';
if (opt_exclusive) *p++ = 'x';
if (opt_level) *p++ = opt_level;
if (opt_strategy) *p++ = opt_strategy;
*p = 0;
if (opt_usestdout) {
outpath = "/dev/stdout";
output = gzdopen(0, openflags);
} else {
if (strlen(inpath) + 3 + 1 > PATH_MAX) _Exit(2);
stpcpy(stpcpy(pathbuf, inpath), ".gz");
outpath = pathbuf;
output = gzopen(outpath, openflags);
}
if (!output) {
fputs(outpath, stderr);
fputs(": gzopen() failed\n", stderr);
fputs(strerdoc(errno), stderr);
fputs("\n", stderr);
exit(1);
}
do {
rc = fread(databuf, 1, sizeof(databuf), input);
if (rc == -1) {
errnum = 0;
fputs(inpath, stderr);
fputs(": read failed: ", stderr);
fputs(strerdoc(ferror(input)), stderr);
fputs("\n", stderr);
_Exit(1);
}
if (!gzwrite(output, databuf, rc)) {
fputs(outpath, stderr);
fputs(": gzwrite failed: ", stderr);
fputs(gzerror(output, &errnum), stderr);
fputs("\n", stderr);
_Exit(1);
}
} while (rc == sizeof(databuf));
if (input != stdin) {
if (fclose(input)) {
fputs(inpath, stderr);
fputs(": close failed\n", stderr);
_Exit(1);
}
}
if (gzclose(output)) {
fputs(outpath, stderr);
fputs(": gzclose failed\n", stderr);
_Exit(1);
}
if (!opt_keep && !opt_usestdout && (opt_force || !access(inpath, W_OK))) {
unlink(inpath);
}
}
void Decompress(const char *inpath) {
FILE *output;
gzFile input;
int rc, n, errnum;
const char *outpath;
outpath = 0;
if (inpath) {
input = gzopen(inpath, "rb");
} else {
opt_usestdout = true;
inpath = "/dev/stdin";
input = gzdopen(0, "rb");
}
if (!input) {
fputs(inpath, stderr);
fputs(": gzopen() failed\n", stderr);
fputs(strerdoc(errno), stderr);
fputs("\n", stderr);
exit(1);
}
if (opt_usestdout) {
output = stdout;
outpath = "/dev/stdout";
} else if (endswith(inpath, ".gz")) {
n = strlen(inpath);
if (n - 3 + 1 > PATH_MAX) _Exit(2);
memcpy(pathbuf, inpath, n - 3);
pathbuf[n - 3] = 0;
outpath = pathbuf;
if (!(output = fopen(outpath, opt_append ? "wa" : "wb"))) {
fputs(outpath, stderr);
fputs(": open failed: ", stderr);
fputs(strerdoc(errno), stderr);
fputs("\n", stderr);
_Exit(1);
}
} else {
fputs(inpath, stderr);
fputs(": needs to end with .gz unless -c is passed\n", stderr);
_Exit(1);
}
do {
rc = gzread(input, databuf, sizeof(databuf));
if (rc == -1) {
errnum = 0;
fputs(inpath, stderr);
fputs(": gzread failed: ", stderr);
fputs(gzerror(input, &errnum), stderr);
fputs("\n", stderr);
_Exit(1);
}
if (fwrite(databuf, rc, 1, output) != 1) {
fputs(outpath, stderr);
fputs(": write failed: ", stderr);
fputs(strerdoc(ferror(output)), stderr);
fputs("\n", stderr);
_Exit(1);
}
} while (rc == sizeof(databuf));
if (gzclose(input)) {
fputs(inpath, stderr);
fputs(": gzclose failed\n", stderr);
_Exit(1);
}
if (output != stdout) {
if (fclose(output)) {
fputs(outpath, stderr);
fputs(": close failed\n", stderr);
_Exit(1);
}
}
if (!opt_keep && !opt_usestdout && (opt_force || !access(inpath, W_OK))) {
unlink(inpath);
}
}
int main(int argc, char *argv[]) {
int i;
prog = argc > 0 ? argv[0] : "cp.com";
GetOpts(argc, argv);
if (opt_decompress) {
if (optind == argc) {
Decompress(0);
} else {
for (i = optind; i < argc; ++i) {
Decompress(argv[i]);
}
}
} else {
if (optind == argc) {
Compress(0);
} else {
for (i = optind; i < argc; ++i) {
Compress(argv[i]);
}
}
}
return 0;
}

View file

@ -28,6 +28,7 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
@ -225,7 +226,7 @@ wontreturn void OnMissingFile(const char *list, const char *src) {
* automatically restart itself.
*/
if (list) {
fprintf(stderr, "%s %s...\n", "Refreshing", list);
kprintf("%s %s...\n", "Refreshing", list);
unlink(list);
}
exit(1);
@ -249,7 +250,9 @@ void LoadRelationships(int argc, char *argv[]) {
while ((src = getargs_next(&ga))) {
if (ShouldSkipSource(src)) continue;
srcid = GetSourceId(src, strlen(src));
if ((fd = open(src, O_RDONLY)) == -1) OnMissingFile(ga.path, src);
if ((fd = open(src, O_RDONLY)) == -1) {
OnMissingFile(ga.path, src);
}
CHECK_NE(-1, (rc = read(fd, buf, MAX_READ)));
close(fd);
size = rc;
@ -282,13 +285,13 @@ void GetOpts(int argc, char *argv[]) {
buildroot = optarg;
break;
default:
fprintf(stderr, "%s: %s [-r %s] [-o %s] [%s...]\n", "Usage", argv[0],
kprintf("%s: %s [-r %s] [-o %s] [%s...]\n", "Usage", argv[0],
"BUILDROOT", "OUTPUT", "PATHSFILE");
exit(1);
}
}
if (isempty(out)) fprintf(stderr, "need -o FILE"), exit(1);
if (isempty(buildroot)) fprintf(stderr, "need -r o/$(MODE)"), exit(1);
if (isempty(out)) kprintf("need -o FILE"), exit(1);
if (isempty(buildroot)) kprintf("need -r o/$(MODE)"), exit(1);
}
const char *StripExt(const char *s) {
@ -352,7 +355,7 @@ bool HasSameContent(void) {
s = GetFileSizeOrZero(out);
if (s == appendz(bout).i) {
if (s) {
CHECK_NE(-1, (fd = open(out, O_RDONLY)));
CHECK_NE(-1, (fd = open(out, O_RDONLY)), "open(%#s)", out);
CHECK_NE(MAP_FAILED, (m = mmap(0, s, PROT_READ, MAP_SHARED, fd, 0)));
r = !bcmp(bout, m, s);
munmap(m, s);
@ -394,7 +397,7 @@ int main(int argc, char *argv[]) {
appendw(&bout, '\n');
}
/* if (!fileexists(out) || !HasSameContent()) { */
CHECK_NE(-1, (fd = open(out, O_CREAT | O_WRONLY, 0644)));
CHECK_NE(-1, (fd = open(out, O_CREAT | O_WRONLY, 0644)), "open(%#s)", out);
CHECK_NE(-1, ftruncate(fd, appendz(bout).i));
CHECK_NE(-1, xwrite(fd, bout, appendz(bout).i));
CHECK_NE(-1, close(fd));

82
tool/build/mkdir.c Normal file
View file

@ -0,0 +1,82 @@
#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 "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/fmt/conv.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.h"
#define USAGE \
" [-p] [-m MODE] DIR...\n\
Utility for creating directories.\n\
\n\
FLAGS\n\
\n\
-h Help\n\
-m MODE Octal mode\n\
-p Make parent directories\n"
const char *prog;
wontreturn void PrintUsage(int rc, FILE *f) {
fputs("Usage: ", f);
fputs(prog, f);
fputs(USAGE, f);
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) {
switch (i) {
case 'p':
mkdirp = makedirs;
break;
case 'm':
mode = strtol(optarg, 0, 8);
break;
case '?':
case 'h':
PrintUsage(0, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
if (optind == argc) {
fputs(prog, stderr);
fputs(": missing argument\n", stderr);
fputs("Try '", stderr);
fputs(prog, stderr);
fputs(" -h' for more information.\n", stderr);
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);
exit(1);
}
}
return 0;
}

37
tool/build/pwd.c Normal file
View file

@ -0,0 +1,37 @@
/*-*- 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/stdio/stdio.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;
}
}

103
tool/build/rm.c Normal file
View file

@ -0,0 +1,103 @@
/*-*- 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/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.h"
#define USAGE \
" FILE...\n\
\n\
SYNOPSIS\n\
\n\
Removes Files\n\
\n\
FLAGS\n\
\n\
-?\n\
-h help\n\
-f force\n\
\n"
bool force;
const char *prog;
wontreturn void PrintUsage(int rc, FILE *f) {
fputs("usage: ", f);
fputs(prog, f);
fputs(USAGE, f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?hf")) != -1) {
switch (opt) {
case 'f':
force = true;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
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);
}
int main(int argc, char *argv[]) {
int i;
prog = argc > 0 ? argv[0] : "rm.com";
if (argc < 2) {
fputs(prog, stderr);
fputs(": missing operand\n"
"Try 'rm -h' for more information.\n",
stderr);
exit(1);
}
GetOpts(argc, argv);
for (i = optind; i < argc; ++i) {
Remove(argv[i]);
}
}

View file

@ -305,6 +305,7 @@ void Recv(void *output, size_t outputsize) {
// pass along eof condition to zlib
INFOF("mbedtls_ssl_read");
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
if (!received) TlsDie("got unexpected eof", received);
if (received < 0) TlsDie("read failed", received);
// decompress packet completely
// into a dynamical size buffer

46
tool/build/touch.c Normal file
View file

@ -0,0 +1,46 @@
/*-*- 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/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* @fileoverview Command for updating timestamps on files.
*/
int main(int argc, char *argv[]) {
int i;
const char *s, *prog;
prog = argc > 0 ? argv[0] : "touch.com";
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);
exit(1);
}
}
return 0;
}

89
tool/build/unbundle.c Normal file
View file

@ -0,0 +1,89 @@
/*-*- 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/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "third_party/musl/ftw.h"
const char *prog;
char tmpdir[PATH_MAX];
char binpath[PATH_MAX];
bool IsDirectory(const char *path) {
int e;
bool res;
struct stat st;
e = errno;
res = stat(path, &st) != -1 && S_ISDIR(st.st_mode);
errno = e;
return res;
}
void Execute(char *argv[]) {
int ws;
if (!vfork()) {
execv(argv[0], argv);
_Exit(127);
}
wait(&ws);
if (!WIFEXITED(ws) || WEXITSTATUS(ws)) {
fputs(argv[0], stderr);
fputs(": command failed\n", stderr);
exit(1);
}
}
int Visit(const char *fpath, const struct stat *sb, int tflag,
struct FTW *ftwbuf) {
if (tflag == FTW_F && endswith(fpath, ".gz")) {
Execute((char *[]){"build/bootstrap/gzip.com", "-d", fpath, 0});
strcpy(binpath, fpath);
binpath[strlen(binpath) - 3] = 0;
chmod(binpath, 0755);
} else if (tflag == FTW_F && endswith(fpath, ".sym")) {
strcpy(binpath, fpath);
binpath[strlen(binpath) - 4] = 0;
symlink(xslurp(fpath, 0), binpath);
}
return 0;
}
int main(int argc, char *argv[]) {
if (!IsLinux()) return 0;
prog = argc > 0 ? argv[0] : "unbundle.com";
if (IsDirectory("o/third_party/gcc")) return 0;
makedirs("o/third_party", 0755);
FormatInt32(stpcpy(tmpdir, "o/third_party/gcc."), getpid());
Execute(
(char *[]){"build/bootstrap/cp.com", "-r", "third_party/gcc", tmpdir, 0});
if (nftw(tmpdir, Visit, 20, 0) == -1) {
fputs(prog, stderr);
fputs(": nftw failed: ", stderr);
fputs(strerdoc(errno), stderr);
fputs("\n", stderr);
exit(1);
}
rename(tmpdir, "o/third_party/gcc");
}

View file

@ -20,6 +20,7 @@
#include "libc/calls/struct/stat.h"
#include "libc/elf/def.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
@ -51,8 +52,8 @@ const char *path_prefix_;
struct timespec timestamp;
size_t kZipCdirHdrLinkableSizeBootstrap;
wontreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "%s%s%s\n", "Usage: ", program_invocation_name,
wontreturn void PrintUsage(int rc) {
kprintf("%s%s%s\n", "Usage: ", program_invocation_name,
" [-n] [-B] [-C INT] [-P PREFIX] [-o FILE] [-s SYMBOL] [-y YOINK] "
"[FILE...]");
exit(rc);
@ -99,9 +100,9 @@ void GetOpts(int *argc, char ***argv) {
break;
case '?':
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
PrintUsage(EXIT_SUCCESS);
default:
PrintUsage(EX_USAGE, stderr);
PrintUsage(EX_USAGE);
}
}
*argc -= optind;
@ -137,7 +138,7 @@ void ProcessFile(struct ElfWriter *elf, const char *path) {
if (S_ISDIR(st.st_mode)) {
st.st_size = 0;
if (!endswith(name, "/")) {
name = gc(xasprintf("%s/", name));
name = gc(xstrcat(name, '/'));
}
}
elfwriter_zip(elf, name, name, strlen(name), map, st.st_size, st.st_mode,