From e90d61435061fac51b70155e95c7775658abe7fc Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Tue, 31 May 2016 18:48:03 -0700 Subject: [PATCH] list_packs: update to latest Signed-off-by: Kyle J. McKay --- src/list_packs.c | 259 ++++++++++++++++++++++++++++++++++++++++++++--------- src/list_packs.txt | 28 ++++++ 2 files changed, 247 insertions(+), 40 deletions(-) diff --git a/src/list_packs.c b/src/list_packs.c index ea22791..903201e 100644 --- a/src/list_packs.c +++ b/src/list_packs.c @@ -61,19 +61,44 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. static int fgetfn(FILE *f, char *b, size_t s); static void process_pack_filename(char *fn, size_t bl, int hasps); +static void process_pack(const char *fn, uint32_t objcnt); +static void process_packs_finish(void); + +struct pack_info { + struct pack_info *next; + uint32_t objcount; + char filename[1]; +}; + +struct ext_info { + struct ext_info *next; + size_t extlen; /* strlen(ext) */ + char ext[12]; /* includes leading '.' and trailing '\0' */ + int xxt; /* >0 excludes if ext present, 0 excludes if ext NOT present */ +}; + +#define MAX_EXT_LEN (sizeof(((const struct ext_info *)0)->ext) - 2) +#define BAD_EXT_CHARS ":./\\ \t\n\v\f\r" const char USAGE[] = #include "list_packs.inc" ; static int opt_q = 0; -static int opt_xkp = -1; -static int opt_xbt = -1; -static int opt_xbn = -1; static int opt_xix = -1; static long opt_limit = 0; static int opt_count = 0; static uint64_t count = 0; +static uint64_t objlimit = 0; +static int opt_desc = 0; +static int opt_boundary = 0; +static uint64_t maxlimit = 0; +static uint64_t processed = 0; + +static struct pack_info *packlist = NULL; +static size_t packcount = 0; + +static struct ext_info *extlist = NULL; static char fnbuff[PATH_BUFF]; @@ -88,6 +113,24 @@ static void die(const char *fmt, ...) exit(EXIT_FAILURE); } +static void dienomem(const char *what) +{ + if (what && !*what) + what = NULL; + die("list_packs: error: out of memory%s%s\n", (what ? " for " : ""), + (what ? what : "")); +} + +static void dienopackmem(void) +{ + dienomem("pack list"); +} + +static void dienoextmem(void) +{ + dienomem("ext list"); +} + static void dieusage(int err) { FILE *f = err ? stderr : stdout; @@ -104,10 +147,7 @@ static int has_suffix(const char *s, size_t l, const char *x, size_t b) return !strncmp(s + l - b, x, b); } #define has_idx_suffix(s,l) has_suffix((s),(l),".idx",4) -#define has_bndl_suffix(s,l) has_suffix((s),(l),".bndl",5) -#define has_keep_suffix(s,l) has_suffix((s),(l),".keep",5) #define has_pack_suffix(s,l) has_suffix((s),(l),".pack",5) -#define has_bitmap_suffix(s,l) has_suffix((s),(l),".bitmap",7) static int is_pack_sha1_name(const char *s, size_t l) { @@ -118,6 +158,46 @@ static int is_pack_sha1_name(const char *s, size_t l) return strspn(s + 5, "0123456789abcdefABCDEF") == 40; } +static struct ext_info *find_add_ext(const char *ext) +{ + size_t elen = strlen(ext); + struct ext_info *result = extlist; + + while (result && strcmp(result->ext, ext)) { + result = result->next; + } + if (!result) { + result = (struct ext_info *)malloc(sizeof(struct ext_info)); + if (!result) + dienoextmem(); + result->extlen = elen + 1; + result->ext[0] = '.'; + memcpy(&result->ext[1], ext, elen + 1); + if (elen + 2 < sizeof(result->ext)) + memset(&result->ext[elen + 2], 0, sizeof(result->ext) - (elen + 2)); + result->xxt = -1; + result->next = extlist; + extlist = result; + } + return result; +} + +void handle_ext_option(const char *ext, int v) +{ + size_t elen; + struct ext_info *einfo; + + if (!ext || !*ext || !(elen = strlen(ext)) || + elen > MAX_EXT_LEN || strcspn(ext, BAD_EXT_CHARS) != elen) + dieusage(EXIT_FAILURE); + if (!strcmp(ext, "idx")) { + opt_xix = v; + } else { + einfo = find_add_ext(ext); + einfo->xxt = v; + } +} + int main(int argc, char *argv[]) { int argn; @@ -134,26 +214,36 @@ int main(int argc, char *argv[]) opt_q = 1; } else if (!strcmp(argv[argn], "-a") || !strcmp(argv[argn], "--all")) { opt_a = 1; + } else if (!strcmp(argv[argn], "--exclude-idx")) { + opt_xix = 1; + } else if (!strcmp(argv[argn], "--exclude-no-idx")) { + opt_xix = 0; } else if (!strcmp(argv[argn], "--exclude-keep")) { - opt_xkp = 1; + handle_ext_option("keep", 1); } else if (!strcmp(argv[argn], "--exclude-no-keep")) { - opt_xkp = 0; + handle_ext_option("keep", 0); } else if (!strcmp(argv[argn], "--exclude-bitmap")) { - opt_xbt = 1; + handle_ext_option("bitmap", 1); } else if (!strcmp(argv[argn], "--exclude-no-bitmap")) { - opt_xbt = 0; + handle_ext_option("bitmap", 0); } else if (!strcmp(argv[argn], "--exclude-bndl")) { - opt_xbn = 1; + handle_ext_option("bndl", 1); } else if (!strcmp(argv[argn], "--exclude-no-bndl")) { - opt_xbn = 0; - } else if (!strcmp(argv[argn], "--exclude-idx")) { - opt_xix = 1; - } else if (!strcmp(argv[argn], "--exclude-no-idx")) { - opt_xix = 0; + handle_ext_option("bndl", 0); } else if (!strcmp(argv[argn], "--count")) { opt_count = 1; } else if (!strcmp(argv[argn], "--count-objects")) { opt_count = 2; + } else if (!strcmp(argv[argn], "--include-boundary")) { + opt_boundary = 1; + } else if (!strcmp(argv[argn], "--exclude-ext")) { + if (++argn >= argc) + dieusage(EXIT_FAILURE); + handle_ext_option(argv[argn], 1); + } else if (!strcmp(argv[argn], "--exclude-no-ext")) { + if (++argn >= argc) + dieusage(EXIT_FAILURE); + handle_ext_option(argv[argn], 0); } else if (!strcmp(argv[argn], "--exclude-limit")) { char *end; long limit = 0; @@ -164,6 +254,31 @@ int main(int argc, char *argv[]) if (!*argv[argn] || *end || !limit) dieusage(EXIT_FAILURE); opt_limit = limit; + } else if (!strcmp(argv[argn], "--object-limit")) { + char *end; + long limit = 0; + + if (++argn >= argc) + dieusage(EXIT_FAILURE); + limit = strtol(argv[argn], &end, 0); + if (!*argv[argn] || *end || !limit) + dieusage(EXIT_FAILURE); + if (limit < 0) { + opt_desc = 1; + objlimit = (uint64_t)-limit; + } else { + objlimit = (uint64_t)limit; + } + } else if (!strcmp(argv[argn], "--max-matches")) { + char *end; + long limit = 0; + + if (++argn >= argc) + dieusage(EXIT_FAILURE); + limit = strtol(argv[argn], &end, 0); + if (!*argv[argn] || *end || limit <= 0) + dieusage(EXIT_FAILURE); + maxlimit = (uint64_t)limit; } else if (!strcmp(argv[argn], "--only")) { if (++argn >= argc || !*argv[argn]) dieusage(EXIT_FAILURE); @@ -199,7 +314,7 @@ int main(int argc, char *argv[]) if (!in) die("list_packs: error: could not open %s\n", only); } - while (fgetfn(in, fnbuff, sizeof(fnbuff) - 7 /* .bitmap */)) { + while (fgetfn(in, fnbuff, sizeof(fnbuff) - (MAX_EXT_LEN + 1))) { char *fn = fnbuff; size_t l = strlen(fn); int ips; @@ -261,6 +376,7 @@ int main(int argc, char *argv[]) } closedir(d); } + process_packs_finish(); if (opt_count) printf("%"PRIu64"\n", count); @@ -337,6 +453,7 @@ static void process_pack_filename(char *fn, size_t bl, int hasps) } hdr; uint32_t packver; uint32_t objcnt; + const struct ext_info *einfo; if (stat(fn, &ps) || !S_ISREG(ps.st_mode)) { if (!opt_q) @@ -350,29 +467,17 @@ static void process_pack_filename(char *fn, size_t bl, int hasps) "invalid pack file '%s'\n", fn); return; } - if (opt_xkp >= 0) { - int hk; - - memcpy(fn + bl, ".keep", 6); - hk = file_exists(fn, &es); - if ((opt_xkp && hk) || (!opt_xkp && !hk)) - return; - } - if (opt_xbt >= 0) { - int hb; - - memcpy(fn + bl, ".bitmap", 8); - hb = file_exists(fn, &es); - if ((opt_xbt && hb) || (!opt_xbt && !hb)) - return; - } - if (opt_xbn >= 0) { - int hn; - - memcpy(fn + bl, ".bndl", 6); - hn = file_exists(fn, &es); - if ((opt_xbn && hn) || (!opt_xbn && !hn)) - return; + einfo = extlist; + while (einfo) { + if (einfo->xxt >= 0) { + int hext; + + memcpy(fn + bl, einfo->ext, einfo->extlen + 1); + hext = file_exists(fn, &es); + if ((einfo->xxt && hext) || (!einfo->xxt && !hext)) + return; + } + einfo = einfo->next; } if (opt_xix >= 0) { int hx; @@ -422,6 +527,27 @@ static void process_pack_filename(char *fn, size_t bl, int hasps) return; } /* the PACK file passed all the checks, process it */ + if (objlimit) { + size_t fnlen = strlen(fn); + struct pack_info *info = (struct pack_info *) + malloc(sizeof(struct pack_info) + fnlen); + + if (!info) + dienopackmem(); + info->objcount = objcnt; + memcpy(info->filename, fn, fnlen + 1); + info->next = packlist; + packlist = info; + ++packcount; + } else { + process_pack(fn, objcnt); + } +} + +static void process_pack(const char *fn, uint32_t objcnt) +{ + if (maxlimit && processed >= maxlimit) + return; if (opt_count) { if (opt_count > 1) count += objcnt; @@ -430,4 +556,57 @@ static void process_pack_filename(char *fn, size_t bl, int hasps) } else { printf("%s\n", fn); } + ++processed; +} + +static void process_pack_info(const struct pack_info *pack) +{ + process_pack(pack->filename, pack->objcount); +} + +static int sort_asc(const void *p, const void *q) +{ + const struct pack_info **a = (const struct pack_info **)p; + const struct pack_info **b = (const struct pack_info **)q; + if ((*a)->objcount < (*b)->objcount) + return -1; + if ((*a)->objcount > (*b)->objcount) + return 1; + return strcmp((*a)->filename, (*b)->filename); +} + +static int sort_dsc(const void *p, const void *q) +{ + return sort_asc(q, p); +} + +static void process_packs_finish(void) +{ + struct pack_info **table, *p; + size_t i; + uint64_t tally; + + if (!objlimit || !packlist || !packcount) + return; + table = (struct pack_info **)malloc(sizeof(struct pack_info *) * packcount); + if (!table) + dienopackmem(); + i = 0; + p = packlist; + do { + table[i++] = p; + p = p->next; + } while (p); + qsort(table, packcount, sizeof(struct pack_info *), (opt_desc ? sort_dsc : sort_asc)); + tally = 0; + for (i=0; i < packcount; ++i) { + tally += table[i]->objcount; + if (tally <= objlimit) { + process_pack_info(table[i]); + } else { + if (opt_boundary) + process_pack_info(table[i]); + break; + } + } } diff --git a/src/list_packs.txt b/src/list_packs.txt index 51576bf..93089e9 100644 --- a/src/list_packs.txt +++ b/src/list_packs.txt @@ -11,8 +11,14 @@ Usage: list_packs [-C ] [option ...] ( --only | ) --exclude-no-bndl exclude any matching packs without associated .bndl --exclude-idx exclude any matching packs with associated .idx --exclude-no-idx exclude any matching packs without associated .idx + --exclude-ext exclude any matching packs with associated .ext + --exclude-no-ext exclude any matching packs without associated .e --exclude-limit exclude any matching packs with at least n objects if n is < 0 then exclude packs without at least -n objs + --object-limit sort matches in ascending (descending if n < 0) order of + obj count and include packs while total obj count <= n + --include-boundary include the boundary pack that exceeds --object-limit + --max-matches limit final output to the first n matches (at most) --count instead of pack files output a count of them --count-objects instead of pack files output a total object count @@ -33,6 +39,28 @@ Usage: list_packs [-C ] [option ...] ( --only | ) If neither --exclude-xxx nor --exclude-no-xxx is given, then packs both with AND without an associated .xxx file match. + The --exclude-ext and --exclude-no-ext options require the given to + contain no whitespace, ':', '/', '\\' or '.' characters and be no more than + ten characters long. + + If --object-limit is given, all other options are first applied as normal, + but the matching packs are collected in a list which is then sorted in + ascending order (descending order if object limit is negative) by the + number of objects in the pack. Then the list is walked in sorted order + and the total object count computed along the way. The first pack that + has an object count that causes the total object count so far to EXCEED + the absolute value of the object limit is excluded as well as all + following packs. However, if --include-boundary is given then the pack + that causes the total object count so far to EXCEED the absolute value of + the object limit will be INCLUDED while all following packs are excluded. + Processing then continues as though only the non-excluded packs matched + meaning final output can still be a count of packs, count of total objects + or list of pack file names depending on the options given. + + If --max-matches is given, then after ALL other matching has been performed + only the first n matches (when there are more than n total final matches) + participate in generating the final output. + For --only, pack names are relative to the current directory (but see the -C option) and MUST include the trailing .pack and MUST be one per line but any characters after and including the first whitespace or colon character -- 2.11.4.GIT