/* * 975_dirs_toolong_run — F14 #69: dirs silently truncated an over-long * composed path and then mkdir'd the WRONG directory, returning it rc=0. * The fix (dirs.ww build()) precomputes the composition length and * rt_aborts loudly when it won't fit the 256B pathbuf — mirroring Hare's * path::push too_long → `!` abort (ref/hare/dirs/xdg.ha). Both stages * share the lib, so this is a stdlib behavior fix, not a stage divergence; * the test still runs both driver twins for parity. * * One fixture program (dirs.config("prog"), print len) built per stage, * then run under two HOME states: * - a short HOME → exit 0 (normal path intact, no regression); * - a ~260-byte HOME → non-zero exit (loud abort) AND no stray * directory created on disk. */ #include #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return -1; } static const char *PROG = "package main;\n" "import dirs;\n" "import fmt;\n" "export fn main() int = {\n" " let d = dirs.config(\"prog\");\n" " fmt.println(d.len: i64);\n" " return 0;\n" "};\n"; /* run_stage — build PROG with `driver`, then exercise the two HOME states. * Returns 0 on success, non-zero on any mismatch. */ static int run_stage(const char *name, const char *driver) { char src[64], tmpdir[64], cmd[2048]; snprintf(src, sizeof src, "/tmp/dtl_%d.ww", getpid()); snprintf(tmpdir, sizeof tmpdir, "/tmp/dtl_%d_d", getpid()); FILE *f = fopen(src, "wb"); if (!f) return 1; fputs(PROG, f); fclose(f); mkdir(tmpdir, 0755); snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", tmpdir, driver, src); if (runwait(cmd) != 0) { fprintf(stderr, "dirs_toolong[%s]: build failed\n", name); unlink(src); rmdir(tmpdir); return 1; } char outbin[128]; snprintf(outbin, sizeof outbin, "%s/dtl_%d", tmpdir, getpid()); int fail = 0; /* short HOME — the normal path, must still exit 0. */ snprintf(cmd, sizeof cmd, "env -u XDG_CONFIG_HOME HOME=/tmp/dtl_%d_home %s >/dev/null 2>&1", getpid(), outbin); if (runwait(cmd) != 0) { fprintf(stderr, "dirs_toolong[%s]: short HOME did not exit 0\n", name); fail = 1; } /* ~260-byte HOME — must abort loudly (non-zero) and create no dir. */ char longhome[512]; int p = snprintf(longhome, sizeof longhome, "/tmp/dtl_%d_long_", getpid()); for (int i = 0; i < 260 && p < (int)sizeof longhome - 1; i++) longhome[p++] = 'a'; longhome[p] = '\0'; snprintf(cmd, sizeof cmd, "env -u XDG_CONFIG_HOME HOME=%s %s >/dev/null 2>&1", longhome, outbin); if (runwait(cmd) == 0) { fprintf(stderr, "dirs_toolong[%s]: long HOME exited 0, " "expected a loud abort\n", name); fail = 1; } /* the truncated wrong directory must NOT have been created. */ snprintf(cmd, sizeof cmd, "test -e %s", longhome); if (runwait(cmd) == 0) { fprintf(stderr, "dirs_toolong[%s]: stray directory created\n", name); fail = 1; snprintf(cmd, sizeof cmd, "rm -rf %s", longhome); (void)system(cmd); } unlink(src); unlink(outbin); rmdir(tmpdir); snprintf(cmd, sizeof cmd, "rm -rf /tmp/dtl_%d_home", getpid()); (void)system(cmd); return fail; } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[1024]; if (bin[0] != '/') { char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } char cdrv[1024], wdrv[1024]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); int fail = 0; fail += run_stage("cstage", cdrv); if (access(wdrv, X_OK) == 0) fail += run_stage("wwstage", wdrv); else fprintf(stderr, "dirs_toolong: skip wwstage (no %s)\n", wdrv); if (fail) { fprintf(stderr, "dirs_toolong: %d stage(s) failed\n", fail); return 1; } printf("dirs_toolong: ok\n"); return 0; }