1DOLLY 72TOOLCHAIN system-build3REQUIRES HOST runtime@04REQUIRES HOST http@056# This is the first step. It has no requirements: its exact compiler commands7# and C headers are the externally supplied bootstrap seed. How those headers8# eventually map to the lower-level machine contract is deliberately open.9SOURCE /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 libc      /usr/include38EXPORTS HEADER toolchain /usr/include/dolly/toolchain.h39EXPORTS HEADER runtime   /usr/include/dolly/runtime.h40EXPORTS HEADER process   /usr/include/dolly/process.h41EXPORTS HEADER http      /usr/include/dolly/http.h42EXPORTS HEADER http-abi /usr/include/dolly/http-abi.h43EXPORTS HEADER display   /usr/include/dolly/display.h44EXPORTS HEADER display-abi /usr/include/dolly/display-abi.h45EXPORTS HEADER input     /usr/include/dolly/input.h46EXPORTS HEADER input-abi /usr/include/dolly/input-abi.h47EXPORTS HEADER download  /usr/include/dolly/download.h48EXPORTS HEADER download-abi /usr/include/dolly/download-abi.h4950EXPORTS HEADER host /usr/include/dolly/host.h51EXPORTS HEADER host-abi /usr/include/dolly/host-abi.h52EXPORTS HEADER gpu /usr/include/dolly/gpu.h53EXPORTS HEADER gpu-abi /usr/include/dolly/gpu-abi.h54EXPORTS HEADER audio /usr/include/dolly/audio.h55EXPORTS HEADER audio-abi /usr/include/dolly/audio-abi.h56EXPORTS HEADER upload /usr/include/dolly/upload.h57EXPORTS HEADER upload-abi /usr/include/dolly/upload-abi.h58EXPORTS HEADER threads /usr/include/dolly/threads.h59EXPORTS HEADER threads-abi /usr/include/dolly/threads-abi.h60EXPORTS HEADER dso /usr/include/dolly/dso.h61EXPORTS HEADER dso-abi /usr/include/dolly/dso-abi.h62EXPORTS HEADER sockets /usr/include/dolly/sockets.h63EXPORTS HEADER sockets-abi /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.a6869# These are the complete externally seeded compiler dependencies, not ambient70# additions to every boot. Images retain them by re-exporting these objects.71EXPORTS FOLDER process-sdk       /usr/lib/dolly/process72EXPORTS FOLDER clang-headers     /usr/lib/clang/24/include73EXPORTS FILE   compiler          /usr/libexec/dolly/process-bin/compiler74EXPORTS FILE   kernel-plugin-abi /usr/lib/dolly/dolly-kernel-plugin-0.wasm7576# Every program links the process libc: Emscripten's musl.77SOURCE /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/LICENSE8182# cc, c++, ld and ar forward to the seed compiler. Only that compiler exists at83# this point, so COMPILEC builds them; every later recipe step uses SLOP.84FILE /tmp/process-tools/cc.c85    #include <dolly/toolchain.h>86    int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_C); }87FILE /tmp/process-tools/cxx.c88    #include <dolly/toolchain.h>89    int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_CXX); }90FILE /tmp/process-tools/ld.c91    #include <dolly/toolchain.h>92    int main(int argc, char **argv) { return dolly_toolchain_proxy(argc, argv, DOLLY_TOOLCHAIN_LD); }93FILE /tmp/process-tools/ar.c94    #include <dolly/toolchain.h>95    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/bin104105# These programs are linked against the seeded process adapter, so their106# complete input identity is the image build ID rather than this recipe107# alone. They are still validated as dolly-process-0 executables when loaded.108# /bin/dollyfile is the engine running this recipe; the bootstrap compiles it.109EXPORTS TOOL cc110EXPORTS TOOL c++111EXPORTS TOOL ld112EXPORTS TOOL ar113EXPORTS TOOL dollyfile114# Slop runs every SLOP step, so it is built before any shell exists: COMPILEC115# runs the seed compiler alone.116REQUIRES 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   cc125126# Small Dolly-owned commands live directly in the module. The root's module127# hash authenticates their source; each TOOL export names the compiled result.128FILE /tmp/core-tools/mkdir.c129    #include <errno.h>130    #include <stdio.h>131    #include <string.h>132    #include <sys/stat.h>133    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.c200    #define _POSIX_C_SOURCE 200809L201    202    #include <dirent.h>203    #include <errno.h>204    #include <stdio.h>205    #include <stdlib.h>206    #include <string.h>207    #include <sys/stat.h>208    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.c341    #include <errno.h>342    #include <stdio.h>343    #include <string.h>344    #include <dolly/runtime.h>345    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.c376    #include <stdio.h>377    #include <string.h>378    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.c396    #define _POSIX_C_SOURCE 200809L397    #include <stdio.h>398    #include <stdlib.h>399    #include <string.h>400    #include <sys/wait.h>401    #include <unistd.h>402    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.c442    #include <errno.h>443    #include <stdio.h>444    #include <string.h>445    #include <unistd.h>446    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.c467    #include <errno.h>468    #include <stdio.h>469    #include <stdlib.h>470    #include <string.h>471    #include <unistd.h>472    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.c497    #include <errno.h>498    #include <stdio.h>499    #include <string.h>500    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.c581    #include <stdio.h>582    #include <string.h>583    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.c605    #define _POSIX_C_SOURCE 200809L606    #include <ctype.h>607    #include <errno.h>608    #include <stdio.h>609    #include <string.h>610    #include <time.h>611    #include <utime.h>612    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.c722    #include <stdio.h>723    #include <string.h>724    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.c739    #define _POSIX_C_SOURCE 200809L740    741    #include <dirent.h>742    #include <errno.h>743    #include <stdint.h>744    #include <stdio.h>745    #include <stdlib.h>746    #include <string.h>747    #include <sys/stat.h>748    #include <time.h>749    #include <unistd.h>750    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.c957    #define _POSIX_C_SOURCE 200809L958    959    #include <errno.h>960    #include <stdio.h>961    #include <string.h>962    #include <sys/stat.h>963    #include <time.h>964    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.c1032    #define _POSIX_C_SOURCE 200809L1033    1034    #include <ctype.h>1035    #include <errno.h>1036    #include <stdio.h>1037    #include <string.h>1038    #include <sys/stat.h>1039    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.c1106    #define _POSIX_C_SOURCE 200809L1107    1108    #include <errno.h>1109    #include <stdio.h>1110    #include <stdlib.h>1111    #include <string.h>1112    #include <sys/stat.h>1113    1114    #ifndef DOLLY_BRACKET1115    #define DOLLY_BRACKET 01116    #endif1117    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.c1274    #define DOLLY_BRACKET 11275    #include "test.c"1276FILE /tmp/core-tools/mv.c1277    #define _POSIX_C_SOURCE 200809L1278    1279    #include <errno.h>1280    #include <stdint.h>1281    #include <stdio.h>1282    #include <stdlib.h>1283    #include <string.h>1284    #include <sys/stat.h>1285    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.c1371    #define _POSIX_C_SOURCE 200809L1372    #define _XOPEN_SOURCE 7001373    1374    #include <dirent.h>1375    #include <errno.h>1376    #include <fcntl.h>1377    #include <limits.h>1378    #include <stdint.h>1379    #include <stdio.h>1380    #include <stdlib.h>1381    #include <string.h>1382    #include <sys/stat.h>1383    #include <unistd.h>1384    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 cp17371738# A page is a file under /usr/share/man. One of Dolly's own commands has a1739# single text: the page is what NAME --help prints, captured when it is built.1740SLOP 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   rm17691770# Dolly's deliberately small ustar extractor, embedded directly in the module.1771FILE /tmp/bootstrap/tar.c1772    #include <errno.h>1773    #include <fcntl.h>1774    #include <stdint.h>1775    #include <stdio.h>1776    #include <stdlib.h>1777    #include <string.h>1778    #include <sys/stat.h>1779    #include <unistd.h>1780    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/bootstrap2048# The module owns Make's build-time command vocabulary. `cp` is required here,2049# rather than by consumers which merely invoke Make, because real Make recipes2050# rely on it. The Dollyfile checker documents this build contract; it is not a2051# runtime sandbox for Make or Slop.2052REQUIRES 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.12171# FOLDER keeps the members a directory has at this row: the last page is written.2172FOLDER /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/make2181# libc++, libc++abi and libunwind are Emscripten's copy, compiled here from its2182# pinned source with the flags of its own build (tools/system_libs.py,2183# -ww-wasmexcept; its -mt-wasmexcept build is the same bytes) for the headers2184# installed beside them. Every C++ link takes them, including explicit2185# -lc++/-lc++abi; the links above this point are C and need none.2186REQUIRES 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/process2208    # Static code with hidden symbols, as for a program. The directory is the one2209    # Emscripten's build names, so the code and data are its archives' bytes.2210    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 cpp    /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