1DOLLY 72TOOLCHAIN system-build3REQUIRES HOST runtime@04REQUIRES HOST http@056789SOURCE /v0.1.2/include/dolly/toolchain.h 6f9da3258e970e356c31034e1f109a04ac8bd5ab57dc82c19aebf293289fb415 /usr/include/dolly/toolchain.h10SOURCE /v0.1.2/include/dolly/runtime.h 6768fbace3430463e64706356bce945a5c6bd147b4db87ea93717b3ad3ffbf91 /usr/include/dolly/runtime.h11SOURCE /v0.1.2/include/dolly/process.h 5f20c11c294311d68d931a198a0e82de5022f7e080f39611aa59c48334065818 /usr/include/dolly/process.h12SOURCE /v0.1.2/host/http/http.h d09f57cda4a6cde89106653f12366ac95554c21d37d05a69c15764d4254b70a5 /usr/include/dolly/http.h13SOURCE /v0.1.2/host/http/http-abi.h 2f4e1e5c7145730babc91a0ff0a38db8b5e100ae1fab0b722b38ecbd53ad64a0 /usr/include/dolly/http-abi.h14SOURCE /v0.1.2/host/display/display.h 31054ebb6af133ea0a5de7e01730475a536a362261b1c24569a67d0749969633 /usr/include/dolly/display.h15SOURCE /v0.1.2/host/display/display-abi.h 89e4ac84fb4b807ed1aa5e47c28eb402746a1596d7949fa373ea45878782c05f /usr/include/dolly/display-abi.h16SOURCE /v0.1.2/host/input/input.h 50e20fdc6e9949559f2d174f4c4e3708fd5dcc5de8a96af2f06df1c3e405f8f3 /usr/include/dolly/input.h17SOURCE /v0.1.2/host/input/input-abi.h 6d3506ba8384546888499fb3bf9a108096efb3518c0857255607ac707aa15d58 /usr/include/dolly/input-abi.h18SOURCE /v0.1.2/host/download/download.h 666bca4fba5da7bf1e998c6486f56dce24cdbc82f1126f9be59067eb03b03bfa /usr/include/dolly/download.h19SOURCE /v0.1.2/host/download/download-abi.h 45cfbbc85fd2ea3846db581cc3129ef1ae1304a3f9ede06d5dae3b544bd6c0e0 /usr/include/dolly/download-abi.h2021SOURCE /v0.1.2/include/dolly/host.h 9737f9b15bd65f5732e16bd942fe593132c7c886c3076dfda89a529a23fee08a /usr/include/dolly/host.h22SOURCE /v0.1.2/include/dolly/host-abi.h 3f2d26426bb088d61a0cc18b6c464697e98c83d8d34ffaf1d063768e619eb766 /usr/include/dolly/host-abi.h23SOURCE /v0.1.2/host/gpu/gpu.h 4c77c750144fb65eba1de4694ae3b78522a2162810fd83f72dda1592f1578000 /usr/include/dolly/gpu.h24SOURCE /v0.1.2/host/gpu/gpu-abi.h 01e97fc225cda01fb5e295819ceb230252d45fb211b64262d5a2c63e9e065be2 /usr/include/dolly/gpu-abi.h25SOURCE /v0.1.2/host/audio/audio.h 9d291eeb25345a75bb21dbd4c422beb15f30700a5f96d5f665ca52a5d8948b65 /usr/include/dolly/audio.h26SOURCE /v0.1.2/host/audio/audio-abi.h 4eb1f6e8f948ec1371fd5bb0bf4e904c438504e0cbccc4380cca9c98280eea30 /usr/include/dolly/audio-abi.h27SOURCE /v0.1.2/host/upload/upload.h 8d5b888175631a2f6dba010942f73e53c8514559de13ea336af804aa75f15eff /usr/include/dolly/upload.h28SOURCE /v0.1.2/host/upload/upload-abi.h 006056e6891a4d4399f521bc3b6a52362044dc146efb1bca46184c3cd9afb3e6 /usr/include/dolly/upload-abi.h2930SOURCE /v0.1.2/host/threads/threads.h ec223fbc34ff65f173f4b15e338cb25bf34d8bf01b699418f4598d7fc4626394 /usr/include/dolly/threads.h31SOURCE /v0.1.2/host/threads/threads-abi.h 894528f5f392b8f95e4617035ac799b9ff4d520a246ec1bd89b6d04257c644c1 /usr/include/dolly/threads-abi.h32SOURCE /v0.1.2/host/dso/dso.h 8f58b257826b92a7f903d2a33484d6bc49bcc035dac5de3af288bc906728a67b /usr/include/dolly/dso.h33SOURCE /v0.1.2/host/dso/dso-abi.h 7b2243afa5cf82109b2768264686cb57e4a611a577a74793056b4f7cfc2e68f4 /usr/include/dolly/dso-abi.h34SOURCE /v0.1.2/host/sockets/sockets.h 8abea9ccf861677d4dec5a222d048a6be691e8c7f6596d604dd601d44cb65eb3 /usr/include/dolly/sockets.h35SOURCE /v0.1.2/host/sockets/sockets-abi.h d707a200164602165663283a79c2cd43d3932a330dac2dbeddd1e0ef87d7b158 /usr/include/dolly/sockets-abi.h3637EXPORTS HEADER /usr/include38EXPORTS HEADER /usr/include/dolly/toolchain.h39EXPORTS HEADER /usr/include/dolly/runtime.h40EXPORTS HEADER /usr/include/dolly/process.h41EXPORTS HEADER /usr/include/dolly/http.h42EXPORTS HEADER /usr/include/dolly/http-abi.h43EXPORTS HEADER /usr/include/dolly/display.h44EXPORTS HEADER /usr/include/dolly/display-abi.h45EXPORTS HEADER /usr/include/dolly/input.h46EXPORTS HEADER /usr/include/dolly/input-abi.h47EXPORTS HEADER /usr/include/dolly/download.h48EXPORTS HEADER /usr/include/dolly/download-abi.h4950EXPORTS HEADER /usr/include/dolly/host.h51EXPORTS HEADER /usr/include/dolly/host-abi.h52EXPORTS HEADER /usr/include/dolly/gpu.h53EXPORTS HEADER /usr/include/dolly/gpu-abi.h54EXPORTS HEADER /usr/include/dolly/audio.h55EXPORTS HEADER /usr/include/dolly/audio-abi.h56EXPORTS HEADER /usr/include/dolly/upload.h57EXPORTS HEADER /usr/include/dolly/upload-abi.h58EXPORTS HEADER /usr/include/dolly/threads.h59EXPORTS HEADER /usr/include/dolly/threads-abi.h60EXPORTS HEADER /usr/include/dolly/dso.h61EXPORTS HEADER /usr/include/dolly/dso-abi.h62EXPORTS HEADER /usr/include/dolly/sockets.h63EXPORTS HEADER /usr/include/dolly/sockets-abi.h6465EXPORTS LIB compiler-rt /usr/lib/libclang_rt.builtins.a66EXPORTS LIB dolly-gpu /usr/lib/dolly/process/libdolly-gpu.a67EXPORTS LIB dolly-audio /usr/lib/dolly/process/libdolly-audio.a68697071EXPORTS FOLDER process-sdk /usr/lib/dolly/process72EXPORTS FOLDER /usr/lib/clang/24/include73EXPORTS FILE compiler /usr/libexec/dolly/process-bin/compiler74EXPORTS FILE kernel-plugin-abi /usr/lib/dolly/dolly-kernel-plugin-0.wasm757677SOURCE /v0.1.2/dist/static/default/licenses/musl b870108ec5e7790e9f9919064f1b9421d62d5f9b0e6c230c6adf7ea2da62e97b /usr/share/licenses/musl/COPYRIGHT78SOURCE /v0.1.2/dist/static/default/licenses/emscripten 620a78084fc7ca97c0b5dea9abf891f3ffcadfdbf305276f099c9c4e12fc1d86 /usr/share/licenses/emscripten/LICENSE79FILE /usr/share/licenses/musl/COPYRIGHT80FILE /usr/share/licenses/emscripten/LICENSE81828384FILE /tmp/process-tools/cc.c8586 int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_C); }87FILE /tmp/process-tools/cxx.c8889 int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_CXX); }90FILE /tmp/process-tools/ld.c9192 int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_LD); }93FILE /tmp/process-tools/ar.c9495 int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_AR); }96RUN /usr/libexec/dolly/process-bin/compiler --dolly-toolchain-mode=c -O1 /tmp/process-tools/cc.c -o /bin/cc97RUN /usr/libexec/dolly/process-bin/compiler --dolly-toolchain-mode=c -O1 /tmp/process-tools/cxx.c -o /bin/c++98RUN /usr/libexec/dolly/process-bin/compiler --dolly-toolchain-mode=c -O1 /tmp/process-tools/ld.c -o /bin/ld99RUN /usr/libexec/dolly/process-bin/compiler --dolly-toolchain-mode=c -O1 /tmp/process-tools/ar.c -o /bin/ar100101EXPORTS ENV CC cc102EXPORTS ENV AR ar103EXPORTS ENV PATH /bin:/usr/bin104105106107108109EXPORTS TOOL cc110EXPORTS TOOL c++111EXPORTS TOOL ld112EXPORTS TOOL ar113EXPORTS TOOL dollyfile114115116REQUIRES HEADER libc117REQUIRES HEADER runtime118119SOURCE /v0.1.2/dist/static/default/slop.c c6b07d98c65f81f626c90568a2286887e50e2a5be4b85a7879663487ab06d3bf /tmp/slop/slop.c120RUN /usr/libexec/dolly/process-bin/compiler --dolly-toolchain-mode=c -O1 /tmp/slop/slop.c -o /bin/slop121122EXPORTS TOOL slop123EXPORTS ENV SHELL /bin/slop124REQUIRES TOOL cc125126127128FILE /tmp/core-tools/mkdir.c129130131132133 134 static int ensure_directory(const char *path) {135 if (mkdir(path, 0777) == 0) return 0;136 if (errno != EEXIST) return -1;137 struct stat metadata;138 if (stat(path, &metadata) != 0) return -1;139 if (S_ISDIR(metadata.st_mode)) return 0;140 errno = ENOTDIR;141 return -1;142 }143 144 static int create_parents(const char *path) {145 char buffer[1024];146 const size_t length = strlen(path);147 if (length == 0 || length >= sizeof(buffer)) {148 errno = length == 0 ? EINVAL : ENAMETOOLONG;149 return -1;150 }151 memcpy(buffer, path, length + 1);152 for (char *cursor = buffer + 1; *cursor != '\0'; cursor++) {153 if (*cursor != '/') continue;154 *cursor = '\0';155 if (ensure_directory(buffer) != 0) return -1;156 *cursor = '/';157 }158 return ensure_directory(buffer);159 }160 161 int main(int argc, char **argv) {162 int parents = 0;163 int first_path = 1;164 for (; first_path < argc; first_path++) {165 if (strcmp(argv[first_path], "--") == 0) {166 first_path++;167 break;168 }169 if (strcmp(argv[first_path], "--help") == 0) {170 fputs("usage: mkdir [-p] [--] DIRECTORY ...\n", stdout);171 return 0;172 }173 if (strcmp(argv[first_path], "-p") == 0 ||174 strcmp(argv[first_path], "--parents") == 0) {175 parents = 1;176 } else if (argv[first_path][0] == '-') {177 fprintf(stderr, "mkdir: unsupported option: %s\n", argv[first_path]);178 return 2;179 } else {180 break;181 }182 }183 if (first_path == argc) {184 fputs("mkdir: missing directory operand\n", stderr);185 return 2;186 }187 188 int status = 0;189 for (int index = first_path; index < argc; index++) {190 const int result = parents ? create_parents(argv[index])191 : mkdir(argv[index], 0777);192 if (result != 0) {193 fprintf(stderr, "mkdir: %s: %s\n", argv[index], strerror(errno));194 status = 1;195 }196 }197 return status;198 }199FILE /tmp/core-tools/rm.c200201 202203204205206207208 209 static int remove_path(const char *path, int recursive, int force) {210 struct stat metadata;211 if (lstat(path, &metadata) != 0) {212 if (force && errno == ENOENT) return 0;213 fprintf(stderr, "rm: %s: %s\n", path, strerror(errno));214 return 1;215 }216 if (!S_ISDIR(metadata.st_mode)) {217 if (remove(path) == 0) return 0;218 fprintf(stderr, "rm: %s: %s\n", path, strerror(errno));219 return 1;220 }221 if (!recursive) {222 fprintf(stderr, "rm: %s: is a directory\n", path);223 return 1;224 }225 226 DIR *directory = opendir(path);227 if (directory == NULL) {228 fprintf(stderr, "rm: %s: %s\n", path, strerror(errno));229 return 1;230 }231 232 int status = 0;233 struct dirent *entry;234 while ((entry = readdir(directory)) != NULL) {235 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {236 continue;237 }238 char child[1024];239 const int length = snprintf(child, sizeof(child), "%s/%s", path, entry->d_name);240 if (length < 0 || (size_t)length >= sizeof(child)) {241 fprintf(stderr, "rm: %s/%s: path is too long\n", path, entry->d_name);242 status = 1;243 continue;244 }245 if (remove_path(child, recursive, force) != 0) status = 1;246 }247 if (closedir(directory) != 0) status = 1;248 if (status == 0 && remove(path) != 0) {249 fprintf(stderr, "rm: %s: %s\n", path, strerror(errno));250 status = 1;251 }252 return status;253 }254 255 static int protected_path(const char *path) {256 size_t length = strlen(path);257 while (length > 1 && path[length - 1] == '/') length--;258 if (length != 0) {259 size_t base = length;260 while (base != 0 && path[base - 1] != '/') base--;261 const size_t base_length = length - base;262 if ((base_length == 1 && path[base] == '.') ||263 (base_length == 2 && path[base] == '.' && path[base + 1] == '.')) {264 return 1;265 }266 }267 for (size_t index = 0; index < length; index++) {268 if (path[index] != '/') return 0;269 }270 return length != 0;271 }272 273 static int trailing_slash_symlink(const char *path) {274 size_t length = strlen(path);275 size_t trimmed = length;276 while (trimmed > 1 && path[trimmed - 1] == '/') trimmed--;277 if (trimmed == length) return 0;278 char *without_slashes = strndup(path, trimmed);279 if (without_slashes == NULL) return -1;280 struct stat metadata;281 const int result = lstat(without_slashes, &metadata) == 0 &&282 S_ISLNK(metadata.st_mode);283 free(without_slashes);284 return result;285 }286 287 int main(int argc, char **argv) {288 int recursive = 0;289 int force = 0;290 int first_path = 1;291 for (; first_path < argc; first_path++) {292 const char *argument = argv[first_path];293 if (strcmp(argument, "--") == 0) {294 first_path++;295 break;296 }297 if (strcmp(argument, "--help") == 0) {298 fputs("usage: rm [-f] [-r|-R] [--] PATH ...\n", stdout);299 return 0;300 }301 if (argument[0] != '-' || argument[1] == '\0') break;302 for (const char *option = argument + 1; *option != '\0'; option++) {303 if (*option == 'f') force = 1;304 else if (*option == 'r' || *option == 'R') recursive = 1;305 else {306 fprintf(stderr, "rm: unsupported option: -%c\n", *option);307 return 2;308 }309 }310 }311 if (first_path == argc) {312 if (force) return 0;313 fputs("rm: missing operand\n", stderr);314 return 2;315 }316 317 int status = 0;318 for (int index = first_path; index < argc; index++) {319 if (protected_path(argv[index])) {320 fprintf(stderr, "rm: refusing to remove protected path %s\n", argv[index]);321 status = 1;322 continue;323 }324 const int trailing_link = trailing_slash_symlink(argv[index]);325 if (trailing_link != 0) {326 if (trailing_link > 0) {327 fprintf(stderr,328 "rm: refusing to follow directory symlink with trailing slash: %s\n",329 argv[index]);330 } else {331 fprintf(stderr, "rm: %s: out of memory\n", argv[index]);332 }333 status = 1;334 continue;335 }336 if (remove_path(argv[index], recursive, force) != 0) status = 1;337 }338 return status;339 }340FILE /tmp/core-tools/foreground.c341342343344345 346 int main(int argc, char **argv) {347 int first = 1;348 int interactive = 0;349 if (argc == 2 && strcmp(argv[1], "--help") == 0) {350 fputs("usage: foreground [-i] /absolute/program [ARG ...]\n", stdout);351 return 0;352 }353 if (first < argc && strcmp(argv[first], "-i") == 0) {354 interactive = 1;355 first++;356 }357 if (first == argc || argv[first][0] != '/') {358 fputs("usage: foreground [-i] /absolute/program [ARG ...]\n", stderr);359 return 2;360 }361 const int pid = dolly_spawn_foreground(argv[first], argc - first,362 argv + first, interactive);363 if (pid < 0) {364 fprintf(stderr, "foreground: %s: %s\n", argv[first], strerror(-pid));365 return pid == -ENOENT ? 127 : 126;366 }367 int status;368 const int waited = dolly_wait(pid, &status);369 if (waited < 0) {370 fprintf(stderr, "foreground: wait: %s\n", strerror(-waited));371 return 126;372 }373 return status;374 }375FILE /tmp/core-tools/help.c376377378 379 int main(int argc, char **argv) {380 if (argc > 2 || (argc == 2 && strcmp(argv[1], "--help") != 0)) {381 fputs("usage: help\n", stderr);382 return 2;383 }384 fputs("Slop is Dolly's shell: POSIX-like, not Bash\n", stdout);385 fputs("commands are files on PATH (echo $PATH): ls /bin /usr/bin lists them; man NAME prints a command's page and NAME --help its usage (ls /usr/share/man/cat1 /usr/share/man/man1 lists the pages)\n", stdout);386 fputs("the platform is described in /usr/share/doc/dolly/docs: slop.md is the shell language, process-model.md processes and their limits; amy install dolly-docs brings the directory to an image without it\n", stdout);387 fputs("not supported: job control (jobs, fg, bg), aliases, umask, ulimit, brace expansion {a,b}, ${VAR/a/b}, ${VAR:1:2}, $'...'; a glob that matches nothing stays as typed\n", stdout);388 fputs("pipelines: programs run at the same time, so seq 1 999999999 | head -n1 ends at once; a builtin, function or compound stage finishes before the next stage reads its output\n", stdout);389 fputs("background: PROGRAM & and a | b & start programs; $! is the last PID; wait [PID...] collects them; wrap anything else as slop -c '...' &\n", stdout);390 fputs("one user and no permission bits: the modes ls -l prints are fixed and nothing checks them; there is no chmod, chown, id, whoami, ps or df, and install -m changes nothing\n", stdout);391 fputs("limits: 256 descriptors and 8 GiB of memory per process, 32 processes, 16 threads per process (64 in all), 64 KiB pipes; getrlimit reports them\n", stdout);392 fputs("a program that exits 126 was refused or failed: its stderr says why\n", stdout);393 return 0;394 }395FILE /tmp/core-tools/man.c396397398399400401402 403 int main(int argc, char **argv) {404 if (argc == 2 && strcmp(argv[1], "--help") == 0) {405 fputs("usage: man NAME\n"406 "Print the manual page of the command NAME: /usr/share/man/cat1/NAME.1,\n"407 "a page as plain text, or else /usr/share/man/man1/NAME.1, a page as upstream\n"408 "ships it (mdoc or man source, printed unformatted).\n"409 "A terminal gets the page through $PAGER, a shell command, or else through\n"410 "less when it is installed (amy install less). Output that is not a terminal\n"411 "is never paged, so nothing waits for a key.\n"412 "example: man grep\n", stdout);413 return 0;414 }415 if (argc != 2 || argv[1][0] == '-' || strchr(argv[1], '/') != NULL) {416 fputs("usage: man NAME\n", stderr);417 return 2;418 }419 static const char *const sections[] = {"cat1", "man1"};420 for (int section = 0; section < 2; section++) {421 char path[4096];422 if (snprintf(path, sizeof(path), "/usr/share/man/%s/%s.1", sections[section], argv[1]) >= (int)sizeof(path)) break;423 FILE *page = fopen(path, "r");424 if (page == NULL) continue;425 // Paged as Git pages: by $PAGER, else by less when it is installed, and426 // with Git's LESS, which prints a page that fits the screen and returns.427 const char *pager = getenv("PAGER");428 if (isatty(STDOUT_FILENO) && dup2(fileno(page), STDIN_FILENO) == STDIN_FILENO) {429 setenv("LESS", "FRX", 0);430 return WEXITSTATUS(system(pager != NULL && *pager != '\0' ? pager : "$(command -v less || command -v cat)"));431 }432 char buffer[4096];433 size_t count;434 while ((count = fread(buffer, 1, sizeof(buffer), page)) > 0) fwrite(buffer, 1, count, stdout);435 fclose(page);436 return 0;437 }438 fprintf(stderr, "man: no manual entry for %s\n", argv[1]);439 return 1;440 }441FILE /tmp/core-tools/pwd.c442443444445446 447 int main(int argc, char **argv) {448 if (argc == 2 && strcmp(argv[1], "--help") == 0) {449 fputs("usage: pwd [-L|-P]\n", stdout);450 return 0;451 }452 if (argc > 2 ||453 (argc == 2 && strcmp(argv[1], "-L") != 0 && strcmp(argv[1], "-P") != 0)) {454 fprintf(stderr, "pwd: unsupported option: %s\n", argc > 1 ? argv[1] : "");455 return 2;456 }457 char cwd[1024];458 if (getcwd(cwd, sizeof(cwd)) == NULL) {459 fprintf(stderr, "pwd: %s\n", strerror(errno));460 return 1;461 }462 fputs(cwd, stdout);463 fputc('\n', stdout);464 return 0;465 }466FILE /tmp/core-tools/cd.c467468469470471472 473 int main(int argc, char **argv) {474 int first_path = 1;475 if (first_path < argc && strcmp(argv[first_path], "--help") == 0) {476 fputs("usage: cd [--] [DIRECTORY]\n", stdout);477 return 0;478 }479 if (first_path < argc && strcmp(argv[first_path], "--") == 0) first_path++;480 if (argc - first_path > 1) {481 fputs("cd: expected at most one path\n", stderr);482 return 2;483 }484 const char *path = first_path < argc ? argv[first_path] : getenv("HOME");485 if (path == NULL || path[0] == '\0') {486 if (first_path < argc) return 0;487 fputs("cd: HOME is not set\n", stderr);488 return 1;489 }490 if (chdir(path) != 0) {491 fprintf(stderr, "cd: %s: %s\n", path, strerror(errno));492 return 1;493 }494 return 0;495 }496FILE /tmp/core-tools/cat.c497498499500 501 static int copy_stream(FILE *input, const char *name, int number_lines,502 unsigned long *line_number) {503 unsigned char buffer[4096];504 int line_start = 1;505 size_t count;506 while ((count = fread(buffer, 1, sizeof(buffer), input)) != 0) {507 if (!number_lines) {508 if (fwrite(buffer, 1, count, stdout) != count) {509 fprintf(stderr, "cat: %s: write failed\n", name);510 return 1;511 }512 continue;513 }514 for (size_t index = 0; index < count; index++) {515 if (line_start) {516 if (fprintf(stdout, "%6lu\t", (*line_number)++) < 0) return 1;517 line_start = 0;518 }519 if (fputc(buffer[index], stdout) == EOF) return 1;520 if (buffer[index] == '\n') line_start = 1;521 }522 }523 if (ferror(input)) {524 fprintf(stderr, "cat: %s: %s\n", name, strerror(errno));525 return 1;526 }527 return 0;528 }529 530 int main(int argc, char **argv) {531 int number_lines = 0;532 int first_file = 1;533 for (; first_file < argc; first_file++) {534 if (strcmp(argv[first_file], "--") == 0) {535 first_file++;536 break;537 }538 if (strcmp(argv[first_file], "--help") == 0) {539 fputs("usage: cat [-n] [--] [FILE ...]\n", stdout);540 fputs("with no FILE, or when FILE is -, read standard input\n", stdout);541 return 0;542 }543 if (strcmp(argv[first_file], "-n") == 0) {544 number_lines = 1;545 continue;546 }547 if (argv[first_file][0] == '-' && strcmp(argv[first_file], "-") != 0) {548 fprintf(stderr, "cat: unsupported option: %s\n", argv[first_file]);549 return 2;550 }551 break;552 }553 554 int status = 0;555 unsigned long line_number = 1;556 if (first_file == argc) {557 status = copy_stream(stdin, "standard input", number_lines, &line_number);558 }559 for (int index = first_file; index < argc; index++) {560 FILE *input = stdin;561 if (strcmp(argv[index], "-") != 0) {562 input = fopen(argv[index], "rb");563 if (input == NULL) {564 fprintf(stderr, "cat: %s: %s\n", argv[index], strerror(errno));565 status = 1;566 continue;567 }568 }569 if (copy_stream(input, argv[index], number_lines, &line_number) != 0) {570 status = 1;571 }572 if (input != stdin && fclose(input) != 0) {573 fprintf(stderr, "cat: %s: %s\n", argv[index], strerror(errno));574 status = 1;575 }576 }577 if (fflush(stdout) != 0) status = 1;578 return status;579 }580FILE /tmp/core-tools/echo.c581582583 584 int main(int argc, char **argv) {585 int newline = 1;586 int first = 1;587 if (first < argc && strcmp(argv[first], "--help") == 0) {588 fputs("usage: echo [-n] [ARG ...]\n", stdout);589 return 0;590 }591 if (first < argc && strcmp(argv[first], "-n") == 0) {592 newline = 0;593 first++;594 }595 596 for (int index = first; index < argc; index++) {597 if (index != first) fputc(' ', stdout);598 fputs(argv[index], stdout);599 }600 if (newline) fputc('\n', stdout);601 fflush(stdout);602 return ferror(stdout) ? 1 : 0;603 }604FILE /tmp/core-tools/touch.c605606607608609610611612 613 static int field(const char *p) { return (p[0] - '0') * 10 + (p[1] - '0'); }614 615 /* POSIX -t [[CC]YY]MMDDhhmm[.ss] in local time. */616 static int parse_stamp(const char *stamp, time_t *out) {617 size_t len = strcspn(stamp, ".");618 const char *seconds = stamp[len] == '.' ? stamp + len + 1 : NULL;619 for (size_t i = 0; i < len; i++) {620 if (!isdigit((unsigned char)stamp[i])) return -1;621 }622 struct tm tm;623 time_t now = time(NULL);624 localtime_r(&now, &tm);625 const char *p = stamp;626 if (len == 12) {627 tm.tm_year = (field(p) * 100 + field(p + 2)) - 1900;628 p += 4;629 } else if (len == 10) {630 int yy = field(p);631 tm.tm_year = yy < 69 ? 2000 + yy - 1900 : yy;632 p += 2;633 } else if (len != 8) {634 return -1;635 }636 tm.tm_mon = field(p) - 1;637 tm.tm_mday = field(p + 2);638 tm.tm_hour = field(p + 4);639 tm.tm_min = field(p + 6);640 tm.tm_sec = 0;641 if (seconds) {642 if (strlen(seconds) != 2 || !isdigit((unsigned char)seconds[0]) ||643 !isdigit((unsigned char)seconds[1])) {644 return -1;645 }646 tm.tm_sec = field(seconds);647 }648 tm.tm_isdst = -1;649 time_t when = mktime(&tm);650 if (when == (time_t)-1) return -1;651 *out = when;652 return 0;653 }654 655 int main(int argc, char **argv) {656 int no_create = 0;657 struct utimbuf stamp;658 int have_stamp = 0;659 int first_file = 1;660 for (; first_file < argc; first_file++) {661 if (strcmp(argv[first_file], "--") == 0) {662 first_file++;663 break;664 }665 if (strcmp(argv[first_file], "--help") == 0) {666 fputs("usage: touch [-c] [-t [[CC]YY]MMDDhhmm[.ss]] [--] FILE ...\n", stdout);667 return 0;668 }669 if (strcmp(argv[first_file], "-c") == 0 ||670 strcmp(argv[first_file], "--no-create") == 0) {671 no_create = 1;672 } else if (strcmp(argv[first_file], "-t") == 0) {673 if (++first_file == argc) {674 fputs("touch: -t requires a timestamp\n", stderr);675 return 2;676 }677 time_t when;678 if (parse_stamp(argv[first_file], &when) != 0) {679 fprintf(stderr, "touch: invalid timestamp: %s\n", argv[first_file]);680 return 2;681 }682 stamp.actime = stamp.modtime = when;683 have_stamp = 1;684 } else if (argv[first_file][0] == '-') {685 fprintf(stderr, "touch: unsupported option: %s\n", argv[first_file]);686 return 2;687 } else {688 break;689 }690 }691 if (first_file == argc) {692 fputs("touch: missing file operand\n", stderr);693 return 2;694 }695 696 int status = 0;697 for (int index = first_file; index < argc; index++) {698 if (utime(argv[index], have_stamp ? &stamp : NULL) == 0) {699 continue;700 }701 if (errno != ENOENT) {702 fprintf(stderr, "touch: %s: %s\n", argv[index], strerror(errno));703 status = 1;704 continue;705 }706 if (no_create) continue;707 FILE *file = fopen(argv[index], "ab");708 if (file == NULL) {709 fprintf(stderr, "touch: %s: %s\n", argv[index], strerror(errno));710 status = 1;711 } else if (fclose(file) != 0) {712 fprintf(stderr, "touch: %s: %s\n", argv[index], strerror(errno));713 status = 1;714 } else if (have_stamp && utime(argv[index], &stamp) != 0) {715 fprintf(stderr, "touch: %s: %s\n", argv[index], strerror(errno));716 status = 1;717 }718 }719 return status;720 }721FILE /tmp/core-tools/clear.c722723724 725 int main(int argc, char **argv) {726 if (argc == 2 && strcmp(argv[1], "--help") == 0) {727 fputs("usage: clear\n", stdout);728 return 0;729 }730 if (argc != 1) {731 fprintf(stderr, "clear: unsupported option: %s\n", argv[1]);732 return 2;733 }734 fputs("\033[2J\033[H", stdout);735 fflush(stdout);736 return 0;737 }738FILE /tmp/core-tools/ls.c739740 741742743744745746747748749750 751 typedef struct { char **items; size_t length; size_t capacity; } name_list;752 typedef struct {753 int show_all, almost_all, long_format, human, directory;754 int classify, recursive, reverse;755 } options;756 757 static void free_names(name_list *names) {758 for (size_t index = 0; index < names->length; index++) free(names->items[index]);759 free(names->items);760 }761 762 static int add_name(name_list *names, const char *name) {763 if (names->length == names->capacity) {764 size_t capacity = names->capacity == 0 ? 32 : names->capacity * 2;765 if (capacity < names->capacity || capacity > SIZE_MAX / sizeof(*names->items)) return -1;766 char **items = realloc(names->items, capacity * sizeof(*items));767 if (items == NULL) return -1;768 names->items = items;769 names->capacity = capacity;770 }771 char *copy = strdup(name);772 if (copy == NULL) return -1;773 names->items[names->length++] = copy;774 return 0;775 }776 777 static int compare_names(const void *left, const void *right) {778 return strcmp(*(const char *const *)left, *(const char *const *)right);779 }780 781 static char *join_path(const char *directory, const char *name) {782 const size_t directory_length = strlen(directory), name_length = strlen(name);783 const int slash = directory_length != 0 && directory[directory_length - 1] != '/';784 if (directory_length > SIZE_MAX - name_length - (size_t)slash - 1) return NULL;785 char *path = malloc(directory_length + (size_t)slash + name_length + 1);786 if (path == NULL) return NULL;787 memcpy(path, directory, directory_length);788 if (slash) path[directory_length] = '/';789 memcpy(path + directory_length + (size_t)slash, name, name_length + 1);790 return path;791 }792 793 static void format_size(char output[32], off_t size, int human) {794 if (!human || size < 1024) { snprintf(output, 32, "%lld", (long long)size); return; }795 static const char units[] = "KMGTPE";796 double value = (double)size;797 size_t unit = 0;798 do { value /= 1024.0; unit++; } while (value >= 1024.0 && unit < sizeof(units) - 1);799 if (value < 10.0) snprintf(output, 32, "%.1f%c", value, units[unit - 1]);800 else snprintf(output, 32, "%.0f%c", value, units[unit - 1]);801 }802 803 static int print_entry(const char *path, const char *name, const options *option) {804 struct stat metadata;805 if (lstat(path, &metadata) != 0) {806 fprintf(stderr, "ls: %s: %s\n", path, strerror(errno));807 return 1;808 }809 const char suffix = option->classify810 ? S_ISDIR(metadata.st_mode) ? '/' : S_ISLNK(metadata.st_mode) ? '@' : '\0'811 : '\0';812 if (!option->long_format) {813 fputs(name, stdout);814 if (suffix != '\0') fputc(suffix, stdout);815 fputc('\n', stdout);816 return 0;817 }818 // The POSIX long format: the bits are what stat reports, no more; the819 // owner and group are the one user's numeric ids, which have no names;820 // the date carries the time of day for six months, then the year.821 char size[32], timestamp[32] = "?", mode[] = "----------";822 format_size(size, metadata.st_size, option->human);823 const time_t age = time(NULL) - metadata.st_mtime;824 struct tm *broken = localtime(&metadata.st_mtime);825 if (broken != NULL) {826 strftime(timestamp, sizeof(timestamp), age >= 0 && age < 182 * 86400 ? "%b %e %H:%M" : "%b %e %Y", broken);827 }828 mode[0] = S_ISDIR(metadata.st_mode) ? 'd' : S_ISLNK(metadata.st_mode) ? 'l'829 : S_ISCHR(metadata.st_mode) ? 'c' : S_ISBLK(metadata.st_mode) ? 'b'830 : S_ISFIFO(metadata.st_mode) ? 'p' : S_ISSOCK(metadata.st_mode) ? 's' : '-';831 for (int bit = 0; bit < 9; bit++) {832 if (metadata.st_mode & (0400u >> bit)) mode[1 + bit] = "rwx"[bit % 3];833 }834 printf("%s %3lu %u %u %10s %s %s", mode, (unsigned long)metadata.st_nlink,835 (unsigned)metadata.st_uid, (unsigned)metadata.st_gid, size, timestamp, name);836 if (suffix != '\0') fputc(suffix, stdout);837 if (S_ISLNK(metadata.st_mode)) {838 char target[4096];839 const ssize_t length = readlink(path, target, sizeof(target) - 1);840 if (length >= 0) printf(" -> %.*s", (int)length, target);841 }842 fputc('\n', stdout);843 return 0;844 }845 846 static int hidden(const char *name, const options *option) {847 if (name[0] != '.' || option->show_all) return 0;848 if (option->almost_all && strcmp(name, ".") != 0 && strcmp(name, "..") != 0) return 0;849 return 1;850 }851 852 static int list_path(const char *path, const options *option, int print_heading) {853 struct stat metadata;854 if (lstat(path, &metadata) != 0) {855 fprintf(stderr, "ls: %s: %s\n", path, strerror(errno));856 return 1;857 }858 if (!S_ISDIR(metadata.st_mode) || option->directory) return print_entry(path, path, option);859 DIR *directory = opendir(path);860 if (directory == NULL) { fprintf(stderr, "ls: %s: %s\n", path, strerror(errno)); return 1; }861 name_list names = {0};862 int status = 0;863 struct dirent *entry;864 while ((entry = readdir(directory)) != NULL) {865 if (hidden(entry->d_name, option)) continue;866 if (add_name(&names, entry->d_name) != 0) { fputs("ls: out of memory\n", stderr); status = 1; break; }867 }868 if (closedir(directory) != 0) status = 1;869 if (names.length > 1) {870 qsort(names.items, names.length, sizeof(*names.items), compare_names);871 }872 if (print_heading) printf("%s:\n", path);873 if (option->long_format) {874 // Files occupy memory, not blocks: the total counts their sizes in KiB.875 unsigned long long total = 0;876 for (size_t index = 0; index < names.length; index++) {877 char *entry_path = join_path(path, names.items[index]);878 struct stat entry_metadata;879 if (entry_path != NULL && lstat(entry_path, &entry_metadata) == 0) {880 total += ((unsigned long long)entry_metadata.st_size + 1023) / 1024;881 }882 free(entry_path);883 }884 printf("total %llu\n", total);885 }886 for (size_t offset = 0; offset < names.length; offset++) {887 const size_t index = option->reverse ? names.length - offset - 1 : offset;888 char *entry_path = join_path(path, names.items[index]);889 if (entry_path == NULL || print_entry(entry_path, names.items[index], option) != 0) status = 1;890 free(entry_path);891 }892 if (option->recursive) {893 for (size_t offset = 0; offset < names.length; offset++) {894 const size_t index = option->reverse ? names.length - offset - 1 : offset;895 if (strcmp(names.items[index], ".") == 0 || strcmp(names.items[index], "..") == 0) continue;896 char *entry_path = join_path(path, names.items[index]);897 struct stat child;898 if (entry_path != NULL && lstat(entry_path, &child) == 0 && S_ISDIR(child.st_mode) && !S_ISLNK(child.st_mode)) {899 fputc('\n', stdout);900 if (list_path(entry_path, option, 1) != 0) status = 1;901 }902 free(entry_path);903 }904 }905 free_names(&names);906 return status;907 }908 909 static int short_option(options *option, char value) {910 switch (value) {911 case '1': return 0;912 case 'a': option->show_all = 1; option->almost_all = 0; return 0;913 case 'A': option->almost_all = 1; option->show_all = 0; return 0;914 case 'd': option->directory = 1; return 0;915 case 'F': case 'p': option->classify = 1; return 0;916 case 'h': option->human = 1; return 0;917 case 'l': option->long_format = 1; return 0;918 case 'R': option->recursive = 1; return 0;919 case 'r': option->reverse = 1; return 0;920 default: return -1;921 }922 }923 924 int main(int argc, char **argv) {925 options option = {0};926 int first_path = 1;927 for (; first_path < argc; first_path++) {928 const char *argument = argv[first_path];929 if (strcmp(argument, "--") == 0) { first_path++; break; }930 if (strcmp(argument, "--help") == 0) {931 fputs("usage: ls [-1aAdFhlpRr] [--color[=WHEN]] [--] [PATH ...]\n"932 "-l prints mode, links, numeric owner and group, size, date and name; its total is the\n"933 "listed sizes in KiB, each rounded up, because disk blocks do not exist.\n", stdout);934 return 0;935 }936 if (strncmp(argument, "--color", 7) == 0 || strcmp(argument, "--group-directories-first") == 0) continue;937 if (argument[0] != '-' || argument[1] == '\0') break;938 for (size_t index = 1; argument[index] != '\0'; index++) {939 if (short_option(&option, argument[index]) != 0) {940 fprintf(stderr, "ls: unsupported option: -%c\n", argument[index]);941 return 2;942 }943 }944 }945 if (first_path == argc) return list_path(".", &option, 0);946 const int multiple = argc - first_path > 1;947 int status = 0;948 for (int index = first_path; index < argc; index++) {949 struct stat metadata;950 const int listing = !option.directory && lstat(argv[index], &metadata) == 0 && S_ISDIR(metadata.st_mode);951 if (index != first_path && listing) fputc('\n', stdout);952 if (list_path(argv[index], &option, multiple) != 0) status = 1;953 }954 return status;955 }956FILE /tmp/core-tools/stat.c957958 959960961962963964 965 static const char *kind(mode_t mode) {966 if (S_ISDIR(mode)) return "directory";967 if (S_ISLNK(mode)) return "symbolic link";968 if (S_ISREG(mode)) return "regular file";969 if (S_ISCHR(mode)) return "character device";970 if (S_ISFIFO(mode)) return "fifo";971 return "other";972 }973 974 static void print_mode(mode_t mode) {975 putchar(S_ISDIR(mode) ? 'd' : S_ISLNK(mode) ? 'l' : S_ISCHR(mode) ? 'c' : S_ISFIFO(mode) ? 'p' : '-');976 for (int bit = 8; bit >= 0; bit--) putchar(mode & (1u << bit) ? "xwr"[bit % 3] : '-');977 }978 979 static void formatted(const char *format, const char *path, const struct stat *metadata) {980 for (const char *cursor = format; *cursor != '\0'; cursor++) {981 if (*cursor != '%') { putchar(*cursor); continue; }982 switch (*++cursor) {983 case '%': putchar('%'); break;984 case 'n': fputs(path, stdout); break;985 case 's': printf("%lld", (long long)metadata->st_size); break;986 case 'F': fputs(kind(metadata->st_mode), stdout); break;987 case 'Y': printf("%lld", (long long)metadata->st_mtime); break;988 case 'a': printf("%o", (unsigned)(metadata->st_mode & 07777)); break;989 case 'A': print_mode(metadata->st_mode); break;990 }991 }992 putchar('\n');993 }994 995 int main(int argc, char **argv) {996 const char *format = NULL;997 int first = 1;998 if (first < argc && strcmp(argv[first], "-c") == 0) {999 if (++first == argc) { fputs("stat: -c requires a format\n", stderr); return 2; }1000 format = argv[first++];1001 } else if (first < argc && strncmp(argv[first], "--format=", 9) == 0) {1002 format = argv[first++] + 9;1003 } else if (first < argc && strcmp(argv[first], "--help") == 0) {1004 fputs("usage: stat [-c FORMAT] FILE ...\nformats: %n name, %s size, %F type, %Y mtime, %a octal mode, %A mode, %% percent\n", stdout);1005 return 0;1006 }1007 for (const char *cursor = format; cursor != NULL && (cursor = strchr(cursor, '%')) != NULL; cursor += 2) {1008 if (cursor[1] == '\0' || strchr("%nsFYaA", cursor[1]) == NULL) {1009 fprintf(stderr, "stat: unsupported format directive: %.2s\n", cursor);1010 return 2;1011 }1012 }1013 if (first == argc) { fputs("stat: missing file operand\n", stderr); return 2; }1014 int status = 0;1015 for (; first < argc; first++) {1016 struct stat metadata;1017 if (lstat(argv[first], &metadata) != 0) {1018 fprintf(stderr, "stat: %s: %s\n", argv[first], strerror(errno));1019 status = 1;1020 continue;1021 }1022 if (format != NULL) { formatted(format, argv[first], &metadata); continue; }1023 char timestamp[32] = "?";1024 struct tm *broken = localtime(&metadata.st_mtime);1025 if (broken != NULL) strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", broken);1026 printf(" File: %s\n Size: %lld\tType: %s\nModify: %s\n",1027 argv[first], (long long)metadata.st_size, kind(metadata.st_mode), timestamp);1028 }1029 return status;1030 }1031FILE /tmp/core-tools/file.c10321033 103410351036103710381039 1040 // Printable ASCII and well-formed UTF-8; a sequence may be cut off only1041 // where the sample ends before the file does.1042 static int text(const unsigned char *bytes, size_t length, int sampled) {1043 for (size_t index = 0; index < length;) {1044 const unsigned char byte = bytes[index++];1045 size_t extra = byte < 0x80 ? 0 : byte >= 0xc2 && byte <= 0xdf ? 11046 : byte >= 0xe0 && byte <= 0xef ? 2 : byte >= 0xf0 && byte <= 0xf4 ? 3 : 4;1047 if (extra == 4 || (extra == 0 && !isprint(byte) && !isspace(byte))) return 0;1048 for (; extra != 0; extra--, index++) {1049 if (index == length) return sampled;1050 if ((bytes[index] & 0xc0) != 0x80) return 0;1051 }1052 }1053 return 1;1054 }1055 1056 static const char *classify(const char *path, int mime) {1057 static char result[96];1058 struct stat metadata;1059 if (lstat(path, &metadata) != 0) {1060 snprintf(result, sizeof(result), "cannot open: %s", strerror(errno));1061 return result;1062 }1063 if (S_ISDIR(metadata.st_mode)) return mime ? "inode/directory" : "directory";1064 FILE *stream = fopen(path, "rb");1065 if (stream == NULL) {1066 snprintf(result, sizeof(result), "cannot open: %s", strerror(errno));1067 return result;1068 }1069 unsigned char bytes[512];1070 const size_t length = fread(bytes, 1, sizeof(bytes), stream);1071 fclose(stream);1072 static const unsigned char wasm_magic[] = {0, 'a', 's', 'm', 1, 0, 0, 0};1073 if (length >= sizeof(wasm_magic) && memcmp(bytes, wasm_magic, sizeof(wasm_magic)) == 0)1074 return mime ? "application/wasm" : "WebAssembly binary module";1075 if (length >= 8 && memcmp(bytes, "!<arch>\n", 8) == 0)1076 return mime ? "application/x-archive" : "current ar archive";1077 if (length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b)1078 return mime ? "application/gzip" : "gzip compressed data";1079 if (length == 0) return mime ? "application/x-empty" : "empty";1080 if (!text(bytes, length, length == sizeof(bytes)))1081 return mime ? "application/octet-stream" : "data";1082 for (size_t index = 0; index < length; index++) {1083 if (bytes[index] >= 0x80) return mime ? "text/plain" : "UTF-8 Unicode text";1084 }1085 return mime ? "text/plain" : "ASCII text";1086 }1087 1088 int main(int argc, char **argv) {1089 int brief = 0, mime = 0, first = 1;1090 for (; first < argc; first++) {1091 if (strcmp(argv[first], "--") == 0) { first++; break; }1092 if (strcmp(argv[first], "-b") == 0 || strcmp(argv[first], "--brief") == 0) brief = 1;1093 else if (strcmp(argv[first], "--mime-type") == 0) mime = 1;1094 else if (strcmp(argv[first], "--help") == 0) { fputs("usage: file [-b] [--mime-type] FILE ...\n", stdout); return 0; }1095 else if (argv[first][0] == '-') { fprintf(stderr, "file: unsupported option: %s\n", argv[first]); return 2; }1096 else break;1097 }1098 if (first == argc) { fputs("file: missing file operand\n", stderr); return 2; }1099 for (; first < argc; first++) {1100 if (!brief) printf("%s: ", argv[first]);1101 puts(classify(argv[first], mime));1102 }1103 return 0;1104 }1105FILE /tmp/core-tools/test.c11061107 110811091110111111121113 1114111511161117 1118 static int unary(const char *operation, const char *value, int *known) {1119 struct stat metadata;1120 *known = 1;1121 if (strcmp(operation, "-n") == 0) return value[0] != '\0';1122 if (strcmp(operation, "-z") == 0) return value[0] == '\0';1123 if (strcmp(operation, "-L") == 0 || strcmp(operation, "-h") == 0)1124 return lstat(value, &metadata) == 0 && S_ISLNK(metadata.st_mode);1125 if (strcmp(operation, "-e") == 0 || strcmp(operation, "-r") == 0 ||1126 strcmp(operation, "-w") == 0) return stat(value, &metadata) == 0;1127 if (strcmp(operation, "-f") == 0) return stat(value, &metadata) == 0 && S_ISREG(metadata.st_mode);1128 // Dolly has no execute permission bits. PATH resolution likewise accepts a1129 // regular file and leaves Wasm-format/ABI validation to the loader.1130 if (strcmp(operation, "-x") == 0) return stat(value, &metadata) == 0 && S_ISREG(metadata.st_mode);1131 if (strcmp(operation, "-d") == 0) return stat(value, &metadata) == 0 && S_ISDIR(metadata.st_mode);1132 if (strcmp(operation, "-s") == 0) return stat(value, &metadata) == 0 && metadata.st_size > 0;1133 *known = 0;1134 return 0;1135 }1136 1137 static int integer(const char *left, const char *operation, const char *right, int *known) {1138 char *left_end, *right_end;1139 errno = 0;1140 const long long a = strtoll(left, &left_end, 10), b = strtoll(right, &right_end, 10);1141 if (errno != 0 || left_end == left || right_end == right ||1142 *left_end != '\0' || *right_end != '\0') return -1;1143 *known = 1;1144 if (strcmp(operation, "-eq") == 0) return a == b;1145 if (strcmp(operation, "-ne") == 0) return a != b;1146 if (strcmp(operation, "-gt") == 0) return a > b;1147 if (strcmp(operation, "-ge") == 0) return a >= b;1148 if (strcmp(operation, "-lt") == 0) return a < b;1149 if (strcmp(operation, "-le") == 0) return a <= b;1150 *known = 0;1151 return 0;1152 }1153 1154 enum { EXPRESSION_DEPTH_LIMIT = 64 };1155 1156 static int top_level_operator(int argc, char **argv, const char *operation,1157 int *error) {1158 int parentheses = 0;1159 for (int index = 0; index < argc; ++index) {1160 if (strcmp(argv[index], "(") == 0) {1161 parentheses++;1162 } else if (strcmp(argv[index], ")") == 0) {1163 if (parentheses == 0) {1164 *error = 1;1165 return -1;1166 }1167 parentheses--;1168 } else if (parentheses == 0 && strcmp(argv[index], operation) == 0) {1169 return index;1170 }1171 }1172 if (parentheses != 0) *error = 1;1173 return -1;1174 }1175 1176 static int outer_parentheses(int argc, char **argv, int *error) {1177 if (argc < 2 || strcmp(argv[0], "(") != 0) return 0;1178 int depth = 0;1179 for (int index = 0; index < argc; ++index) {1180 if (strcmp(argv[index], "(") == 0) depth++;1181 else if (strcmp(argv[index], ")") == 0) {1182 if (--depth < 0) {1183 *error = 1;1184 return 0;1185 }1186 if (depth == 0) return index == argc - 1;1187 }1188 }1189 *error = 1;1190 return 0;1191 }1192 1193 static int evaluate(int argc, char **argv, int *error, unsigned depth) {1194 *error = 0;1195 if (argc == 0) return 0;1196 if (argc == 1) return argv[0][0] != '\0';1197 if (depth == EXPRESSION_DEPTH_LIMIT) {1198 *error = 1;1199 return 0;1200 }1201 if (outer_parentheses(argc, argv, error)) {1202 if (argc == 2) {1203 *error = 1;1204 return 0;1205 }1206 return evaluate(argc - 2, argv + 1, error, depth + 1);1207 }1208 if (*error) return 0;1209 1210 int operation = top_level_operator(argc, argv, "-o", error);1211 if (*error) return 0;1212 if (operation >= 0) {1213 if (operation == 0 || operation + 1 == argc) {1214 *error = 1;1215 return 0;1216 }1217 int left_error = 0;1218 int right_error = 0;1219 const int left = evaluate(operation, argv, &left_error, depth + 1);1220 const int right = evaluate(argc - operation - 1, argv + operation + 1,1221 &right_error, depth + 1);1222 *error = left_error || right_error;1223 return left || right;1224 }1225 1226 operation = top_level_operator(argc, argv, "-a", error);1227 if (*error) return 0;1228 if (operation >= 0) {1229 if (operation == 0 || operation + 1 == argc) {1230 *error = 1;1231 return 0;1232 }1233 int left_error = 0;1234 int right_error = 0;1235 const int left = evaluate(operation, argv, &left_error, depth + 1);1236 const int right = evaluate(argc - operation - 1, argv + operation + 1,1237 &right_error, depth + 1);1238 *error = left_error || right_error;1239 return left && right;1240 }1241 1242 if (strcmp(argv[0], "!") == 0) {1243 return !evaluate(argc - 1, argv + 1, error, depth + 1);1244 }1245 if (argc == 2) {1246 int known;1247 const int result = unary(argv[0], argv[1], &known);1248 if (known) return result;1249 }1250 if (argc == 3) {1251 if (strcmp(argv[1], "=") == 0 || strcmp(argv[1], "==") == 0) return strcmp(argv[0], argv[2]) == 0;1252 if (strcmp(argv[1], "!=") == 0) return strcmp(argv[0], argv[2]) != 0;1253 int known = 0;1254 const int result = integer(argv[0], argv[1], argv[2], &known);1255 if (result < 0) { *error = 1; return 0; }1256 if (known) return result;1257 }1258 *error = 1;1259 return 0;1260 }1261 1262 int main(int argc, char **argv) {1263 argc--; argv++;1264 if (DOLLY_BRACKET) {1265 if (argc == 0 || strcmp(argv[argc - 1], "]") != 0) { fputs("[: missing ]\n", stderr); return 2; }1266 argc--;1267 }1268 int error = 0;1269 const int result = evaluate(argc, argv, &error, 0);1270 if (error) { fputs(DOLLY_BRACKET ? "[: unsupported expression\n" : "test: unsupported expression\n", stderr); return 2; }1271 return result ? 0 : 1;1272 }1273FILE /tmp/core-tools/bracket.c127412751276FILE /tmp/core-tools/mv.c12771278 1279128012811282128312841285 1286 static char *base_name_copy(const char *path) {1287 size_t length = strlen(path);1288 while (length > 1 && path[length - 1] == '/') length--;1289 size_t start = length;1290 while (start > 0 && path[start - 1] != '/') start--;1291 return strndup(path + start, length - start);1292 }1293 1294 static int move_one(const char *source, const char *destination,1295 int destination_is_directory) {1296 char *target = NULL;1297 if (destination_is_directory) {1298 char *name = base_name_copy(source);1299 if (name == NULL) {1300 fputs("mv: out of memory\n", stderr);1301 return 1;1302 }1303 const size_t destination_length = strlen(destination);1304 const size_t name_length = strlen(name);1305 const int slash = destination_length != 0 &&1306 destination[destination_length - 1] != '/';1307 if (destination_length > SIZE_MAX - name_length - (size_t)slash - 1) {1308 fprintf(stderr, "mv: %s/%s: path is too long\n", destination, name);1309 free(name);1310 return 1;1311 }1312 target = malloc(destination_length + (size_t)slash + name_length + 1);1313 if (target == NULL) {1314 fputs("mv: out of memory\n", stderr);1315 free(name);1316 return 1;1317 }1318 memcpy(target, destination, destination_length);1319 if (slash) target[destination_length] = '/';1320 memcpy(target + destination_length + (size_t)slash, name, name_length + 1);1321 free(name);1322 }1323 const char *resolved = target == NULL ? destination : target;1324 if (rename(source, resolved) != 0) {1325 fprintf(stderr, "mv: %s -> %s: %s\n", source, resolved, strerror(errno));1326 free(target);1327 return 1;1328 }1329 free(target);1330 return 0;1331 }1332 1333 int main(int argc, char **argv) {1334 int first = 1;1335 while (first < argc && argv[first][0] == '-') {1336 if (strcmp(argv[first], "--") == 0) {1337 first++;1338 break;1339 }1340 if (strcmp(argv[first], "--help") == 0) {1341 fputs("usage: mv [-f] [--] SOURCE... DESTINATION\n", stdout);1342 return 0;1343 }1344 if (strcmp(argv[first], "-f") != 0) {1345 fprintf(stderr, "mv: unsupported option %s\n", argv[first]);1346 return 2;1347 }1348 first++;1349 }1350 if (argc - first < 2) {1351 fputs("usage: mv [-f] SOURCE... DESTINATION\n", stderr);1352 return 2;1353 }1354 const char *destination = argv[argc - 1];1355 struct stat metadata;1356 const int destination_is_directory =1357 stat(destination, &metadata) == 0 && S_ISDIR(metadata.st_mode);1358 if (argc - first > 2 && !destination_is_directory) {1359 fprintf(stderr, "mv: %s is not a directory\n", destination);1360 return 1;1361 }1362 int status = 0;1363 for (int index = first; index < argc - 1; index++) {1364 if (move_one(argv[index], destination, destination_is_directory) != 0) {1365 status = 1;1366 }1367 }1368 return status;1369 }1370FILE /tmp/core-tools/cp.c137113721373 13741375137613771378137913801381138213831384 1385 static int recursive;1386 static int verbose;1387 1388 static char *base_name_copy(const char *path) {1389 size_t length = strlen(path);1390 while (length > 1 && path[length - 1] == '/') length--;1391 size_t start = length;1392 while (start > 0 && path[start - 1] != '/') start--;1393 return strndup(path + start, length - start);1394 }1395 1396 static char *join_path(const char *directory, const char *name) {1397 const size_t directory_length = strlen(directory);1398 const size_t name_length = strlen(name);1399 const int slash = directory_length != 0 && directory[directory_length - 1] != '/';1400 if (directory_length > SIZE_MAX - name_length - (size_t)slash - 1) {1401 errno = ENAMETOOLONG;1402 return NULL;1403 }1404 char *joined = malloc(directory_length + (size_t)slash + name_length + 1);1405 if (joined == NULL) return NULL;1406 memcpy(joined, directory, directory_length);1407 if (slash) joined[directory_length] = '/';1408 memcpy(joined + directory_length + (size_t)slash, name, name_length + 1);1409 return joined;1410 }1411 1412 static int copy_path(const char *source, const char *destination);1413 1414 static int copy_regular(const char *source, const char *destination) {1415 int input = open(source, O_RDONLY);1416 if (input < 0) {1417 fprintf(stderr, "cp: %s: %s\n", source, strerror(errno));1418 return 1;1419 }1420 struct stat source_metadata;1421 struct stat destination_metadata;1422 if (fstat(input, &source_metadata) != 0) {1423 fprintf(stderr, "cp: %s: %s\n", source, strerror(errno));1424 close(input);1425 return 1;1426 }1427 if (stat(destination, &destination_metadata) == 0 &&1428 source_metadata.st_dev == destination_metadata.st_dev &&1429 source_metadata.st_ino == destination_metadata.st_ino) {1430 fprintf(stderr, "cp: %s and %s are the same file\n", source, destination);1431 close(input);1432 return 1;1433 }1434 int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0666);1435 if (output < 0) {1436 fprintf(stderr, "cp: %s: %s\n", destination, strerror(errno));1437 close(input);1438 return 1;1439 }1440 1441 int status = 0;1442 unsigned char buffer[16384];1443 for (;;) {1444 ssize_t count = read(input, buffer, sizeof(buffer));1445 if (count < 0 && errno == EINTR) continue;1446 if (count < 0) {1447 fprintf(stderr, "cp: %s: %s\n", source, strerror(errno));1448 status = 1;1449 break;1450 }1451 if (count == 0) break;1452 size_t offset = 0;1453 while (offset < (size_t)count) {1454 ssize_t written = write(output, buffer + offset, (size_t)count - offset);1455 if (written < 0 && errno == EINTR) continue;1456 if (written <= 0) {1457 fprintf(stderr, "cp: %s: %s\n", destination,1458 written == 0 ? "short write" : strerror(errno));1459 status = 1;1460 break;1461 }1462 offset += (size_t)written;1463 }1464 if (status != 0) break;1465 }1466 if (close(input) != 0 && status == 0) status = 1;1467 if (close(output) != 0 && status == 0) {1468 fprintf(stderr, "cp: %s: %s\n", destination, strerror(errno));1469 status = 1;1470 }1471 return status;1472 }1473 1474 static int copy_link(const char *source, const char *destination,1475 off_t source_size) {1476 size_t capacity = source_size > 0 && (uintmax_t)source_size < SIZE_MAX - 11477 ? (size_t)source_size + 21478 : 256;1479 char *target = malloc(capacity);1480 if (target == NULL) {1481 fputs("cp: out of memory\n", stderr);1482 return 1;1483 }1484 const ssize_t length = readlink(source, target, capacity - 1);1485 if (length < 0 || (size_t)length >= capacity - 1) {1486 fprintf(stderr, "cp: %s: %s\n", source,1487 length < 0 ? strerror(errno) : "link target is too long");1488 free(target);1489 return 1;1490 }1491 target[length] = '\0';1492 if (unlink(destination) != 0 && errno != ENOENT) {1493 fprintf(stderr, "cp: %s: %s\n", destination, strerror(errno));1494 free(target);1495 return 1;1496 }1497 if (symlink(target, destination) != 0) {1498 fprintf(stderr, "cp: %s: %s\n", destination, strerror(errno));1499 free(target);1500 return 1;1501 }1502 free(target);1503 return 0;1504 }1505 1506 static int copy_directory(const char *source, const char *destination) {1507 if (!recursive) {1508 fprintf(stderr, "cp: %s is a directory (use -R)\n", source);1509 return 1;1510 }1511 struct stat destination_metadata;1512 int created = 0;1513 if (lstat(destination, &destination_metadata) != 0) {1514 if (errno != ENOENT || mkdir(destination, 0777) != 0) {1515 fprintf(stderr, "cp: %s: %s\n", destination, strerror(errno));1516 return 1;1517 }1518 created = 1;1519 } else if (!S_ISDIR(destination_metadata.st_mode)) {1520 fprintf(stderr, "cp: %s is not a directory\n", destination);1521 return 1;1522 }1523 1524 char source_resolved[PATH_MAX];1525 char destination_resolved[PATH_MAX];1526 if (realpath(source, source_resolved) != NULL &&1527 realpath(destination, destination_resolved) != NULL) {1528 const size_t source_length = strlen(source_resolved);1529 const int nested = strcmp(source_resolved, destination_resolved) == 0 ||1530 (strncmp(source_resolved, destination_resolved, source_length) == 0 &&1531 destination_resolved[source_length] == '/');1532 if (nested) {1533 fprintf(stderr, "cp: refusing to copy %s into itself at %s\n",1534 source, destination);1535 if (created) (void)rmdir(destination);1536 return 1;1537 }1538 }1539 1540 DIR *directory = opendir(source);1541 if (directory == NULL) {1542 fprintf(stderr, "cp: %s: %s\n", source, strerror(errno));1543 return 1;1544 }1545 int status = 0;1546 struct dirent *entry;1547 while ((entry = readdir(directory)) != NULL) {1548 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;1549 char *source_child = join_path(source, entry->d_name);1550 char *destination_child = join_path(destination, entry->d_name);1551 if (source_child == NULL || destination_child == NULL) {1552 fputs("cp: out of memory\n", stderr);1553 status = 1;1554 } else if (copy_path(source_child, destination_child) != 0) {1555 status = 1;1556 }1557 free(source_child);1558 free(destination_child);1559 if (status != 0) break;1560 }1561 if (closedir(directory) != 0 && status == 0) status = 1;1562 return status;1563 }1564 1565 static int copy_path(const char *source, const char *destination) {1566 struct stat metadata;1567 if (lstat(source, &metadata) != 0) {1568 fprintf(stderr, "cp: %s: %s\n", source, strerror(errno));1569 return 1;1570 }1571 struct stat destination_metadata;1572 if (lstat(destination, &destination_metadata) == 0 &&1573 metadata.st_dev == destination_metadata.st_dev &&1574 metadata.st_ino == destination_metadata.st_ino) {1575 fprintf(stderr, "cp: %s and %s are the same file\n", source, destination);1576 return 1;1577 }1578 if (verbose) printf("%s -> %s\n", source, destination);1579 if (S_ISREG(metadata.st_mode)) return copy_regular(source, destination);1580 if (S_ISDIR(metadata.st_mode)) return copy_directory(source, destination);1581 if (S_ISLNK(metadata.st_mode)) return copy_link(source, destination, metadata.st_size);1582 fprintf(stderr, "cp: %s: unsupported file type\n", source);1583 return 1;1584 }1585 1586 static int copy_operand(const char *source, const char *destination,1587 int destination_is_directory) {1588 char *target = NULL;1589 if (destination_is_directory) {1590 char *base = base_name_copy(source);1591 target = base == NULL ? NULL : join_path(destination, base);1592 free(base);1593 if (target == NULL) {1594 fputs("cp: out of memory\n", stderr);1595 return 1;1596 }1597 }1598 const int status = copy_path(source, target == NULL ? destination : target);1599 free(target);1600 return status;1601 }1602 1603 int main(int argc, char **argv) {1604 int first = 1;1605 for (; first < argc; first++) {1606 const char *argument = argv[first];1607 if (strcmp(argument, "--") == 0) {1608 first++;1609 break;1610 }1611 if (strcmp(argument, "--help") == 0) {1612 fputs("usage: cp [-Rrvf] [--] SOURCE... DESTINATION\n", stdout);1613 return 0;1614 }1615 if (argument[0] != '-' || argument[1] == '\0') break;1616 for (const char *option = argument + 1; *option != '\0'; option++) {1617 if (*option == 'R' || *option == 'r') recursive = 1;1618 else if (*option == 'v') verbose = 1;1619 else if (*option != 'f') {1620 fprintf(stderr, "cp: unsupported option -%c\n", *option);1621 return 2;1622 }1623 }1624 }1625 if (argc - first < 2) {1626 fputs("usage: cp [-Rrvf] [--] SOURCE... DESTINATION\n", stderr);1627 return 2;1628 }1629 1630 const char *destination = argv[argc - 1];1631 struct stat metadata;1632 const int destination_is_directory =1633 stat(destination, &metadata) == 0 && S_ISDIR(metadata.st_mode);1634 if (argc - first > 2 && !destination_is_directory) {1635 fprintf(stderr, "cp: %s is not a directory\n", destination);1636 return 1;1637 }1638 int status = 0;1639 for (int index = first; index < argc - 1; index++) {1640 if (copy_operand(argv[index], destination, destination_is_directory) != 0) {1641 status = 1;1642 }1643 }1644 return status;1645 }1646SLOP cc \1647 -O2 \1648 /tmp/core-tools/foreground.c \1649 -o /bin/foreground1650SLOP cc \1651 -O2 \1652 /tmp/core-tools/help.c \1653 -o /bin/help1654SLOP cc \1655 -O2 \1656 /tmp/core-tools/man.c \1657 -o /bin/man1658SLOP cc \1659 -O2 \1660 /tmp/core-tools/pwd.c \1661 -o /bin/pwd1662SLOP cc \1663 -O2 \1664 /tmp/core-tools/cd.c \1665 -o /bin/cd1666SLOP cc \1667 -O2 \1668 /tmp/core-tools/cat.c \1669 -o /bin/cat1670SLOP cc \1671 -O2 \1672 /tmp/core-tools/echo.c \1673 -o /bin/echo1674SLOP cc \1675 -O2 \1676 /tmp/core-tools/touch.c \1677 -o /bin/touch1678SLOP cc \1679 -O2 \1680 /tmp/core-tools/clear.c \1681 -o /bin/clear1682SLOP cc \1683 -O2 \1684 /tmp/core-tools/ls.c \1685 -o /bin/ls1686SLOP cc \1687 -O2 \1688 /tmp/core-tools/stat.c \1689 -o /bin/stat1690SLOP cc \1691 -O2 \1692 /tmp/core-tools/file.c \1693 -o /bin/file1694SLOP cc \1695 -O2 \1696 /tmp/core-tools/test.c \1697 -o /bin/test1698SLOP cc \1699 -O2 \1700 -I /tmp/core-tools \1701 /tmp/core-tools/bracket.c \1702 -o /bin/[1703SLOP cc \1704 -O2 \1705 /tmp/core-tools/mv.c \1706 -o /bin/mv1707SLOP cc \1708 -O2 \1709 /tmp/core-tools/cp.c \1710 -o /bin/cp1711SLOP cc \1712 -O2 \1713 /tmp/core-tools/mkdir.c \1714 -o /bin/mkdir1715SLOP cc \1716 -O2 \1717 /tmp/core-tools/rm.c \1718 -o /bin/rm1719EXPORTS TOOL mkdir1720EXPORTS TOOL rm1721EXPORTS TOOL foreground1722EXPORTS TOOL help1723EXPORTS TOOL man1724EXPORTS TOOL pwd1725EXPORTS TOOL cd1726EXPORTS TOOL cat1727EXPORTS TOOL echo1728EXPORTS TOOL touch1729EXPORTS TOOL clear1730EXPORTS TOOL ls1731EXPORTS TOOL stat1732EXPORTS TOOL file1733EXPORTS TOOL test1734EXPORTS TOOL [1735EXPORTS TOOL mv1736EXPORTS TOOL cp1737173817391740SLOP mkdir \1741 -p \1742 /usr/share/man/cat1 \1743 /usr/share/man/man11744SLOP for name in ar c++ cat cc cd clear cp dollyfile echo file foreground help ld ls man mkdir mv pwd rm slop stat touch; do \1745 "/bin/$name" --help > "/usr/share/man/cat1/$name.1"; \1746 done1747SLOP help >> /usr/share/man/cat1/slop.11748FILE /usr/share/man/cat1/test.11749 usage: test EXPRESSION1750 [ EXPRESSION ]1751 Exit 0 when EXPRESSION is true, 1 when it is false or missing, 2 when it is malformed.1752 -e FILE FILE exists (-r and -w are the same: there are no permission bits)1753 -f FILE, -x FILE FILE is a regular file1754 -d FILE FILE is a directory1755 -h FILE, -L FILE FILE is a symbolic link1756 -s FILE FILE is not empty1757 -n STRING STRING is not empty (so is a lone STRING)1758 -z STRING STRING is empty1759 A = B, A != B the strings are equal (== is the same), differ1760 M -eq N the integers are equal; also -ne -lt -le -gt -ge1761 ! E, E -a E, E -o E, ( E ) not, and, or, grouping1762 example: test -d /tmp && [ "$HOME" != / ]1763SLOP cp /usr/share/man/cat1/test.1 '/usr/share/man/cat1/[.1'17641765SLOP rm \1766 -rf \1767 /tmp/core-tools1768REQUIRES TOOL rm176917701771FILE /tmp/bootstrap/tar.c177217731774177517761777177817791780 1781 enum { BLOCK_SIZE = 512 };1782 1783 static int read_exact(int descriptor, void *bytes_value, size_t length) {1784 unsigned char *bytes = bytes_value;1785 while (length != 0) {1786 const ssize_t count = read(descriptor, bytes, length);1787 if (count < 0 && errno == EINTR) continue;1788 if (count < 0) return -1;1789 if (count == 0) {1790 errno = EIO;1791 return -1;1792 }1793 bytes += (size_t)count;1794 length -= (size_t)count;1795 }1796 return 0;1797 }1798 1799 static int write_exact(int descriptor, const void *bytes_value, size_t length) {1800 const unsigned char *bytes = bytes_value;1801 while (length != 0) {1802 const ssize_t count = write(descriptor, bytes, length);1803 if (count < 0 && errno == EINTR) continue;1804 if (count < 0) return -1;1805 if (count == 0) { errno = EIO; return -1; }1806 bytes += (size_t)count;1807 length -= (size_t)count;1808 }1809 return 0;1810 }1811 1812 static int all_zero(const unsigned char *bytes, size_t length) {1813 for (size_t index = 0; index < length; ++index) {1814 if (bytes[index] != 0) return 0;1815 }1816 return 1;1817 }1818 1819 static size_t bounded_length(const unsigned char *bytes, size_t capacity) {1820 size_t length = 0;1821 while (length < capacity && bytes[length] != '\0') ++length;1822 return length;1823 }1824 1825 static int parse_octal(const unsigned char *bytes, size_t length,1826 uint64_t *value_out) {1827 uint64_t value = 0;1828 size_t index = 0;1829 while (index < length && (bytes[index] == ' ' || bytes[index] == '\0')) ++index;1830 int digits = 0;1831 for (; index < length && bytes[index] != '\0' && bytes[index] != ' '; ++index) {1832 if (bytes[index] < '0' || bytes[index] > '7' || value > (UINT64_MAX >> 3)) {1833 return -1;1834 }1835 value = (value << 3) | (uint64_t)(bytes[index] - '0');1836 digits = 1;1837 }1838 if (!digits) return -1;1839 *value_out = value;1840 return 0;1841 }1842 1843 static int valid_member(const char *path) {1844 if (path[0] == '\0' || path[0] == '/' || strlen(path) > 4096 ||1845 strstr(path, "//") != NULL || strchr(path, '\\') != NULL) return 0;1846 const char *cursor = path;1847 while (*cursor != '\0') {1848 const char *end = strchr(cursor, '/');1849 const size_t length = end == NULL ? strlen(cursor) : (size_t)(end - cursor);1850 if ((length == 1 && cursor[0] == '.') ||1851 (length == 2 && cursor[0] == '.' && cursor[1] == '.')) return 0;1852 if (end == NULL) break;1853 cursor = end + 1;1854 }1855 return 1;1856 }1857 1858 static int mkdir_parents(const char *path, int include_last) {1859 char *copy = strdup(path);1860 if (copy == NULL) return -1;1861 if (!include_last) {1862 char *end = strrchr(copy, '/');1863 if (end != NULL) *end = '\0';1864 }1865 for (char *cursor = copy + 1;; ++cursor) {1866 if (*cursor != '/' && *cursor != '\0') continue;1867 const char saved = *cursor;1868 *cursor = '\0';1869 struct stat metadata;1870 int exists = stat(copy, &metadata) == 0;1871 if (exists && !S_ISDIR(metadata.st_mode)) {1872 errno = ENOTDIR;1873 free(copy);1874 return -1;1875 }1876 if (!exists) {1877 /*1878 * The version-0 dynamic-command libc surface does not consistently1879 * preserve errno across its WasmFS wrapper. Treat the filesystem as1880 * authoritative: after mkdir, verify the path instead of interpreting1881 * errno as the result.1882 */1883 (void)mkdir(copy, 0755);1884 if (stat(copy, &metadata) != 0 || !S_ISDIR(metadata.st_mode)) {1885 if (errno == 0) errno = EIO;1886 free(copy);1887 return -1;1888 }1889 }1890 *cursor = saved;1891 if (saved == '\0') break;1892 }1893 free(copy);1894 return 0;1895 }1896 1897 static int extract(const char *archive_path, const char *directory) {1898 int archive = strcmp(archive_path, "-") == 0 ? dup(STDIN_FILENO) : open(archive_path, O_RDONLY);1899 if (archive < 0) return -1;1900 unsigned char header[BLOCK_SIZE];1901 unsigned char data[BLOCK_SIZE];1902 int status = 0;1903 const char *stage = "read header";1904 char active_member[257] = "<header>";1905 for (;;) {1906 stage = "read header";1907 if (read_exact(archive, header, sizeof(header)) != 0) {1908 status = -1;1909 break;1910 }1911 if (all_zero(header, sizeof(header))) break;1912 uint64_t declared_checksum;1913 uint64_t size;1914 stage = "parse header";1915 if (parse_octal(header + 148, 8, &declared_checksum) != 0 ||1916 parse_octal(header + 124, 12, &size) != 0) {1917 errno = EINVAL;1918 status = -1;1919 break;1920 }1921 uint64_t checksum = 0;1922 for (size_t index = 0; index < sizeof(header); ++index) {1923 checksum += index >= 148 && index < 156 ? ' ' : header[index];1924 }1925 if (checksum != declared_checksum) {1926 errno = EBADMSG;1927 status = -1;1928 break;1929 }1930 char member[257];1931 const size_t name_length = bounded_length(header, 100);1932 const size_t prefix_length = bounded_length(header + 345, 155);1933 if (prefix_length != 0) {1934 if (prefix_length + name_length + 2 > sizeof(member)) {1935 errno = ENAMETOOLONG;1936 status = -1;1937 break;1938 }1939 memcpy(member, header + 345, prefix_length);1940 member[prefix_length] = '/';1941 memcpy(member + prefix_length + 1, header, name_length);1942 member[prefix_length + name_length + 1] = '\0';1943 } else {1944 memcpy(member, header, name_length);1945 member[name_length] = '\0';1946 }1947 memcpy(active_member, member, strlen(member) + 1);1948 stage = "validate path";1949 const unsigned char type = header[156];1950 const char *relative = member;1951 while (strncmp(relative, "./", 2) == 0) relative += 2;1952 const int root = *member != '\0' && (*relative == '\0' || strcmp(relative, ".") == 0);1953 if (root && type == '5' && size == 0) continue;1954 if (root || !valid_member(relative)) {1955 errno = EINVAL;1956 status = -1;1957 break;1958 }1959 const size_t output_length = strlen(directory) + strlen(relative) + 2;1960 char *output = malloc(output_length);1961 if (output == NULL) {1962 status = -1;1963 break;1964 }1965 snprintf(output, output_length, "%s%s%s", directory,1966 directory[strlen(directory) - 1] == '/' ? "" : "/", relative);1967 int target = -1;1968 if (type == '5') {1969 stage = "create directory";1970 if (size != 0 || mkdir_parents(output, 1) != 0) status = -1;1971 } else if (type == '\0' || type == '0') {1972 stage = "create parents";1973 if (size > SIZE_MAX || mkdir_parents(output, 0) != 0) {1974 status = -1;1975 } else {1976 stage = "open output";1977 target = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0666);1978 }1979 if (target < 0) {1980 status = -1;1981 }1982 } else {1983 errno = ENOTSUP;1984 status = -1;1985 }1986 uint64_t remaining = size;1987 while (status == 0 && remaining != 0) {1988 stage = "read data";1989 if (read_exact(archive, data, sizeof(data)) != 0) {1990 status = -1;1991 break;1992 }1993 const size_t count = remaining < sizeof(data) ? (size_t)remaining : sizeof(data);1994 if (target >= 0 && write_exact(target, data, count) != 0) {1995 stage = "write data";1996 status = -1;1997 }1998 remaining -= count;1999 }2000 if (target >= 0 && close(target) != 0 && status == 0) status = -1;2001 free(output);2002 if (status != 0) break;2003 }2004 if (status != 0) {2005 fprintf(stderr, "tar: %s at %s (errno %d)\n", stage, active_member, errno);2006 }2007 if (close(archive) != 0 && status == 0) status = -1;2008 return status;2009 }2010 2011 static void usage(FILE *stream) {2012 fputs("usage: tar -xf ARCHIVE [-C DIRECTORY]\nUse - as ARCHIVE to read stdin. This tar extracts only; git archive creates archives.\n", stream);2013 }2014 2015 int main(int argc, char **argv) {2016 if (argc == 2 && strcmp(argv[1], "--help") == 0) {2017 usage(stdout);2018 return 0;2019 }2020 if ((argc != 3 && argc != 5) || strcmp(argv[1], "-xf") != 0 ||2021 (argc == 5 && strcmp(argv[3], "-C") != 0)) {2022 usage(stderr);2023 return 2;2024 }2025 const char *directory = argc == 5 ? argv[4] : ".";2026 struct stat metadata;2027 if (stat(directory, &metadata) != 0 || !S_ISDIR(metadata.st_mode)) {2028 fprintf(stderr, "tar: %s: %s\n", directory, strerror(errno));2029 return 1;2030 }2031 if (extract(argv[2], directory) != 0) {2032 fprintf(stderr, "tar: %s: %s\n", argv[2], strerror(errno));2033 return 1;2034 }2035 return 0;2036 }2037SLOP cc \2038 /tmp/bootstrap/tar.c \2039 -o /bin/tar20402041EXPORTS TOOL tar20422043SLOP tar --help > /usr/share/man/cat1/tar.120442045SLOP rm \2046 -rf \2047 /tmp/bootstrap20482049205020512052REQUIRES TOOL cp2053REQUIRES TOOL mkdir2054REQUIRES TOOL tar20552056SOURCE /v0.1.2/dist/static/default/make-4.4.1.tar 5dce03b47a90e26891e789b3361481c65765e12bd8dc79ec0cf949336ef94616 /tmp/make-4.4.1.tar2057SOURCE /v0.1.2/dist/static/default/runtimes/make-amalgamation-dolly.c c37c85dd20c843b9bface7e12bd190f8d943b18c0b2b5e0341cfdc2cba11b4fd /tmp/make-4.4.1/make-amalgamation-dolly.c20582059SLOP tar \2060 -xf /tmp/make-4.4.1.tar \2061 -C /2062SLOP mkdir \2063 -p \2064 /usr/bin2065SLOP cc \2066 -c \2067 -O1 \2068 -std=gnu11 \2069 -DHAVE_CONFIG_H \2070 -I /usr/src/make \2071 -I /usr/src/make/src \2072 -I /usr/src/make/lib \2073 '-DLIBDIR="/usr/lib"' \2074 '-DLOCALEDIR="/usr/share/locale"' \2075 -DDOLLY_MAKE_PART=1 \2076 /tmp/make-4.4.1/make-amalgamation-dolly.c \2077 -o /tmp/make-4.4.1/part-1.o2078SLOP cc \2079 -c \2080 -O1 \2081 -std=gnu11 \2082 -DHAVE_CONFIG_H \2083 -I /usr/src/make \2084 -I /usr/src/make/src \2085 -I /usr/src/make/lib \2086 '-DLIBDIR="/usr/lib"' \2087 '-DLOCALEDIR="/usr/share/locale"' \2088 -DDOLLY_MAKE_PART=2 \2089 /tmp/make-4.4.1/make-amalgamation-dolly.c \2090 -o /tmp/make-4.4.1/part-2.o2091SLOP cc \2092 -c \2093 -O1 \2094 -std=gnu11 \2095 -DHAVE_CONFIG_H \2096 -I /usr/src/make \2097 -I /usr/src/make/src \2098 -I /usr/src/make/lib \2099 '-DLIBDIR="/usr/lib"' \2100 '-DLOCALEDIR="/usr/share/locale"' \2101 -DDOLLY_MAKE_PART=3 \2102 /tmp/make-4.4.1/make-amalgamation-dolly.c \2103 -o /tmp/make-4.4.1/part-3.o2104SLOP cc \2105 -c \2106 -O1 \2107 -std=gnu11 \2108 -DHAVE_CONFIG_H \2109 -I /usr/src/make \2110 -I /usr/src/make/src \2111 -I /usr/src/make/lib \2112 '-DLIBDIR="/usr/lib"' \2113 '-DLOCALEDIR="/usr/share/locale"' \2114 -DDOLLY_MAKE_PART=4 \2115 /tmp/make-4.4.1/make-amalgamation-dolly.c \2116 -o /tmp/make-4.4.1/part-4.o2117SLOP cc \2118 -c \2119 -O1 \2120 -std=gnu11 \2121 -DHAVE_CONFIG_H \2122 -I /usr/src/make \2123 -I /usr/src/make/src \2124 -I /usr/src/make/lib \2125 '-DLIBDIR="/usr/lib"' \2126 '-DLOCALEDIR="/usr/share/locale"' \2127 -DDOLLY_MAKE_PART=5 \2128 /tmp/make-4.4.1/make-amalgamation-dolly.c \2129 -o /tmp/make-4.4.1/part-5.o2130SLOP cc \2131 -c \2132 -O1 \2133 -std=gnu11 \2134 -DHAVE_CONFIG_H \2135 -I /usr/src/make \2136 -I /usr/src/make/src \2137 -I /usr/src/make/lib \2138 '-DLIBDIR="/usr/lib"' \2139 '-DLOCALEDIR="/usr/share/locale"' \2140 -DDOLLY_MAKE_PART=6 \2141 /tmp/make-4.4.1/make-amalgamation-dolly.c \2142 -o /tmp/make-4.4.1/part-6.o2143SLOP cc \2144 -c \2145 -O1 \2146 -std=gnu11 \2147 -DHAVE_CONFIG_H \2148 -I /usr/src/make \2149 -I /usr/src/make/src \2150 -I /usr/src/make/lib \2151 '-DLIBDIR="/usr/lib"' \2152 '-DLOCALEDIR="/usr/share/locale"' \2153 -DDOLLY_MAKE_PART=7 \2154 /tmp/make-4.4.1/make-amalgamation-dolly.c \2155 -o /tmp/make-4.4.1/part-7.o2156SLOP cc \2157 /tmp/make-4.4.1/part-1.o \2158 /tmp/make-4.4.1/part-2.o \2159 /tmp/make-4.4.1/part-3.o \2160 /tmp/make-4.4.1/part-4.o \2161 /tmp/make-4.4.1/part-5.o \2162 /tmp/make-4.4.1/part-6.o \2163 /tmp/make-4.4.1/part-7.o \2164 -o /usr/bin/make21652166EXPORTS TOOL make21672168SLOP make \2169 --version2170SLOP make --help > /usr/share/man/cat1/make.121712172FOLDER /usr/share/man21732174FILE /usr/share/licenses/make/COPYING21752176SLOP rm \2177 -rf \2178 /tmp/make-4.4.1 \2179 /tmp/make-4.4.1.tar \2180 /usr/src/make218121822183218421852186REQUIRES FOLDER process-sdk2187REQUIRES TOOL c++2188REQUIRES TOOL cc2189REQUIRES TOOL ar2190REQUIRES TOOL make21912192SOURCE /v0.1.2/dist/static/default/libcxx-headers.tar 5e558f6f862d4a3fb22cc3c5b62c5e2652bf699f176fa349eeecb85d1a946735 /tmp/cpp/libcxx-headers.tar2193SOURCE /v0.1.2/dist/static/default/libcxx.tar 729e5a55776e024b76619aca5416bd668f1e05a65873eeb3608efa287b51ccda /tmp/cpp/libcxx.tar2194SOURCE /v0.1.2/dist/static/default/licenses/libcxx 539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b /usr/share/licenses/libcxx/LICENSE2195SOURCE /v0.1.2/dist/static/default/licenses/libcxxabi e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c /usr/share/licenses/libcxxabi/LICENSE21962197SLOP tar \2198 -xf /tmp/cpp/libcxx-headers.tar \2199 -C /2200SLOP tar \2201 -xf /tmp/cpp/libcxx.tar \2202 -C /22032204FILE /tmp/cpp/Makefile2205 .RECIPEPREFIX := >2206 S := /tmp/cpp/system/lib2207 L := /usr/lib/dolly/process220822092210 FLAGS := -Oz -g -fno-pic -fvisibility=hidden -ffile-prefix-map=/tmp/cpp=/emsdk/emscripten -DNDEBUG2211 COMPILE := c++ $(FLAGS) -std=c++23 -D_LIBCPP_BUILDING_LIBRARY -I$S/libcxx/src2212 LIBCXX := $(filter-out %/int128_builtins.cpp,$(wildcard $S/libcxx/src/*.cpp $S/libcxx/src/filesystem/*.cpp \2213 $S/libcxx/src/ryu/*.cpp)) $(addprefix $S/libcxx/src/experimental/,chrono_exception.cpp keep.cpp log_hardening_failure.cpp)2214 LIBCXXABI := $(addprefix $S/libcxxabi/src/,abort_message.cpp cxa_aux_runtime.cpp cxa_default_handlers.cpp \2215 cxa_demangle.cpp cxa_guard.cpp cxa_handlers.cpp cxa_virtual.cpp cxa_thread_atexit.cpp fallback_malloc.cpp \2216 stdlib_new_delete.cpp stdlib_exception.cpp stdlib_stdexcept.cpp stdlib_typeinfo.cpp private_typeinfo.cpp \2217 cxa_exception_js_utils.cpp cxa_exception_storage.cpp cxa_exception.cpp cxa_personality.cpp)2218 objects = $(sort $(addprefix /tmp/cpp/$1/,$(addsuffix .o,$(basename $(notdir $2)))))2219 all: $L/libc++-ww-wasmexcept.a $L/libc++abi-ww-wasmexcept.a $L/libunwind-ww-wasmexcept.a2220 $L/libc++-ww-wasmexcept.a: $(call objects,libc++,$(LIBCXX))2221 $L/libc++abi-ww-wasmexcept.a: $(call objects,libc++abi,$(LIBCXXABI))2222 $L/libunwind-ww-wasmexcept.a: /tmp/cpp/libunwind/Unwind-wasm.o2223 $L/%.a:2224 >ar rc $@ $^2225 define compile2226 /tmp/cpp/$1/$(basename $(notdir $2)).o: $22227 >$3 -c $$< -o $$@2228 endef2229 $(foreach source,$(LIBCXX),$(eval $(call compile,libc++,$(source),$(COMPILE) -DLIBCXX_BUILDING_LIBCXXABI=1 \2230 -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -DLIBC_NAMESPACE=__llvm_libc -I$S/llvm-libc)))2231 $(foreach source,$(LIBCXXABI),$(eval $(call compile,libc++abi,$(source),$(COMPILE) -D_LIBCXXABI_USE_FUTEX \2232 -D_LIBCXXABI_BUILDING_LIBRARY -DLIBCXXABI_NON_DEMANGLING_TERMINATE -I$S/libunwind/include)))2233 $(eval $(call compile,libunwind,$S/libunwind/src/Unwind-wasm.c,cc $(FLAGS) -D_LIBUNWIND_HIDE_SYMBOLS \2234 -I$S/libunwind/include))2235SLOP mkdir /tmp/cpp/libc++ /tmp/cpp/libc++abi /tmp/cpp/libunwind2236SLOP make -C /tmp/cpp -j222372238EXPORTS HEADER /usr/include/c++/v12239EXPORTS LIB c++ /usr/lib/dolly/process/libc++-ww-wasmexcept.a2240EXPORTS LIB c++abi /usr/lib/dolly/process/libc++abi-ww-wasmexcept.a2241EXPORTS ENV CXX c++22422243FILE /usr/share/licenses/libcxx/LICENSE2244FILE /usr/share/licenses/libcxxabi/LICENSE22452246SLOP rm \2247 -rf \2248 /tmp/cpp2249