summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--grep-dctrl/grep-dctrl.c127
-rw-r--r--lib/fieldtrie.c50
-rw-r--r--lib/fieldtrie.h13
-rw-r--r--lib/para_bundle.c2
-rw-r--r--lib/paragraph.c41
-rw-r--r--lib/paragraph.h4
-rw-r--r--lib/predicate.c4
-rw-r--r--man/grep-dctrl.1.cp7
-rw-r--r--sort-dctrl/sort-dctrl.c4
-rw-r--r--tbl-dctrl/tbl-dctrl.c4
-rw-r--r--tests/bug144174.out31
-rw-r--r--tests/bug144174.sh5
12 files changed, 215 insertions, 77 deletions
diff --git a/grep-dctrl/grep-dctrl.c b/grep-dctrl/grep-dctrl.c
index 687f8cf..044334c 100644
--- a/grep-dctrl/grep-dctrl.c
+++ b/grep-dctrl/grep-dctrl.c
@@ -42,7 +42,7 @@ const char * argp_program_version = "grep-dctrl (dctrl-tools) " VERSION;
const char * argp_program_bug_address = MAINTAINER;
const char description [] = "Description";
-size_t description_inx;
+struct field_attr *description_attr;
static char progdoc [] = N_("grep-dctrl -- grep Debian control files");
@@ -120,6 +120,7 @@ static struct argp_option options[] = {
{ "regex", 'r', 0, 0, N_("The pattern is a standard POSIX regular expression.") },
{ "ignore-case", 'i', 0, 0, N_("Ignore case when looking for a match.") },
{ "invert-match", 'v', 0, 0, N_("Show only paragraphs that do not match.") },
+ { "invert-show", 'I', 0, 0, N_("Show those fields that have NOT been selected with -s") },
{ "count", 'c', 0, 0, N_("Show only the count of matching paragraphs.") },
{ "config-file", OPT_CONFIG, N_("FNAME"),0, N_("Use FNAME as the config file.") },
{ "exact-match", 'X', 0, 0, N_("Do an exact match.") },
@@ -186,6 +187,8 @@ struct arguments {
bool count;
/* Invert match? */
bool invert_match;
+ /* Show fields that are NOT listed? */
+ bool invert_show;
/* First unused position in toks. */
size_t toks_np;
/* Token read position. */
@@ -205,16 +208,19 @@ struct arguments {
} *atom_code[MAX_ATOMS];
/* File names seen on the command line. */
struct ifile fname[MAX_FNAMES];
- /**/
- struct show_fields {
- char const * name;
- size_t inx;
- size_t repl; // the field to use if this is empty
- } show_fields[MAX_FIELDS];
+ /**/
+ size_t show_fields[MAX_FIELDS];
/* Search field names seen during current atom. */
char * search_fields[MAX_FIELDS];
};
+#define IS_SHOW_FIELD(field_app_data) ((field_app_data) & 1)
+#define SET_SHOW_FIELD(field_app_data,val) \
+ ((field_app_data) = ((field_app_data & ~1) | val))
+#define GET_BACKUP_FIELD(field_app_data) ((field_app_data) >> 1)
+#define SET_BACKUP_FIELD(field_app_data,val) \
+ ((field_app_data) = (((field_app_data)&1) | (val<<1)))
+
struct atom * clone_atom(struct arguments * args)
{
if (args->p.num_atoms >= MAX_ATOMS) {
@@ -399,26 +405,35 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
case 'd':
args->short_descr = true;
break;
+ case 'I':
+ args->invert_show = true;
+ break;
case 's': {
char * carg = strdup(arg);
if (carg == 0) fatal_enomem(0);
for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){
- struct show_fields * sf =
- &args->show_fields[args->num_show_fields];
- sf->name = strdup(s);
- if (sf->name == 0) fatal_enomem(0);
- char * repl = strchr(sf->name, ':');
+ char * repl = strchr(s, ':');
if (repl != NULL) {
*repl = '\0';
++repl;
}
- sf->inx = fieldtrie_insert(sf->name);
- if (sf->inx == description_inx) {
- args->description_selected = true;
- }
- sf->repl = repl == NULL
+ struct field_attr *fa = fieldtrie_insert(s);
+ if (args->num_show_fields >= MAX_FIELDS) {
+ message(L_FATAL, _("too many output fields"), 0);
+ fail();
+ }
+ args->show_fields[args->num_show_fields] = fa->inx;
+ if (fa == description_attr) {
+ args->description_selected = true;
+ }
+ SET_SHOW_FIELD(fa->application_data, true);
+
+ size_t repl_inx = repl == NULL
? (size_t)(-1)
- : fieldtrie_insert(repl);
+ : fieldtrie_insert(repl)->inx;
+
+ SET_BACKUP_FIELD(fa->application_data, repl_inx);
+
++args->num_show_fields;
}
free(carg);
@@ -732,6 +747,27 @@ static void parse_predicate(struct arguments * args)
if (tok != TOK_EOD) unexpected(tok);
}
+static void show_field(struct arguments *args,
+ struct paragraph *para,
+ struct field_attr *fa)
+{
+ if (args->show_field_name) {
+ printf("%s: ", fa->name);
+ }
+ struct fsaf_read_rv r
+ = get_field(para,
+ fa->inx,
+ GET_BACKUP_FIELD(fa->application_data));
+
+ if (args->short_descr &&
+ fa == description_attr) {
+ char * nl = memchr(r.b, '\n', r.len);
+ if (nl != 0) r.len = nl - r.b;
+ }
+ fwrite(r.b, 1, r.len, stdout);
+ puts("");
+}
+
static struct argp argp = { .options = options,
.parser = parse_opt,
.args_doc = argsdoc,
@@ -749,7 +785,7 @@ int main (int argc, char * argv[])
args.show_field_name = true;
msg_set_progname(argv[0]);
init_predicate(&args.p);
- description_inx = fieldtrie_insert(description);
+ description_attr = fieldtrie_insert(description);
argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args);
#ifdef BANNER
banner(true);
@@ -780,11 +816,17 @@ int main (int argc, char * argv[])
message(L_INFORMATIONAL,
_("Adding \"Description\" to selected output fields because of -d"),
0);
- args.show_fields[args.num_show_fields].name = description;
- args.show_fields[args.num_show_fields].inx = description_inx;
+ SET_SHOW_FIELD(description_attr->application_data, 1);
+ args.show_fields[args.num_show_fields] = description_attr->inx;
++args.num_show_fields;
}
+ if (args.invert_show && args.num_show_fields == 0) {
+ message(L_FATAL,
+ _("-I requires at least one instance of -s"), 0);
+ fail();
+ }
+
if (!args.show_field_name && args.num_show_fields == 0) {
message(L_FATAL,
_("cannot suppress field names when showing whole paragraphs"),
@@ -820,7 +862,8 @@ int main (int argc, char * argv[])
FSAF * fp = fsaf_fdopen(fd, fname.s);
para_parser_t pp;
- para_parser_init(&pp, fp, true, args.ignore_errors);
+ para_parser_init(&pp, fp, true, args.ignore_errors,
+ args.invert_show);
para_t para;
para_init(&pp, &para);
while (1) {
@@ -846,23 +889,29 @@ int main (int argc, char * argv[])
putchar('\n');
continue;
}
- for (size_t j = 0; j < args.num_show_fields; j++) {
- if (args.show_field_name) {
- printf("%s: ", args.show_fields[j].name);
- }
- struct fsaf_read_rv r
- = get_field(&para,
- args.show_fields[j].inx,
- args.show_fields[j].repl);
- if (args.short_descr &&
- args.show_fields[j].inx == description_inx) {
- char * nl = memchr(r.b, '\n', r.len);
- if (nl != 0) r.len = nl - r.b;
- }
- fwrite(r.b, 1, r.len, stdout);
- puts("");
- continue;
- }
+ if (args.invert_show) {
+ for (size_t j = 0;
+ j < fieldtrie_count() &&
+ j < para.nfields;
+ j++) {
+ struct field_attr *fa =
+ fieldtrie_get(j);
+ if (IS_SHOW_FIELD(fa->application_data)) {
+ continue;
+ }
+ show_field(&args, &para, fa);
+ }
+ } else {
+ for (size_t j = 0;
+ j < args.num_show_fields; j++) {
+ size_t inx = args.show_fields[j];
+ struct field_attr *fa =
+ fieldtrie_get(inx);
+ assert(IS_SHOW_FIELD
+ (fa->application_data));
+ show_field(&args, &para, fa);
+ }
+ }
if (args.num_show_fields > 1) puts("");
}
diff --git a/lib/fieldtrie.c b/lib/fieldtrie.c
index 250fc89..79ea832 100644
--- a/lib/fieldtrie.c
+++ b/lib/fieldtrie.c
@@ -23,8 +23,6 @@
#include "msg.h"
struct field_bucket {
- char const * name;
- size_t namelen;
struct field_attr attr;
struct field_bucket * next;
};
@@ -34,6 +32,8 @@ struct fieldtrie_private {
/* A one-level trie listing all field names occurring in the
* atomic predicates. */
struct field_bucket * fields[UCHAR_MAX];
+ /* An array mapping each field inx to its attributes */
+ struct field_attr * field_map[65536];
};
static struct fieldtrie_private trie;
@@ -46,34 +46,42 @@ void fieldtrie_init(void)
trie.nextfree = 0;
}
-size_t fieldtrie_insert(char const * s)
+struct field_attr *fieldtrie_insert_n(char const * s, size_t slen)
{
- size_t slen = strlen(s);
- struct field_attr l_attr = fieldtrie_lookup(s, slen);
- if (l_attr.valid) return l_attr.inx;
+ struct field_attr *l_attr = fieldtrie_lookup(s, slen);
+ if (l_attr != 0) return l_attr;
struct field_bucket * b = malloc(sizeof *b);
if (b == 0) fatal_enomem(0);
- b->name = malloc(slen+1);
- if (b->name == 0) fatal_enomem(0);
- strcpy((char*)b->name, s);
- b->namelen = slen;
- b->attr.inx = trie.nextfree++;
- b->attr.valid = true;
- unsigned char c = tolower((unsigned char)(b->name[0]));
+ char *name = malloc(slen+1);
+ if (name == 0) fatal_enomem(0);
+ strncpy(name, s, slen);
+ (name)[slen] = '\0';
+ *(char**)&b->attr.name = name;
+ *(size_t*)&b->attr.namelen = slen;
+ assert(trie.nextfree < sizeof trie.field_map / sizeof *trie.field_map);
+ *(size_t*)&b->attr.inx = trie.nextfree++;
+ b->attr.application_data = 0;
+ unsigned char c = tolower((unsigned char)(b->attr.name[0]));
b->next = trie.fields[c];
trie.fields[c] = b;
- return b->attr.inx;
+ trie.field_map[b->attr.inx] = &b->attr;
+ return &b->attr;
}
-struct field_attr fieldtrie_lookup(char const * s, size_t n)
+struct field_attr *fieldtrie_insert(char const * s)
+{
+ return fieldtrie_insert_n(s, strlen(s));
+}
+
+struct field_attr *fieldtrie_lookup(char const * s, size_t n)
{
for (struct field_bucket * b = trie.fields[tolower((unsigned char)s[0])];
b != 0;
b = b->next) {
- if (n == b->namelen &&
- strncasecmp(s, b->name, n) == 0) return b->attr;
+ if (n == b->attr.namelen &&
+ strncasecmp(s, b->attr.name, n) == 0) return &b->attr;
}
- return (struct field_attr){ .valid = false };
+ return NULL;
}
size_t fieldtrie_count(void)
@@ -81,6 +89,12 @@ size_t fieldtrie_count(void)
return trie.nextfree;
}
+struct field_attr *fieldtrie_get(size_t inx)
+{
+ assert(inx < sizeof trie.field_map / sizeof *trie.field_map);
+ return trie.field_map[inx];
+}
+
#if 0
void fieldtrie_clear(void)
{
diff --git a/lib/fieldtrie.h b/lib/fieldtrie.h
index 0e1c634..a33e089 100644
--- a/lib/fieldtrie.h
+++ b/lib/fieldtrie.h
@@ -24,17 +24,22 @@
#include <stdbool.h>
struct field_attr {
- bool valid;
- size_t inx;
+ char const *const name;
+ const size_t namelen;
+ const size_t inx;
+ unsigned application_data;
};
void fieldtrie_init(void);
// case-insensitive
-size_t fieldtrie_insert(char const *);
+struct field_attr *fieldtrie_insert(char const *);
+struct field_attr *fieldtrie_insert_n(char const * s, size_t slen);
// case-insensitive
-struct field_attr fieldtrie_lookup(char const *, size_t n);
+struct field_attr *fieldtrie_lookup(char const *, size_t n);
+
+struct field_attr *fieldtrie_get(size_t inx);
//void fieldtrie_clear(void);
diff --git a/lib/para_bundle.c b/lib/para_bundle.c
index d52edbf..848ec01 100644
--- a/lib/para_bundle.c
+++ b/lib/para_bundle.c
@@ -39,7 +39,7 @@ void bundle_slurp(struct para_bundle * pb, struct ifile ifi)
sf->next = pb->files;
pb->files = sf;
- para_parser_init(&sf->pp, f, false, false);
+ para_parser_init(&sf->pp, f, false, false, false);
while (true) {
if (pb->num_paras == pb->max_num) {
size_t max_num = pb->max_num == 0 ? 256 :
diff --git a/lib/paragraph.c b/lib/paragraph.c
index 6926abf..b08b537 100644
--- a/lib/paragraph.c
+++ b/lib/paragraph.c
@@ -22,7 +22,8 @@
#include "strutil.h"
void para_parser_init(para_parser_t * pp, FSAF * fp,
- bool invalidate_p, bool ignore_failing_paras)
+ bool invalidate_p, bool ignore_failing_paras,
+ bool register_unknown_fields)
{
pp->fp = fp;
pp->eof = false;
@@ -30,6 +31,7 @@ void para_parser_init(para_parser_t * pp, FSAF * fp,
pp->line = 1;
pp->invalidate_p = invalidate_p;
pp->ignore_broken_paras = ignore_failing_paras;
+ pp->register_unknown_fields = register_unknown_fields;
}
void para_init(para_parser_t * pp, para_t * para)
@@ -57,13 +59,28 @@ void para_finalize(para_t * para)
para->fields = 0;
}
+static struct field_data * register_field(para_t * para,
+ char const * s, size_t slen)
+{
+ size_t inx = fieldtrie_insert_n(s, slen)->inx;
+ if (inx >= para->nfields) {
+ assert(para->nfields > 0);
+ para->nfields = para->nfields * 2;
+ para->fields = realloc(para->fields,
+ para->nfields * sizeof(para->fields[0])
+ );
+ if (para->fields == 0) fatal_enomem(0);
+ }
+ struct field_data *field_data = &para->fields[inx];
+ return field_data;
+}
+
void para_parse_next(para_t * para)
{
redo:
debug_message("para_parse_next", 0);
assert(para != 0);
para_parser_t * pp = para->common;
- assert(para->nfields == fieldtrie_count());
para->start = pp->loc;
para->line = pp->line;
for (size_t i = 0; i < para->nfields; i++) {
@@ -132,13 +149,23 @@ redo:
field_start,
len);
assert(r.len == len);
- struct field_attr attr =
+ struct field_attr *attr =
fieldtrie_lookup(r.b, len);
- if (!attr.valid) {
- field_data = 0;
+ if (attr == NULL) {
+ if (para->common->
+ register_unknown_fields) {
+ field_data =
+ register_field(para,
+ r.b,
+ len);
+ } else {
+ field_data = 0;
+ }
} else {
- assert(attr.inx < para->nfields);
- field_data = &para->fields[attr.inx];
+ assert(attr->inx < para->nfields);
+ field_data = &para->fields[attr->inx];
+ }
+ if (field_data != NULL) {
field_data->start = pos;
field_data->line = line;
}
diff --git a/lib/paragraph.h b/lib/paragraph.h
index b3daafc..6fbcf81 100644
--- a/lib/paragraph.h
+++ b/lib/paragraph.h
@@ -38,6 +38,7 @@ struct paragraph_parser {
size_t line;
bool ignore_broken_paras;
+ bool register_unknown_fields;
};
struct paragraph {
@@ -54,7 +55,8 @@ typedef struct paragraph para_t;
/* Initialize the given para_parser_t, associating with it the given FSAF. */
void para_parser_init(para_parser_t *, FSAF *,
- bool invalidate_p, bool ignore_broken_paras);
+ bool invalidate_p, bool ignore_broken_paras,
+ bool register_unknown_fields);
/* Initialize the given para_t for the given parser. */
void para_init(para_parser_t *, para_t *);
diff --git a/lib/predicate.c b/lib/predicate.c
index 67614cc..e75aa82 100644
--- a/lib/predicate.c
+++ b/lib/predicate.c
@@ -51,11 +51,11 @@ void predicate_finish_atom(struct predicate * p)
char * repl = strchr(atom->field_name, ':');
if (repl != NULL) {
*repl++ = '\0';
- atom->repl_inx = fieldtrie_insert(repl);
+ atom->repl_inx = fieldtrie_insert(repl)->inx;
} else {
atom->repl_inx = -1;
}
- atom->field_inx = fieldtrie_insert(atom->field_name);
+ atom->field_inx = fieldtrie_insert(atom->field_name)->inx;
}
if (atom->mode == M_REGEX || atom->mode == M_EREGEX) {
diff --git a/man/grep-dctrl.1.cp b/man/grep-dctrl.1.cp
index bfa50c2..c4f66fb 100644
--- a/man/grep-dctrl.1.cp
+++ b/man/grep-dctrl.1.cp
@@ -182,7 +182,12 @@ escaped for most shells.
Show only the body of these fields from the matching paragraphs. The
field names must not include any colons or commas. Commas are used to
delimit field names in the argument to this option. The fields are
-shown in the order given here.
+shown in the order given here. See also the option \-I.
+.IP "\-I; \-\-invert\-show"
+Invert the meaning of option \-s: show only the fields that have
+.B not
+been named using a \-s option. As an artefact of the implementation,
+the order of the fields in the original paragraph is not preserved.
.PP
A FIELD specification can contain a colon. In such a case, the part
up to the colon is taken as the name of the field to be shown, and the
diff --git a/sort-dctrl/sort-dctrl.c b/sort-dctrl/sort-dctrl.c
index 725687c..d1a55ed 100644
--- a/sort-dctrl/sort-dctrl.c
+++ b/sort-dctrl/sort-dctrl.c
@@ -72,7 +72,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
*(flags++) = '\0';
}
struct key key;
- key.field_inx = fieldtrie_insert(carg);
+ key.field_inx = fieldtrie_insert(carg)->inx;
key.type = FT_STRING;
key.reverse = false;
for (char *p = flags; *p != '\0'; p++) {
@@ -152,7 +152,7 @@ int main(int argc, char * argv[])
argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args);
if (args.keys.nks == 0) {
- size_t inx = fieldtrie_insert("Package");
+ size_t inx = fieldtrie_insert("Package")->inx;
keys_append(&args.keys, (struct key){ .field_inx = inx,
.type = FT_STRING,
.reverse = false });
diff --git a/tbl-dctrl/tbl-dctrl.c b/tbl-dctrl/tbl-dctrl.c
index aec0898..2801ddf 100644
--- a/tbl-dctrl/tbl-dctrl.c
+++ b/tbl-dctrl/tbl-dctrl.c
@@ -259,7 +259,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
if (fn != NULL) *(fn++) = '\0';
struct column *col = &args->columns[args->num_columns];
col->heading = carg;
- col->field_inx = fieldtrie_insert(fn == NULL ? carg : fn);
+ col->field_inx = fieldtrie_insert(fn == NULL ? carg : fn)->inx;
size_t n = 0;
_Bool err = 0;
for (char const *p = lens; *p != '\0'; p++) {
@@ -417,7 +417,7 @@ int main(int argc, char * argv[])
FSAF * fp = fsaf_fdopen(fd, fname.s);
para_parser_t pp;
- para_parser_init(&pp, fp, true, false);
+ para_parser_init(&pp, fp, true, false, false);
para_t para;
para_init(&pp, &para);
while (1) {
diff --git a/tests/bug144174.out b/tests/bug144174.out
new file mode 100644
index 0000000..9638eb6
--- /dev/null
+++ b/tests/bug144174.out
@@ -0,0 +1,31 @@
+Description: Command-line tools to process Debian package information
+ Debian package information is generally stored in files having a
+ special file format, dubbed the Debian control file format (the dctrl
+ format), a special case of the record jar file format. These tools
+ operate on any files conforming in a general sense to that format and
+ are therefore widely applicable whenever those formats are in play.
+ .
+ Included are:
+ .
+ grep-dctrl - Grep dctrl-format files
+ grep-available - Grep the DPKG available database
+ grep-status - Grep the DPKG status database
+ grep-aptavail - Grep the APT available database
+ .
+ sort-dctrl - Sort dctrl-format files
+ .
+ tbl-dctrl - Tabulate dctrl-format files
+ .
+ sync-available - Sync the dpkg available database with
+ the apt database
+Priority: optional
+Section: utils
+Installed-Size: 512
+Maintainer: Antti-Juhani Kaijanaho <ajk@debian.org>
+Architecture: amd64
+Version: 2.10
+Replaces: grep-dctrl
+Provides: grep-dctrl
+Depends: libc6 (>= 2.3.5-1)
+Suggests: apt
+Conflicts: grep-dctrl
diff --git a/tests/bug144174.sh b/tests/bug144174.sh
new file mode 100644
index 0000000..3d4da86
--- /dev/null
+++ b/tests/bug144174.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -e
+
+$GREP_DCTRL -I -sPackage '' 0001.out