summaryrefslogtreecommitdiff
path: root/grep-dctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'grep-dctrl.c')
-rw-r--r--grep-dctrl.c204
1 files changed, 151 insertions, 53 deletions
diff --git a/grep-dctrl.c b/grep-dctrl.c
index b20d421..b0574fa 100644
--- a/grep-dctrl.c
+++ b/grep-dctrl.c
@@ -47,6 +47,34 @@ static char progdoc [] = "grep-dctrl -- grep Debian control files";
#undef BANNER
+#define COPYING "/usr/share/common-licenses/GPL"
+
+/* Copy the file called fname to standard outuput stream. Return zero
+ iff problems were encountered. */
+static int to_stdout (const char * fname)
+{
+ FILE * f;
+ int c;
+ int rv = 1;
+
+ f = fopen (fname, "r");
+ if (f == 0) {
+ message (L_FATAL, strerror (errno), COPYING);
+ return 0;
+ }
+
+ while ( ( c = getc (f)) != EOF) putchar (c);
+
+ if (ferror (f)) {
+ message (L_FATAL, strerror (errno), COPYING);
+ rv = 0;
+ }
+
+ fclose (f);
+
+ return rv;
+}
+
#ifdef BANNER
void banner(bool automatic)
{
@@ -89,33 +117,35 @@ end:
#endif
static struct argp_option options[] = {
- { "banner", 'B', 0, 0, "Show the testing banner." },
- { "errorlevel", 'l', "LEVEL", 0, "Set debugging level to LEVEL." },
- { "field", 'F', "FIELD,FIELD,...", 0, "Restrict pattern matching to the FIELDs given." },
- { 0, 'P', 0, 0, "Shorthand for -FPackage" },
- { "show-field", 's', "FIELD,FIELD,...", 0, "Show only the body of these fields from the matching paragraphs." },
- { 0, 'd', 0, 0, "Show only the first line of the Description field from the matching paragraphs." },
- { "no-field-names", 'n', 0, 0, "Suppress field names when showing specified fields." },
- { "eregex", 'e', 0, 0, "Regard the pattern as an extended POSIX regular expression." },
- { "regex", 'r', 0, 0, "The pattern is a standard POSIX regular expression." },
- { "ignore-case", 'i', 0, 0, "Ignore case when looking for a match." },
- { "invert-match", 'v', 0, 0, "Show only paragraphs that do not match." },
- { "count", 'c', 0, 0, "Show only the count of matching paragraphs." },
+#ifdef BANNER
+ { "banner", 'B', 0, 0, "Show the testing banner." },
+#endif
+ { "errorlevel", 'l', "LEVEL", 0, "Set debugging level to LEVEL." },
+ { "field", 'F', "FIELD,FIELD,...", 0, "Restrict pattern matching to the FIELDs given." },
+ { 0, 'P', 0, 0, "Shorthand for -FPackage" },
+ { "show-field", 's', "FIELD,FIELD,...", 0, "Show only the body of these fields from the matching paragraphs." },
+ { 0, 'd', 0, 0, "Show only the first line of the Description field from the matching paragraphs." },
+ { "no-field-names", 'n', 0, 0, "Suppress field names when showing specified fields." },
+ { "eregex", 'e', 0, 0, "Regard the pattern as an extended POSIX regular expression." },
+ { "regex", 'r', 0, 0, "The pattern is a standard POSIX regular expression." },
+ { "ignore-case", 'i', 0, 0, "Ignore case when looking for a match." },
+ { "invert-match", 'v', 0, 0, "Show only paragraphs that do not match." },
+ { "count", 'c', 0, 0, "Show only the count of matching paragraphs." },
{ "config-file", OPT_CONFIG, "FNAME", 0, "Use FNAME as the config file." },
- { "exact-match", 'X', 0, 0, "Do an exact match." },
- { "copying", 'C', 0, 0, "Print out the copyright license." },
- { "and", 'a', 0, 0, "Conjunct predicates." },
- { "or", 'o', 0, 0, "Disjunct predicates." },
- { "not", '!', 0, 0, "Negate the following predicate." },
- { "debug-optparse", OPT_OPTPARSE, 0, 0, "Debug option parsing." },
- { "quiet", 'q', 0, 0, "No output to stdout" },
- { "silent", OPT_SILENT, 0, 0, "No output to stdout" },
+ { "exact-match", 'X', 0, 0, "Do an exact match." },
+ { "copying", 'C', 0, 0, "Print out the copyright license." },
+ { "and", 'a', 0, 0, "Conjunct predicates." },
+ { "or", 'o', 0, 0, "Disjunct predicates." },
+ { "not", '!', 0, 0, "Negate the following predicate." },
+ { "debug-optparse", OPT_OPTPARSE, 0, 0, "Debug option parsing." },
+ { "quiet", 'q', 0, 0, "No output to stdout" },
+ { "silent", OPT_SILENT, 0, 0, "No output to stdout" },
{ 0 }
};
-enum state { STATE_ATOM, STATE_NEG, STATE_CONJ, STATE_DISJ, STATE_PAREN,
- STATE_START, STATE_FINISHED };
+enum state { STATE_ATOM, STATE_NEG, STATE_CONJ, STATE_DISJ,
+ STATE_PAREN, STATE_START, STATE_FINISHED };
#define MAX_FNAMES 4096
@@ -130,6 +160,8 @@ struct arguments {
size_t num_fnames;
/**/
size_t num_show_fields;
+ /**/
+ size_t num_search_fields;
/* A machine-readable representation of the predicate. */
struct predicate p;
/* Configuration file name */
@@ -147,9 +179,13 @@ struct arguments {
/* Invert match? */
bool invert_match;
/* Parser stack. */
- struct {
+ struct stack_elem {
enum state state;
- int insn;
+ /* A linked list of instructions. */
+ struct insn_node {
+ int insn;
+ struct insn_node * next;
+ } * insns_first, * insns_last;
} stack[MAX_OPS];
/* File names seen on the command line. */
char const * fname[MAX_FNAMES];
@@ -158,8 +194,41 @@ struct arguments {
char const * name;
size_t inx;
} show_fields[MAX_FIELDS];
+ /* Search field names seen during current atom. */
+ char * search_fields[MAX_FIELDS];
};
+struct atom * clone_atom(struct arguments * args)
+{
+ if (args->p.num_atoms >= MAX_ATOMS) {
+ message(L_FATAL, "predicate is too complex", 0);
+ fail();
+ }
+ struct atom * atom = get_current_atom(&args->p);
+ int push_insn = I_PUSH(args->p.num_atoms);
+ struct atom * rv = &args->p.atoms[args->p.num_atoms++];
+ rv->field_name = atom->field_name;
+ rv->field_inx = atom->field_inx;
+ rv->mode = atom->mode;
+ rv->ignore_case = atom->ignore_case;
+ rv->pat = atom->pat;
+ rv->patlen = atom->patlen;
+ assert(args->top > 0);
+ struct stack_elem * selem = &args->stack[args->top-1];
+ assert(selem->insns_first != 0);
+ assert(selem->insns_last != 0);
+ struct insn_node * node1 = malloc(sizeof *node1);
+ struct insn_node * node2 = malloc(sizeof *node2);
+ if (node1 == 0 || node2 == 0) fatal_enomem(0);
+ node1->insn = push_insn;
+ node2->insn = I_OR;
+ node1->next = node2;
+ node2->next = 0;
+ selem->insns_last->next = node1;
+ selem->insns_last = node2;
+ return rv;
+}
+
static void finish_atom(struct arguments * args)
{
struct atom * atom = get_current_atom(&args->p);
@@ -167,10 +236,14 @@ static void finish_atom(struct arguments * args)
message(L_FATAL, "A pattern is mandatory.", 0);
fail();
}
- predicate_finish_atom(&args->p);
+ for (size_t i = 0; i < args->num_search_fields; i++) {
+ if (i > 0) atom = clone_atom(args);
+ atom->field_name = args->search_fields[i];
+ predicate_finish_atom(&args->p);
+ }
}
-/* Pop off one stack state, inserting the associated instruction to
+/* Pop off one stack state, inserting the associated instructions to
* the predicate program. If paren is true, current state must be
* STATE_PAREN, and if paren is false, it must not be STATE_PAREN. */
static void leave(struct arguments * args, int paren)
@@ -180,7 +253,15 @@ static void leave(struct arguments * args, int paren)
if (args->state == STATE_ATOM) finish_atom(args);
assert(args->top > 0);
--args->top;
- addinsn(&args->p, args->stack[args->top].insn);
+ for (struct insn_node * it = args->stack[args->top].insns_first;
+ it != 0;) {
+ addinsn(&args->p, it->insn);
+ struct insn_node * next = it->next;
+ free(it);
+ it = next;
+ }
+ args->stack[args->top].insns_first = 0;
+ args->stack[args->top].insns_last = 0;
args->state = args->stack[args->top].state;
}
@@ -192,7 +273,13 @@ static void prim_enter(struct arguments * args, const enum state state, const in
message(L_FATAL, "predicate is too complex", 0);
fail();
}
- args->stack[args->top].insn = insn;
+// args->stack[args->top].insn = insn;
+ struct insn_node * node = malloc(sizeof *node);
+ if (node == 0) fatal_enomem(0);
+ node->insn = insn;
+ node->next = 0;
+ args->stack[args->top].insns_first = node;
+ args->stack[args->top].insns_last = node;
args->stack[args->top].state = args->state;
++args->top;
args->state = state;
@@ -264,12 +351,12 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
struct atom * atom;
debug_message("parse_opt", 0);
switch (key) {
- case 'B':
+ case 'C':
+ if (!to_stdout (COPYING)) fail();
+ exit(0);
#ifdef BANNER
+ case 'B':
banner(false);
-#else
- fprintf(stderr, "Banner is disabled.\n");
- fail();
#endif
case 'v':
args->invert_match = true;
@@ -291,8 +378,9 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
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];
+ 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);
sf->inx = fieldtrie_insert(&args->p.trie, s);
@@ -301,6 +389,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
}
++args->num_show_fields;
}
+ free(carg);
}
break;
case 'l': {
@@ -330,34 +419,43 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
debug_message("parse_opt: P", 0);
arg = "Package";
/* pass through */
- case 'F':
- debug_message("parse_opt: F", 0);
+ case 'F': {
atom = ENTER_ATOM;
- if (atom->field_name != 0) {
- message(L_FATAL, "multiple -F (or -P) options are "
- " currently broken; workaround: --or", 0);
- fail();
+ char * carg = strdup(arg);
+ if (carg == 0) fatal_enomem(0);
+ for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){
+ char * tmp = strdup(s);
+ if (tmp == 0) fatal_enomem(0);
+ args->search_fields[args->num_search_fields++] = tmp;
}
-//assert(atom->field_name == 0); /* FIXME */
- atom->field_name = strdup(arg);
- if (atom->field_name == 0) fatal_enomem(0);
+ free(carg);
+ }
break;
case 'X':
debug_message("parse_opt: X", 0);
atom = ENTER_ATOM;
- assert(atom->mode == M_SUBSTR); /* FIXME */
+ if (atom->mode != M_SUBSTR) {
+ message(L_FATAL, "inconsistent atom modifiers", 0);
+ fail();
+ }
atom->mode = M_EXACT;
break;
case 'r':
debug_message("parse_opt: r", 0);
atom = ENTER_ATOM;
- assert(atom->mode == M_SUBSTR); /* FIXME */
+ if (atom->mode != M_SUBSTR) {
+ message(L_FATAL, "inconsistent atom modifiers", 0);
+ fail();
+ }
atom->mode = M_REGEX;
break;
case 'e':
debug_message("parse_opt: e", 0);
atom = ENTER_ATOM;
- assert(atom->mode == M_SUBSTR); /* FIXME */
+ if (atom->mode != M_SUBSTR) {
+ message(L_FATAL, "inconsistent atom modifiers", 0);
+ fail();
+ }
atom->mode = M_EREGEX;
break;
case 'i':
@@ -369,7 +467,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
debug_message("parse_opt: optparse", 0);
debug_optparse = 1;
break;
- case ARGP_KEY_ARG:
+ case ARGP_KEY_ARG:
debug_message("parse_opt: argument", 0);
redo:
debug_message("!!!", 0);
@@ -384,7 +482,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
break;
}
if (strcmp(arg, ")") == 0) {
- debug_message("parse_opt: )", 0);
+ debug_message("parse_opt: )", 0);
while (args->state != STATE_PAREN) {
if (args->top == 0) {
message(L_FATAL, "unexpected ')' in command line", 0);
@@ -418,7 +516,7 @@ static error_t parse_opt (int key, char * arg, struct argp_state * state)
debug_message("parse_opt: end", 0);
if (args->state != STATE_FINISHED) FINISH;
break;
- case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS:
case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS:
debug_message("parse_opt: ignored", 0);
break;
@@ -449,10 +547,10 @@ static void dump_args(struct arguments * args)
int op = args->p.program[i];
printf("program[%zi] = ", i);
switch (op) {
- case I_NOP: puts("NOP"); break;
- case I_NEG: puts("NEG"); break;
- case I_AND: puts("AND"); break;
- case I_OR: puts("OR"); break;
+ case I_NOP: puts("NOP"); break;
+ case I_NEG: puts("NEG"); break;
+ case I_AND: puts("AND"); break;
+ case I_OR: puts("OR"); break;
default:
printf("PUSH(%i)\n", op - I_PUSH(0));
}