X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/runlisp/blobdiff_plain/8996f767e047eefa8af4d01b1434b54f4c169b79..10427eb21d77a0edeb2f17e434515b91b420cdfb:/dump-runlisp-image.c diff --git a/dump-runlisp-image.c b/dump-runlisp-image.c index 1c6cb55..50bfb3f 100644 --- a/dump-runlisp-image.c +++ b/dump-runlisp-image.c @@ -23,7 +23,7 @@ * along with Runlisp. If not, see . */ -/*----- Header files ------------------------------------------------------*/ +/*----- Header files ---------------------------------------------------------*/ #include "config.h" @@ -70,6 +70,7 @@ struct linebuf { /* Job-state constants. */ enum { JST_READY, /* not yet started */ + JST_DELETE, /* just delete the image file */ JST_RUN, /* currently running */ JST_DEAD, /* process exited */ JST_NSTATE @@ -79,6 +80,7 @@ enum { struct job { struct treap_node _node; /* treap intrusion */ struct job *next; /* next job in whichever list */ + unsigned op; /* operation (`JOP_...') */ struct argv av; /* argument vector to execute */ char *imgnew, *imgout; /* staging and final output files */ unsigned st; /* job state (`JST_...') */ @@ -90,8 +92,12 @@ struct job { #define JOB_NAME(job) TREAP_NODE_KEY(job) #define JOB_NAMELEN(job) TREAP_NODE_KEYLEN(job) -static struct treap jobs = TREAP_INIT; /* Lisp systems scheduled to dump */ -static struct job *job_ready, *job_run, *job_dead; /* list jobs by state */ +static struct treap jobs = TREAP_INIT, /* Lisp systems seen so far */ + good = TREAP_INIT; /* files ok to be in image dir */ +static struct job /* lists of jobs by state */ + *job_ready, **job_ready_tail = &job_ready, /* some have tail pointers... */ + *job_delete, **job_delete_tail = &job_delete, + *job_run, *job_dead; /* ... and some don't */ static unsigned nrun, maxrun = 1; /* running and maximum job counts */ static int rc = 0; /* code that we should return */ static int nullfd; /* file descriptor for `/dev/null' */ @@ -108,6 +114,9 @@ static unsigned flags = 0; /* flags for the application */ #define AF_ALL 0x0008u /* dump all known Lisps */ #define AF_FORCE 0x0010u /* dump even if images exist */ #define AF_CHECKINST 0x0020u /* check Lisp exists before dump */ +#define AF_REMOVE 0x0040u /* remove selected Lisp images */ +#define AF_CLEAN 0x0080u /* remove other Lisp images */ +#define AF_JUNK 0x0100u /* remove unrecognized files */ /*----- Miscellany --------------------------------------------------------*/ @@ -490,60 +499,112 @@ static void prefix_lines(struct job *job, struct linebuf *buf, char marker) /*----- Job management ----------------------------------------------------*/ -/* Add a new job to the `ready' queue. +/* Consider a Lisp system description and maybe add a job to the right queue. * - * The job will be to dump the Lisp system with the given LEN-byte NAME. On - * entry, *TAIL_INOUT should point to the `next' link of the last node in the - * list (or the list head pointer), and will be updated on exit. + * The Lisp system is described by the configuration section SECT. Most of + * the function is spent on inspecting this section for suitability and + * deciding what to do about it. * - * This function reports (fatal) errors for most kinds of problems. If - * `JF_QUIET' is set in F then silently ignore a well-described Lisp system - * which nonetheless isn't suitable. (This is specifically intended for the - * case where we try to dump all known Lisp systems, but some don't have a - * `dump-image' command.) + * The precise behaviour depends on F, which should be the bitwise-OR of a + * `JQ_...' constant and zero or more flags, as follows. + * + * * The bits covered by `JMASK_QUEUE' identify which queue the job should + * be added to if the section defines a cromulent Lisp system: + * + * -- `JQ_NONE' -- don't actually make a job at all; + * -- `JQ_READY' -- add the Lisp to the `job_ready' queue, so we'll; or + * -- `JQ_DELETE' -- add the Lisp to the `job_delete' queue. + * + * * `JF_PICKY': The user identified this Lisp system explicitly, so + * complain if the configuration section doesn't look right. This is + * clear if the caller is just enumerating all of the configuration + * sections: without this feature, we'd be checking everything twice, + * which (a) is inefficient, and -- more importantly -- (b) could lead to + * problems if the two checks are inconsistent. + * + * * `JF_CHECKINST': Ignore this Lisp if `AF_CHECKINST' is set and it's not + * actually installed. (This is usually set for `JQ_READY' calls, so + * that we don't try to dump Lisps which aren't there, but clear for + * `JQ_DELETE' calls so that we clear out Lisps which have gone away.) + * + * * `JF_CHECKEXIST': Ignore this Lisp if its image file already exists. + * + * * `JF_NOTICE': Record the Lisp's image basename in the `good' treap so + * that we can identify everything else we find in the image directory as + * junk. */ -#define JF_QUIET 1u -static void add_job(struct job ***tail_inout, unsigned f, - const char *name, size_t len) +#define JMASK_QUEUE 3u /* which queue to add good Lisp to */ +#define JQ_NONE 0u /* don't add to any queue */ +#define JQ_READY 1u /* `job_ready' */ +#define JQ_DELETE 2u /* `job_delete' */ +#define JF_PICKY 4u /* lose if section isn't Lisp defn */ +#define JF_CHECKINST 8u /* maybe check Lisp is installed */ +#define JF_CHECKEXIST 16u /* skip if image already exists */ +#define JF_NOTICE 32u /* record Lisp's image basename */ + +#define JADD_NAMED (JQ_READY | JF_PICKY | JF_CHECKINST) +#define JADD_DEFAULT (JQ_READY | JF_CHECKINST) +#define JADD_CLEANUP (JQ_DELETE) +#define JADD_NOTICE (JQ_NONE) +static void add_job(unsigned f, struct config_section *sect) { - struct job *job; - struct treap_path path; - struct config_section *sect; + const char *name; + struct job *job, ***tail; + struct treap_path path, jobpath; struct config_var *dumpvar, *cmdvar, *imgvar; + struct treap_node *n; struct dstr d = DSTR_INIT; struct argv av = ARGV_INIT; char *imgnew = 0, *imgout = 0; - size_t i; + size_t i, len; unsigned fef; - /* Check to see whether this Lisp system is already queued up. */ - job = treap_probe(&jobs, name, len, &path); + /* We'll want the section's name for all sorts of things. */ + name = CONFIG_SECTION_NAME(sect); + len = CONFIG_SECTION_NAMELEN(sect); + + /* Check to see whether this Lisp system is already queued up. + * + * We'll get around to adding the new job node to the treap right at the + * end, so use a separate path object to keep track of where to put it. + */ + job = treap_probe(&jobs, name, len, &jobpath); if (job) { - if (verbose >= 2) { + if ((f&JF_PICKY) && verbose >= 1) moan("ignoring duplicate Lisp `%s'", JOB_NAME(job)); - return; - } + goto end; } - /* Find the configuration for this Lisp system and check that it can be - * dumped. + /* Check that the section defines a Lisp, and that it can be dumped. + * + * It's not obvious that this is right. Maybe there should be some + * additional flag so that we don't check dumpability if we're planning to + * delete the image. But it /is/ right: since the thing which tells us + * whether we can dump is that the section tells us the image's name, if + * it can't be dumped then we won't know what file to delete! So we have + * no choice. */ - sect = config_find_section_n(&config, 0, name, len); - if (!sect) lose("unknown Lisp implementation `%.*s'", (int)len, name); - name = CONFIG_SECTION_NAME(sect); - dumpvar = config_find_var(&config, sect, 0, "dump-image"); - if (!dumpvar) { - if (!(f&JF_QUIET)) - lose("don't know how to dump images for Lisp implementation `%s'", - name); + if (!config_find_var(&config, sect, CF_INHERIT, "run-script")) { + if (f&JF_PICKY) lose("unknown Lisp implementation `%s'", name); + else if (verbose >= 3) moan("skipping non-Lisp section `%s'", name); + goto end; + } + imgvar = config_find_var(&config, sect, CF_INHERIT, "image-file"); + if (!imgvar) { + if (f&JF_PICKY) + lose("Lisp implementation `%s' doesn't use custom images", name); + else if (verbose >= 3) + moan("skipping Lisp `%s': no custom image support", name); goto end; } /* Check that the other necessary variables are present. */ - imgvar = config_find_var(&config, sect, 0, "image-file"); - if (!imgvar) lose("variable `image-file' not defined for Lisp `%s'", name); - cmdvar = config_find_var(&config, sect, 0, "command"); - if (!cmdvar) lose("variable `command' not defined for Lisp `%s'", name); + dumpvar = config_find_var(&config, sect, CF_INHERIT, "dump-image"); + if (!dumpvar) + lose("variable `dump-image' not defined for Lisp `%s'", name); + cmdvar = config_find_var(&config, sect, CF_INHERIT, "command"); + if (!cmdvar) + lose("variable `command' not defined for Lisp `%s'", name); /* Build the job's command line. */ config_subst_split_var(&config, sect, dumpvar, &av); @@ -557,17 +618,35 @@ static void add_job(struct job ***tail_inout, unsigned f, * because that would cause us to spam the user with redundant * diagnostics.) */ - if (flags&AF_CHECKINST) { + if ((f&JF_CHECKINST) && (flags&AF_CHECKINST)) { dstr_reset(&d); - fef = (verbose >= 2 ? FEF_VERBOSE : 0); + fef = (verbose >= 3 ? FEF_VERBOSE : 0); config_subst_var(&config, sect, cmdvar, &d); - if (!found_in_path_p(d.p, fef) || - (STRCMP(d.p, !=, av.v[0]) && !found_in_path_p(av.v[0], fef))) { - if (verbose >= 2) moan("skipping Lisp implementation `%s'", name); + if (!found_in_path_p(d.p, fef)) { + if (verbose >= 3) + moan("skipping Lisp `%s': can't find Lisp command `%s'", + name, d.p); + goto end; + } + if (STRCMP(d.p, !=, av.v[0]) && !found_in_path_p(av.v[0], fef)) { + moan("skipping Lisp `%s': can't find dump command `%s'", + av.v[0], d.p); goto end; } } + /* If we're supposed to, then notice that this is the name of a good Lisp + * image. + */ + if (f&JF_NOTICE) { + dstr_reset(&d); config_subst_var(&config, sect, imgvar, &d); + n = treap_probe(&good, d.p, d.len, &path); + if (!n) { + n = xmalloc(sizeof(*n)); + treap_insert(&good, &path, n, d.p, d.len); + } + } + /* Collect the output image file names. */ imgnew = config_subst_string_alloc(&config, sect, "", "${@image-new}"); @@ -577,11 +656,11 @@ static void add_job(struct job ***tail_inout, unsigned f, /* If we're supposed to check whether the image file exists, then we should * do that. */ - if (!(flags&AF_FORCE)) { + if ((f&JF_CHECKEXIST) && !(flags&AF_FORCE)) { if (!access(imgout, F_OK)) { - if (verbose >= 2) - moan("image `%s' already exists: skipping `%s'", d.p, name); - goto end; + if (verbose >= 3) + moan("skipping Lisp `%s': image `%s' already exists", name, imgout); + f = (f&~JMASK_QUEUE) | JQ_NONE; } } @@ -589,15 +668,21 @@ static void add_job(struct job ***tail_inout, unsigned f, * of the list. (Steal the command-line vector so that we don't try to * free it during cleanup.) */ + switch (f&JMASK_QUEUE) { + case JQ_NONE: tail = 0; break; + case JQ_READY: tail = &job_ready_tail; break; + case JQ_DELETE: tail = &job_delete_tail; break; + default: assert(0); + } job = xmalloc(sizeof(*job)); job->st = JST_READY; - job->kid = -1; + job->kid = -1; job->log = 0; job->out.fd = -1; job->out.buf = 0; job->err.fd = -1; job->err.buf = 0; job->av = av; argv_init(&av); job->imgnew = imgnew; job->imgout = imgout; imgnew = imgout = 0; - treap_insert(&jobs, &path, &job->_node, name, len); - **tail_inout = job; *tail_inout = &job->next; + treap_insert(&jobs, &jobpath, &job->_node, name, len); + if (tail) { **tail = job; *tail = &job->next; } end: /* All done. Cleanup time. */ @@ -606,6 +691,20 @@ end: dstr_release(&d); argv_release(&av); } +/* As `add_job' above, but look the Lisp implementation up by name. + * + * The flags passed to `add_job' are augmented with `JF_PICKY' because this + * is an explicitly-named Lisp implementation. + */ +static void add_named_job(unsigned f, const char *name, size_t len) +{ + struct config_section *sect; + + sect = config_find_section_n(&config, 0, name, len); + if (!sect) lose("unknown Lisp implementation `%.*s'", (int)len, name); + add_job(f | JF_PICKY, sect); +} + /* Free the JOB and all the resources it holds. * * Close the pipes; kill the child process. Everything must go. @@ -613,6 +712,7 @@ end: static void release_job(struct job *job) { size_t i; + struct job *j; if (job->kid > 0) kill(job->kid, SIGKILL); /* ?? */ if (job->log && job->log != stdout) fclose(job->log); @@ -621,6 +721,7 @@ static void release_job(struct job *job) argv_release(&job->av); free(job->out.buf); if (job->out.fd >= 0) close(job->out.fd); free(job->err.buf); if (job->err.fd >= 0) close(job->err.fd); + j = treap_remove(&jobs, JOB_NAME(job), JOB_NAMELEN(job)); assert(j == job); free(job); } @@ -761,6 +862,22 @@ static void start_jobs(void) job = job_ready; job_ready = job->next; p_out[0] = p_out[1] = p_err[0] = p_err[1] = -1; + /* If we're not actually going to do anything, now is the time to not do + * that. + */ + if (flags&AF_DRYRUN) { + if (try_exec(&job->av, + TEF_DRYRUN | + (verbose >= 2 && !(flags&AF_CHECKINST) ? + TEF_VERBOSE : 0))) + rc = 127; + else if (verbose >= 2) + printf("%-13s > not dumping `%s' (dry run)\n", + JOB_NAME(job), JOB_NAME(job)); + release_job(job); + continue; + } + /* Make a temporary subdirectory for this job to use. */ dstr_reset(&d); dstr_putf(&d, "%s/%s", tmpdir, JOB_NAME(job)); if (mkdir(d.p, 0700)) { @@ -926,6 +1043,10 @@ static void run_jobs(void) * output. */ for (link = &job_dead, job = *link; job; job = next) { + if (job->out.fd >= 0 && FD_ISSET(job->out.fd, &fd_in)) + prefix_lines(job, &job->out, '|'); + if (job->err.fd >= 0 && FD_ISSET(job->err.fd, &fd_in)) + prefix_lines(job, &job->err, '*'); next = job->next; if (job->out.fd >= 0 || job->err.fd >= 0) link = &job->next; else { *link = next; finish_job(job); } @@ -942,7 +1063,7 @@ static void version(FILE *fp) static void usage(FILE *fp) { fprintf(fp, "\ -usage: %s [-afnqv] [-c CONF] [-o [SECT:]VAR=VAL]\n\ +usage: %s [-RUadfinqrv] [+RUdfinr] [-c CONF] [-o [SECT:]VAR=VAL]\n\ [-O FILE|DIR] [-j NJOBS] [LISP ...]\n", progname); } @@ -966,13 +1087,33 @@ Configuration:\n\ \n\ Image dumping:\n\ -O, --output=FILE|DIR Store image(s) in FILE or DIR.\n\ - -a, --all-configured Dump all implementations configured.\n\ + -R, --remove-other Delete image files for other Lisp systems.\n\ + -U, --remove-unknown Delete unrecognized files in image dir.\n\ + -a, --all-configured Select all configured implementations.\n\ + -d, --cleanup Delete images which are no longer wanted.\n\ -f, --force Dump images even if they already exist.\n\ - -i, --check-installed Check Lisp systems exist before invoking.\n\ - -j, --jobs=NJOBS Run up to NJOBS jobs in parallel.\n", + -i, --check-installed Check Lisp systems exist before dumping.\n\ + -j, --jobs=NJOBS Run up to NJOBS jobs in parallel.\n\ + -r, --remove-image Delete image files, instead of creating.\n", fp); } +static void show_job_list(const char *what, struct job *job) +{ + struct dstr d = DSTR_INIT; + int first; + + first = 1; + for (; job; job = job->next) { + if (first) first = 0; + else dstr_puts(&d, ", "); + dstr_putf(&d, "`%s'", JOB_NAME(job)); + } + if (first) dstr_puts(&d, "(none)"); + dstr_putz(&d); + moan("%s: %s", what, d.p); +} + /* Main program. */ int main(int argc, char *argv[]) { @@ -980,16 +1121,22 @@ int main(int argc, char *argv[]) struct config_section *sect; struct config_var *var; const char *out = 0, *p, *q, *l; - struct job *job, **tail; + struct job *job; struct stat st; struct dstr d = DSTR_INIT; - int i, fd, first; + DIR *dir; + struct dirent *de; + int i, fd; + size_t n, o; + unsigned f; /* Command-line options. */ static const struct option opts[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, { "output", OPTF_ARGREQ, 0, 'O' }, + { "remove-other", OPTF_NEGATE, 0, 'R' }, + { "remove-unknown", OPTF_NEGATE, 0, 'U' }, { "all-configured", 0, 0, 'a' }, { "config-file", OPTF_ARGREQ, 0, 'c' }, { "force", OPTF_NEGATE, 0, 'f' }, @@ -998,6 +1145,7 @@ int main(int argc, char *argv[]) { "dry-run", OPTF_NEGATE, 0, 'n' }, { "set-option", OPTF_ARGREQ, 0, 'o' }, { "quiet", 0, 0, 'q' }, + { "remove-image", OPTF_NEGATE, 0, 'r' }, { "verbose", 0, 0, 'v' }, { 0, 0, 0, 0 } }; @@ -1008,30 +1156,41 @@ int main(int argc, char *argv[]) /* Parse the options. */ optprog = (/*unconst*/ char *)progname; + +#define FLAGOPT(ch, f) \ + case ch: \ + flags |= f; \ + break; \ + case ch | OPTF_NEGATED: \ + flags &= ~f; \ + break + for (;;) { - i = mdwopt(argc - 1, argv + 1, "hVO:ac:f+i+j:n+o:qv", opts, 0, 0, + i = mdwopt(argc - 1, argv + 1, "hVO:R+U+ac:d+f+i+j:n+o:qr+v", opts, 0, 0, OPTF_NEGATION | OPTF_NOPROGNAME); if (i < 0) break; switch (i) { case 'h': help(stdout); exit(0); case 'V': version(stdout); exit(0); case 'O': out = optarg; break; + FLAGOPT('R', AF_CLEAN); + FLAGOPT('U', AF_JUNK); case 'a': flags |= AF_ALL; break; case 'c': read_config_path(optarg, 0); flags |= AF_SETCONF; break; - case 'f': flags |= AF_FORCE; break; - case 'f' | OPTF_NEGATED: flags &= ~AF_FORCE; break; - case 'i': flags |= AF_CHECKINST; break; - case 'i' | OPTF_NEGATED: flags &= ~AF_CHECKINST; break; + FLAGOPT('f', AF_FORCE); + FLAGOPT('i', AF_CHECKINST); case 'j': maxrun = parse_int("number of jobs", optarg, 1, 65535); break; - case 'n': flags |= AF_DRYRUN; break; - case 'n' | OPTF_NEGATED: flags &= ~AF_DRYRUN; break; + FLAGOPT('n', AF_DRYRUN); case 'o': if (set_config_var(optarg)) flags |= AF_BOGUS; break; case 'q': if (verbose) verbose--; break; + FLAGOPT('r', AF_REMOVE); case 'v': verbose++; break; default: flags |= AF_BOGUS; break; } } +#undef FLAGOPT + /* CHeck that everything worked. */ optind++; if ((flags&AF_ALL) ? optind < argc : optind >= argc) flags |= AF_BOGUS; @@ -1084,6 +1243,10 @@ int main(int argc, char *argv[]) "@image-out", "${@BUILTIN:@%out-dir}/${image-file}"); } else if (argc - optind != 1) lose("can't dump multiple Lisps to a single output file"); + else if (flags&AF_JUNK) + lose("can't clear junk in a single output file"); + else if (flags&AF_CLEAN) + lose("can't clean other images with a single output file"); else config_set_var(&config, builtin, CF_LITERAL, "@image-out", out); @@ -1093,83 +1256,166 @@ int main(int argc, char *argv[]) /* Dump the final configuration if we're being very verbose. */ if (verbose >= 5) dump_config(); - /* Create jobs for the Lisp systems we're supposed to be dumping. */ - tail = &job_ready; - if (!(flags&AF_ALL)) - for (i = optind; i < argc; i++) - add_job(&tail, 0, argv[i], strlen(argv[i])); - else { - /* So we're supposed to dump `all' of them. If there's a `dump' + /* There are a number of different strategies we might employ, depending on + * the exact request. + * + * queue queue clear + * REMOVE CLEAN JUNK selected others junk? + * + * * nil nil ready/delete -- no + * * nil t ready/delete none yes + * nil t nil ready delete no + * nil t t ready -- yes + * t t nil -- delete no + * t t t -- -- yes + */ + + /* First step: if `AF_REMOVE' and `AF_CLEAN' are not both set, then scan + * the selected Lisp systems and add them to the appropriate queue. + * + * Bit-hack: if they are not both set, then their complements are not both + * clear. + */ + if (~flags&(AF_REMOVE | AF_CLEAN)) { + + /* Determine the flags for `add_job' when we select the Lisp systems. If + * we intend to clear junk then we must notice the image names we + * encounter. If we're supposed to check that Lisps exist before dumping + * then do that -- but it doesn't make any sense for deletion. + */ + f = flags&AF_REMOVE ? JQ_DELETE : JQ_READY; + if (flags&AF_JUNK) f |= JF_NOTICE; + if (flags&AF_CHECKINST) f |= JF_CHECKINST; + if (!(flags&(AF_FORCE | AF_REMOVE))) f |= JF_CHECKEXIST; + + /* If we have named Lisps, then process them. */ + if (!(flags&AF_ALL)) + for (i = optind; i < argc; i++) + add_named_job(f, argv[i], strlen(argv[i])); + + /* Otherwise we're supposed to dump `all' of them. If there's a `dump' * configuration setting then we need to parse that. Otherwise we just * try all of them. */ - var = config_find_var(&config, toplevel, 0, "dump"); - if (!var) { - /* No setting. Just do all of the Lisps which look available. */ - - flags |= AF_CHECKINST; - for (config_start_section_iter(&config, &si); - (sect = config_next_section(&si)); ) - add_job(&tail, JF_QUIET, - CONFIG_SECTION_NAME(sect), - CONFIG_SECTION_NAMELEN(sect)); - } else { - /* Parse the `dump' list. */ - - p = var->val; l = p + var->n; - for (;;) { - while (p < l && ISSPACE(*p)) p++; - if (p >= l) break; - q = p; - while (p < l && !ISSPACE(*p) && *p != ',') p++; - add_job(&tail, 0, q, p - q); - while (p < l && ISSPACE(*p)) p++; - if (p < l && *p == ',') p++; + else { + var = config_find_var(&config, toplevel, CF_INHERIT, "dump"); + if (!var) { + /* No setting. Just do all of the Lisps which look available. */ + + f |= JF_CHECKINST; + for (config_start_section_iter(&config, &si); + (sect = config_next_section(&si)); ) + add_job(f, sect); + } else { + /* Parse the `dump' list. */ + + dstr_reset(&d); config_subst_var(&config, toplevel, var, &d); + p = d.p; l = p + d.len; + for (;;) { + while (p < l && ISSPACE(*p)) p++; + if (p >= l) break; + q = p; + while (p < l && !ISSPACE(*p) && *p != ',') p++; + add_named_job(f, q, p - q); + while (p < l && ISSPACE(*p)) p++; + if (p < l && *p == ',') p++; + } } } } - *tail = 0; + + /* Second step: if exactly one of `AF_CLEAN' and `AF_JUNK' is set, then we + * need to scan all of the remaining Lisps and add them to the `delete' + * queue. + */ + if (!(flags&AF_CLEAN) != !(flags&AF_JUNK)) { + + /* Determine the flag settings. If we're junking, then we're not + * cleaning -- we just want to mark images belonging to other Lisps as + * off-limits to the junking scan. + */ + f = flags&AF_CLEAN ? JQ_DELETE : JQ_NONE | JF_NOTICE; + + /* Now scan the Lisp systems. */ + for (config_start_section_iter(&config, &si); + (sect = config_next_section(&si)); ) + add_job(f, sect); + } + + /* Terminate the job queues. */ + *job_ready_tail = 0; + *job_delete_tail = 0; /* Report on what it is we're about to do. */ if (verbose >= 3) { - dstr_reset(&d); - first = 1; - for (job = job_ready; job; job = job->next) { - if (first) first = 0; - else dstr_puts(&d, ", "); - dstr_putf(&d, "`%s'", JOB_NAME(job)); - } - if (first) dstr_puts(&d, "(none)"); - dstr_putz(&d); - moan("dumping Lisps: %s", d.p); + show_job_list("dumping Lisp images", job_ready); + show_job_list("deleting Lisp images", job_delete); } - /* If we're not actually going to do anything after all then now's the time - * to, err, not do that. - */ - if (flags&AF_DRYRUN) { - for (job = job_ready; job; job = job->next) { - if (try_exec(&job->av, - TEF_DRYRUN | - (verbose >= 2 && !(flags&AF_CHECKINST) ? - TEF_VERBOSE : 0))) - rc = 2; - else if (verbose >= 2) - printf("%-13s > (not dumping `%s': dry run)\n", - JOB_NAME(job), JOB_NAME(job)); - } - return (rc); - } + /* If there turns out to be nothing to do, then mention this. */ + if (!(flags&AF_REMOVE) && verbose >= 2 && !job_ready) + moan("no Lisp images to dump"); - /* Run the jobs. */ + /* Run the dumping jobs. */ run_jobs(); - /* Finally, check for any last signals. If we hit any fatal signals then - * we should kill ourselves so that the exit status will be right. + /* Check for any last signals. If we hit any fatal signals then we should + * kill ourselves so that the exit status will be right. */ check_signals(); if (sigloss) { cleanup(); signal(sigloss, SIG_DFL); raise(sigloss); } + /* Now delete Lisps which need deleting. */ + while (job_delete) { + job = job_delete; job_delete = job->next; + if (flags&AF_DRYRUN) { + if (verbose >= 2) + moan("not deleting `%s' image `%s' (dry run)", + JOB_NAME(job), job->imgout); + } else { + if (verbose >= 2) + moan("deleting `%s' image `%s' (dry run)", + JOB_NAME(job), job->imgout); + if (unlink(job->imgout) && errno != ENOENT) + bad("failed to delete `%s' image `%s': %s", + JOB_NAME(job), job->imgout, strerror(errno)); + } + } + + /* Finally, maybe delete all of the junk files in the image directory. */ + if (flags&AF_JUNK) { + if (!out) { + var = config_find_var(&config, builtin, CF_INHERIT, "@image-dir"); + assert(var); out = config_subst_var_alloc(&config, builtin, var); + } + dir = opendir(out); + if (!dir) + lose("failed to open image directory `%s': %s", out, strerror(errno)); + dstr_reset(&d); + dstr_puts(&d, out); dstr_putc(&d, '/'); o = d.len; + if (verbose >= 2) + moan("cleaning up junk in image directory `%s'", out); + for (;;) { + de = readdir(dir); if (!de) break; + if (de->d_name[0] == '.' && + (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))) + continue; + n = strlen(de->d_name); + d.len = o; dstr_putm(&d, de->d_name, n + 1); + if (!treap_lookup(&good, de->d_name, n)) { + if (flags&AF_DRYRUN) { + if (verbose >= 2) + moan("not deleting junk file `%s' (dry run)", d.p); + } else { + if (verbose >= 2) + moan("deleting junk file `%s'", d.p); + if (unlink(d.p) && errno != ENOENT) + bad("failed to delete junk file `%s': %s", d.p, strerror(errno)); + } + } + } + } + /* All done! */ return (rc); }