diff options
Diffstat (limited to 'grep-dctrl/grep-dctrl.c')
-rw-r--r-- | grep-dctrl/grep-dctrl.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/grep-dctrl/grep-dctrl.c b/grep-dctrl/grep-dctrl.c new file mode 100644 index 0000000..687f8cf --- /dev/null +++ b/grep-dctrl/grep-dctrl.c @@ -0,0 +1,875 @@ +/* dctrl-tools - Debian control file inspection tools + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Antti-Juhani Kaijanaho + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <argp.h> +#include <assert.h> +#include <fcntl.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include "fnutil.h" +#include "fsaf.h" +#include "i18n.h" +#include "misc.h" +#include "msg.h" +#include "paragraph.h" +#include "predicate.h" +#include "rc.h" +#include "util.h" + +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; + +static char progdoc [] = N_("grep-dctrl -- grep Debian control files"); + +static char argsdoc [] = "PREDICATE [FILENAME...]"; + +enum { + OPT_CONFIG=256, + OPT_OPTPARSE, + OPT_SILENT, + OPT_EQ, + OPT_LT, + OPT_LE, + OPT_GT, + OPT_GE, + OPT_MMAP, + OPT_IGN_ERRS, + OPT_PATTERN +}; + +#undef BANNER + +#ifdef BANNER +void banner(bool automatic) +{ + char * fname = fnqualify_xalloc("~/.grep-dctrl-banner-shown"); + struct stat st; + if (automatic) { + int r = stat(fname, &st); + if (r == 0) goto end; + } + FILE * fp = fopen("/dev/tty", "w"); + if (fp == 0) { + perror("/dev/tty"); + goto end; + } + fprintf(fp, + "==========================================================================\n" + " NOTE \n" + " grep-dctrl has been rewritten from scratch. Although this does add new \n" + " features, regressions are certainly possible. Please watch for them and \n" + " report them to the BTS. \n" + "==========================================================================\n" + "(The above annoying banner will not be shown to you again, unless you\n" + "request it with the -B switch. It will also be removed entirely soon.)\n"); + + int r = creat(fname, 0644); + if (r == -1) perror(fname); + + if (!automatic) exit(0); + + for (int i = 15; i > 0; i--) { + fprintf(fp, "%2d seconds until program is resumed...\r", i); + fflush(fp); + sleep(1); + } + fprintf(fp, " \r"); + fflush(fp); +end: + free(fname); +} +#endif + +static struct argp_option options[] = { +#ifdef BANNER + { "banner", 'B', 0, 0, N_("Show the testing banner.") }, +#endif + { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, + { "field", 'F', N_("FIELD,FIELD,..."), 0, N_("Restrict pattern matching to the FIELDs given.") }, + { 0, 'P', 0, 0, N_("This is a shorthand for -FPackage.") }, + { 0, 'S', 0, 0, N_("This is a shorthand for -FSource:Package.") }, + { "show-field", 's', N_("FIELD,FIELD,..."), 0, N_("Show only the body of these fields from the matching paragraphs.") }, + { 0, 'd', 0, 0, N_("Show only the first line of the \"Description\" field from the matching paragraphs.") }, + { "no-field-names", 'n', 0, 0, N_("Suppress field names when showing specified fields.") }, + { "eregex", 'e', 0, 0, N_("Regard the pattern as an extended POSIX regular expression.") }, + { "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.") }, + { "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.") }, + { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, + { "and", 'a', 0, 0, N_("Conjunct predicates.") }, + { "or", 'o', 0, 0, N_("Disjunct predicates.") }, + { "not", '!', 0, 0, N_("Negate the following predicate.") }, + { "eq", OPT_EQ, 0, 0, N_("Test for version number equality.") }, + { "lt", OPT_LT, 0, 0, N_("Version number comparison: <.") }, + { "le", OPT_LE, 0, 0, N_("Version number comparison: <=.") }, + { "gt", OPT_GT, 0, 0, N_("Version number comparison: >.") }, + { "ge", OPT_GE, 0, 0, N_("Version number comparison: >=.") }, + { "debug-optparse", OPT_OPTPARSE, 0, 0, N_("Debug option parsing.") }, + { "quiet", 'q', 0, 0, N_("Do no output to stdout.") }, + { "silent", OPT_SILENT, 0, 0, N_("Do no output to stdout.") }, + { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, + { "ignore-parse-errors", OPT_IGN_ERRS, 0, 0, N_("Ignore parse errors") }, + { "pattern", OPT_PATTERN, N_("PATTERN"), 0, N_("Specify the pattern to search for") }, + { 0 } +}; + + +// Tokens +#define TOK_EOD 0 +#define TOK_NOT 1 +#define TOK_AND 2 +#define TOK_OR 3 +#define TOK_LP 4 /* left paren */ +#define TOK_RP 5 /* right paren */ +#define TOK_ATOM_BASE 6 /* All tokens >= TOK_ATOM_BASE are atoms; the + difference is the atom index. */ + +#define MAX_FNAMES 4096 +#define MAX_TOKS 16384 + +static int debug_optparse = 0; + +struct arguments { + /* Parser state flag: last token seen was ')' */ + bool just_seen_cparen; + /* Top of the parser stack. */ + size_t top; + /* Number of file names seen. */ + 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 */ + char const * rcname; + /* Ignore parse errors? */ + bool ignore_errors; + /* Quiet operation? */ + bool quiet; + /* Do show field names? */ + bool show_field_name; + /* Do show (only) first line of Description? */ + bool short_descr; + /* Does show_fields contain Description? */ + bool description_selected; + /* Count matching paragraphs? */ + bool count; + /* Invert match? */ + bool invert_match; + /* First unused position in toks. */ + size_t toks_np; + /* Token read position. */ + size_t toks_pos; + /* Finished with the predicate scanning? */ + bool finished; + /* Are we inside an atom? */ + bool in_atom; + /* Pattern error? */ + bool pattern_error; + /* Token stream for the predicate parser. */ + int toks[MAX_TOKS]; + /* For each atom, give code with which it can be accessed. */ + struct atom_code { + size_t n; + int * routine; + } *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]; + /* 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(); + } + int oa = args->p.num_atoms-1; + struct atom * atom = get_current_atom(&args->p); + int na = 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; + struct atom_code * ac = args->atom_code[oa]; + args->atom_code[na] = ac; + assert(ac->n > 0); + ac->n += 2; + ac->routine = realloc(ac->routine, ac->n * sizeof *ac->routine); + if (ac->routine == 0) fatal_enomem(0); + ac->routine[ac->n-2] = I_PUSH(na); + ac->routine[ac->n-1] = I_OR; + return rv; +} + +static void finish_atom(struct arguments * args) +{ + assert(args->in_atom); + args->in_atom = false; + struct atom * atom = get_current_atom(&args->p); + if (atom->pat == 0) { + args->pattern_error = true; + return; + } + 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); + } + // If there are no fields, we have not yet run this... + // ... but it must be done (especially with -r/-e atoms) + if (args->num_search_fields == 0) predicate_finish_atom(&args->p); + args->num_search_fields = 0; +} + +#if 0 +/* 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) +{ + debug_message("leaving...", 0); + assert(paren == (args->state == STATE_PAREN)); + if (args->state == STATE_ATOM) finish_atom(args); + assert(args->top > 0); + --args->top; + 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; +} +#endif + +#define APPTOK(tok) do { apptok(args, (tok)); } while (0) + +static void apptok(struct arguments * args, const int tok) +{ + debug_message("apptok", 0); + if (args->in_atom && tok < TOK_ATOM_BASE) { + finish_atom(args); + } + if (args->toks_np >= MAX_TOKS) { + message(L_FATAL, _("predicate is too long"), 0); + fail(); + } + args->toks[args->toks_np++] = tok; +} + +#define FINISH do { finish(args); } while (0) + +/* Flush the state stack. */ +static void finish(struct arguments * args) +{ + assert(!args->finished); + if (args->in_atom) finish_atom(args); + args->finished = true; +} + +#define ENTER_ATOM (enter_atom((args))) + +/* FIXME: UPDATE COMMENT +If necessary, enter STATE_ATOM and allocate a new atom, pushing + * along with the old state a PUSH instruction for the new atom to the + * parser stack. If we are already in STATE_ATOM, reuse the current + * atom. */ +static struct atom * enter_atom(struct arguments * args) +{ + struct atom * rv; + if (args->in_atom) { + assert(args->p.num_atoms > 0); + return &args->p.atoms[args->p.num_atoms-1]; + } + args->in_atom = true; + if (args->p.num_atoms >= MAX_ATOMS) { + message(L_FATAL, _("predicate is too complex"), 0); + fail(); + } + APPTOK(args->p.num_atoms + TOK_ATOM_BASE); + args->atom_code[args->p.num_atoms] = + malloc(sizeof *args->atom_code[args->p.num_atoms]); + if (args->atom_code[args->p.num_atoms] == 0) fatal_enomem(0); + args->atom_code[args->p.num_atoms]->n = 1; + args->atom_code[args->p.num_atoms]->routine = malloc(1 * sizeof(int)); + if (args->atom_code[args->p.num_atoms]->routine == 0) { + fatal_enomem(0); + } + args->atom_code[args->p.num_atoms]->routine[0] + = I_PUSH(args->p.num_atoms); + rv = &args->p.atoms[args->p.num_atoms++]; + rv->field_name = 0; + rv->field_inx = -1; + rv->mode = M_SUBSTR; + rv->ignore_case = 0; + rv->pat = 0; + rv->patlen = 0; + return rv; +} + +#define set_mode(nmode) do { \ + atom = ENTER_ATOM; \ + if (atom->mode != M_SUBSTR) { \ + message(L_FATAL, _("inconsistent atom modifiers"), 0); \ + fail(); \ + } \ + atom->mode = (nmode); \ +} while (0) + +static error_t parse_opt (int key, char * arg, struct argp_state * state) +{ + struct arguments * args = state->input; + bool just_seen_cparen = args->just_seen_cparen; + args->just_seen_cparen = false; + struct atom * atom; + debug_message("parse_opt", 0); +#ifdef INCLUDE_DEBUG_MSGS + if (do_msg(L_DEBUG)) { + fprintf(stderr, "%s: in_atom = %s\n", + get_progname(), + args->in_atom ? "true" : "false"); + } +#endif + switch (key) { + case 'C': + if (!to_stdout (COPYING)) fail(); + exit(0); +#ifdef BANNER + case 'B': + banner(false); +#endif + case 'v': + args->invert_match = true; + break; + case 'c': + args->count = true; + break; + case 'q': case OPT_SILENT: + debug_message("parse_opt: q", 0); + args->quiet = true; + break; + case 'n': + debug_message("parse_opt: n", 0); + args->show_field_name = false; + break; + case 'd': + args->short_descr = 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, ':'); + 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 + ? (size_t)(-1) + : fieldtrie_insert(repl); + ++args->num_show_fields; + } + free(carg); + } + break; + case 'l': { + int ll = str2loglevel(arg); + if (ll < 0) + { + message(L_FATAL, _("no such log level"), arg); + fail(); + } + set_loglevel(ll); + debug_message("parse_opt: l", 0); + } + break; + case '!': + debug_message("parse_opt: !", 0); + APPTOK(TOK_NOT); + break; + case 'a': + debug_message("parse_opt: a", 0); + APPTOK(TOK_AND); + break; + case 'o': + debug_message("parse_opt: o", 0); + APPTOK(I_OR); + break; + case 'S': + debug_message("parse_opt: S", 0); + arg = "Source:Package"; + goto case_F; + case 'P': + debug_message("parse_opt: P", 0); + arg = "Package"; + goto case_F; + case_F: + case 'F': { + debug_message("parse_opt: Fv", 0); + atom = ENTER_ATOM; + 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; + } + free(carg); + } + break; + case 'X': + debug_message("parse_opt: X", 0); + set_mode(M_EXACT); + break; + case 'r': + debug_message("parse_opt: r", 0); + set_mode(M_REGEX); + break; + case 'e': + debug_message("parse_opt: e", 0); + set_mode(M_EREGEX); + break; + case OPT_EQ: + debug_message("parse_opt: eq", 0); + set_mode(M_VER_EQ); + break; + case OPT_LT: + debug_message("parse_opt: lt", 0); + set_mode(M_VER_LT); + break; + case OPT_LE: + debug_message("parse_opt: le", 0); + set_mode(M_VER_LE); + break; + case OPT_GT: + debug_message("parse_opt: gt", 0); + set_mode(M_VER_GT); + break; + case OPT_GE: + debug_message("parse_opt: ge", 0); + set_mode(M_VER_GE); + break; + case OPT_MMAP: + debug_message("parse_opt: mmap", 0); + fsaf_mmap = 1; + break; + case 'i': + debug_message("parse_opt: i", 0); + atom = ENTER_ATOM; + atom->ignore_case = 1; + break; + case OPT_OPTPARSE: + debug_message("parse_opt: optparse", 0); + debug_optparse = 1; + break; + case OPT_IGN_ERRS: + debug_message("parse_opt: ignore-parse-errors", 0); + args->ignore_errors = 1; + break; + case OPT_PATTERN: + debug_message("parse_opt: pattern", 0); + atom = ENTER_ATOM; + if (atom->pat != 0) { + message(L_FATAL, _("Multiple patterns for the same " + "atom are not allowed"), 0); + fail(); + } + atom->patlen = strlen(arg); + atom->pat = malloc(atom->patlen+1); + if (atom->pat == 0) fatal_enomem(0); + strcpy((char*)atom->pat, arg); + break; + case ARGP_KEY_ARG: + debug_message("parse_opt: argument", 0); + redo: + debug_message("!!!", 0); + if (strcmp(arg, "!") == 0) { + debug_message("parse_opt: !", 0); + APPTOK(TOK_NOT); + break; + } + if (strcmp(arg, "(") == 0) { + debug_message("parse_opt: (", 0); + APPTOK(TOK_LP); + break; + } + if (strcmp(arg, ")") == 0) { + debug_message("parse_opt: )", 0); + args->just_seen_cparen = true; + APPTOK(TOK_RP); + break; + } + if (args->finished) { + char const * s; + if (args->num_fnames >= MAX_FNAMES) { + message(L_FATAL, _("too many file names"), 0); + fail(); + } + s = strdup(arg); + if (s == 0) fatal_enomem(0); + args->fname[args->num_fnames++] = + (struct ifile){ .mode = m_read, .s = s }; + break; + } + if (just_seen_cparen) { FINISH; goto redo; } + if (strcmp(arg, "--") == 0) { FINISH; break; } + atom = ENTER_ATOM; + if (atom->pat != 0) { FINISH; goto redo; } + atom->patlen = strlen(arg); + atom->pat = malloc(atom->patlen+1); + if (atom->pat == 0) fatal_enomem(0); + strcpy((char*)atom->pat, arg); + break; + case ARGP_KEY_END: + debug_message("parse_opt: end", 0); + if (!args->finished) FINISH; + break; + 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; + case OPT_CONFIG: + debug_message("parse_opt: --config-file", 0); + args->rcname = strdup(arg); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static void dump_args(struct arguments * args) +{ + size_t i; + assert(args->finished); + assert(args->top == 0); + printf("num_atoms = %zi\n", args->p.num_atoms); + for (i = 0; i < args->p.num_atoms; i++) { + printf("atoms[%zi].field_name = %s\n", i, args->p.atoms[i].field_name); + printf("atoms[%zi].mode = %i\n", i, args->p.atoms[i].mode); + printf("atoms[%zi].ignore_case = %i\n", i, args->p.atoms[i].ignore_case); + printf("atoms[%zi].pat = %s\n", i, args->p.atoms[i].pat); + } + printf("proglen = %zi\n", args->p.proglen); + for (i = 0; i < args->p.proglen; i++) { + 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; + default: + printf("PUSH(%i)\n", op - I_PUSH(0)); + } + } + printf("num_fnames = %zi\n", args->num_fnames); + for (i = 0; i < args->num_fnames; i++) { + printf("fname[%zi].mode = %s, fname[%zi].s = %s\n", + i, ifile_modes[args->fname[i].mode], + i, args->fname[i].s); + } +} + +static +int peek_token(struct arguments const * args) +{ + assert(args->toks_pos <= args->toks_np); + if (args->toks_pos == args->toks_np) return TOK_EOD; + return args->toks[args->toks_pos]; +} + +static +int get_token(struct arguments * args) +{ + assert(args->toks_pos <= args->toks_np); + if (args->toks_pos == args->toks_np) return TOK_EOD; + return args->toks[args->toks_pos++]; +} + +static void unexpected(int tok) +{ + switch (tok) { + case TOK_EOD: + message(L_FATAL, _("unexpected end of predicate"), 0); + fail(); + case TOK_NOT: + message(L_FATAL, _("unexpected '!' in command line"), 0); + fail(); + case TOK_AND: + message(L_FATAL, _("unexpected '-a' in command line"), 0); + fail(); + case TOK_OR : + message(L_FATAL, _("unexpected '-o' in command line"), 0); + fail(); + case TOK_LP : + message(L_FATAL, _("unexpected '(' in command line"), 0); + fail(); + case TOK_RP : + message(L_FATAL, _("unexpected ')' in command line"), 0); + fail(); + default: + assert(tok >=TOK_ATOM_BASE); + message(L_FATAL, _("unexpected atom in command line"), 0); + fail(); + } +} + +static void parse_conj(struct arguments * args); + +static void parse_prim(struct arguments * args) +{ + if (peek_token(args) == TOK_LP) { + get_token(args); + parse_conj(args); + if (get_token(args) != TOK_RP) { + message(L_FATAL, _("missing ')' in command line"), 0); + fail(); + } + return; + } + if (peek_token(args) < TOK_ATOM_BASE) unexpected(peek_token(args)); + int atom = get_token(args) - TOK_ATOM_BASE; + assert(atom >= 0); + assert(atom < MAX_ATOMS); + struct atom_code *ac = args->atom_code[atom]; + for (size_t i = 0; i < ac->n; i++) { + addinsn(&args->p, ac->routine[i]); + } +/* + addinsn(&args->p, I_PUSH(atom)); +*/ +} + +static void parse_neg(struct arguments * args) +{ + bool neg = false; + if (peek_token(args) == TOK_NOT) { + neg = true; + get_token(args); + } + parse_prim(args); + if (neg) addinsn(&args->p, I_NEG); +} + +static void parse_disj(struct arguments * args) +{ + parse_neg(args); + while (peek_token(args) == TOK_OR) { + get_token(args); + parse_neg(args); + addinsn(&args->p, I_OR); + } +} + +static void parse_conj(struct arguments * args) +{ + parse_disj(args); + while (peek_token(args) == TOK_AND) { + get_token(args); + parse_disj(args); + addinsn(&args->p, I_AND); + } +} + +static void parse_predicate(struct arguments * args) +{ + args->toks_pos = 0; + parse_conj(args); + int tok = peek_token(args); + if (tok != TOK_EOD) unexpected(tok); +} + +static struct argp argp = { .options = options, + .parser = parse_opt, + .args_doc = argsdoc, + .doc = progdoc }; + +int main (int argc, char * argv[]) +{ + setlocale(LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + fieldtrie_init(); + + static struct arguments args; + args.show_field_name = true; + msg_set_progname(argv[0]); + init_predicate(&args.p); + description_inx = fieldtrie_insert(description); + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); +#ifdef BANNER + banner(true); +#endif + parse_predicate(&args); + if (args.pattern_error) { + message(L_FATAL, _("A pattern is mandatory"), 0); + fail(); + } + + if (debug_optparse) { dump_args(&args); return 0; } + + if (args.p.num_atoms == 0) { + message(L_FATAL, _("a predicate is required"), 0); + fail(); + } + + if (!check_predicate(&args.p)) { + message(L_FATAL, _("malformed predicate"), 0); + fail(); + } + + if (args.short_descr && !args.description_selected) { + if (args.num_show_fields >= MAX_FIELDS) { + message(L_FATAL, _("too many output fields"), 0); + fail(); + } + 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; + ++args.num_show_fields; + } + + if (!args.show_field_name && args.num_show_fields == 0) { + message(L_FATAL, + _("cannot suppress field names when showing whole paragraphs"), + 0); + fail(); + } + + size_t count = 0; + bool found = false; + for (size_t i = 0; i < args.num_fnames || (i == 0 && args.num_fnames == 0); ++i) { + int fd; + struct ifile fname; + if (args.num_fnames == 0) { + // Hardcode grep-dctrl <-> "-" mapping so that + // Debian packages can genuinely depend on it. + char * argv0 = fnbase(argv[0]); + if (strcmp(argv0, "grep-dctrl") == 0) { + fname = (struct ifile){ .mode = m_read, + .s = "-" }; + } else { + fname = find_ifile_by_exename(argv0, args.rcname); + } + } else { + fname = args.fname[i]; + } + + if (fname.mode == m_error) break; + + fd = open_ifile(fname); + if (fd == -1) break; + + if (!chk_ifile(fname, fd)) break; + + FSAF * fp = fsaf_fdopen(fd, fname.s); + para_parser_t pp; + para_parser_init(&pp, fp, true, args.ignore_errors); + para_t para; + para_init(&pp, ¶); + while (1) { + para_parse_next(¶); + if (para_eof(&pp)) break; + if ((args.invert_match + || !does_para_satisfy(&args.p, ¶)) + && (!args.invert_match + || does_para_satisfy(&args.p, ¶))) { + continue; + } + if (args.quiet) { + exit(0); + } + found = true; + if (args.count) { + ++count; + continue; + } + if (args.num_show_fields == 0) { + struct fsaf_read_rv r = get_whole_para(¶); + fwrite(r.b, 1, r.len, stdout); + 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(¶, + 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.num_show_fields > 1) puts(""); + } + + fsaf_close(fp); + close_ifile(fname, fd); + } + if (count) printf("%zi\n", count); + return errors_reported() ? 2 : found ? 0 : 1; +} + |