miller/c/mapping/mapper_grep.c
2017-03-05 10:17:42 -05:00

134 lines
4.8 KiB
C

#include "cli/argparse.h"
#include "cli/mlrcli.h"
#include "mapping/mappers.h"
#include "lib/mlr_globals.h"
#include "lib/mlrutil.h"
#include "lib/mlrregex.h"
#include "containers/sllv.h"
typedef struct _mapper_grep_state_t {
ap_state_t* pargp;
int exclude;
regex_t regex;
cli_writer_opts_t* pwriter_opts;
} mapper_grep_state_t;
static void mapper_grep_usage(FILE* o, char* argv0, char* verb);
static mapper_t* mapper_grep_parse_cli(int* pargi, int argc, char** argv,
cli_reader_opts_t* _, cli_writer_opts_t* __);
static mapper_t* mapper_grep_alloc(ap_state_t* pargp, char* regex_string, int exclude, int ignore_case,
cli_writer_opts_t* pwriter_opts);
static void mapper_grep_free(mapper_t* pmapper, context_t* _);
static sllv_t* mapper_grep_process(lrec_t* pinrec, context_t* pctx, void* pvstate);
// ----------------------------------------------------------------
mapper_setup_t mapper_grep_setup = {
.verb = "grep",
.pusage_func = mapper_grep_usage,
.pparse_func = mapper_grep_parse_cli,
.ignores_input = FALSE,
};
// ----------------------------------------------------------------
static mapper_t* mapper_grep_parse_cli(int* pargi, int argc, char** argv,
cli_reader_opts_t* _, cli_writer_opts_t* pwriter_opts)
{
char* regex_string = NULL;
int exclude = FALSE;
int ignore_case = FALSE;
if ((argc - *pargi) < 1) {
mapper_grep_usage(stderr, argv[0], argv[*pargi]);
return NULL;
}
char* verb = argv[(*pargi)++];
ap_state_t* pstate = ap_alloc();
ap_define_true_flag(pstate, "-v", &exclude);
ap_define_true_flag(pstate, "-i", &ignore_case);
if (!ap_parse(pstate, verb, pargi, argc, argv)) {
mapper_grep_usage(stderr, argv[0], verb);
return NULL;
}
if ((argc - *pargi) < 1) {
mapper_grep_usage(stderr, argv[0], verb);
return NULL;
}
regex_string = argv[(*pargi)++];
mapper_t* pmapper = mapper_grep_alloc(pstate, regex_string, exclude, ignore_case, pwriter_opts);
return pmapper;
}
static void mapper_grep_usage(FILE* o, char* argv0, char* verb) {
fprintf(o, "Usage: %s %s [options] {regular expression}\n", argv0, verb);
fprintf(o, "Passes through records which match {regex}.\n");
fprintf(o, "Options:\n");
fprintf(o, "-i Use case-insensitive search.\n");
fprintf(o, "-v Invert: pass through records which do not match the regex.\n");
fprintf(o, "Note that \"%s filter\" is more powerful, but requires you to know field names.\n", argv0);
fprintf(o, "By contrast, \"%s %s\" allows you to regex-match the entire record. It does\n", argv0, verb);
fprintf(o, "this by formatting each record in memory as DKVP, using command-line-specified\n");
fprintf(o, "ORS/OFS/OPS, and matching the resulting line against the regex specified\n");
fprintf(o, "here. In particular, the regex is not applied to the input stream: if you\n");
fprintf(o, "have CSV with header line \"x,y,z\" and data line \"1,2,3\" then the regex will\n");
fprintf(o, "be matched, not against either of these lines, but against the DKVP line\n");
fprintf(o, "\"x=1,y=2,z=3\". Furthermore, not all the options to system grep are supported,\n");
fprintf(o, "and this command is intended to be merely a keystroke-saver. To get all the\n");
fprintf(o, "features of system grep, you can do\n");
fprintf(o, " \"%s --odkvp ... | grep ... | %s --idkvp ...\"\n", argv0, argv0);
}
// ----------------------------------------------------------------
static mapper_t* mapper_grep_alloc(ap_state_t* pargp, char* regex_string, int exclude, int ignore_case,
cli_writer_opts_t* pwriter_opts)
{
mapper_t* pmapper = mlr_malloc_or_die(sizeof(mapper_t));
mapper_grep_state_t* pstate = mlr_malloc_or_die(sizeof(mapper_grep_state_t));
pstate->pargp = pargp;
int cflags = REG_NOSUB;
if (ignore_case)
cflags |= REG_ICASE;
regcomp_or_die_quoted(&pstate->regex, regex_string, cflags);
pstate->exclude = exclude;
pstate->pwriter_opts = pwriter_opts;
pmapper->pvstate = pstate;
pmapper->pprocess_func = mapper_grep_process;
pmapper->pfree_func = mapper_grep_free;
return pmapper;
}
static void mapper_grep_free(mapper_t* pmapper, context_t* _) {
mapper_grep_state_t* pstate = pmapper->pvstate;
regfree(&pstate->regex);
ap_free(pstate->pargp);
free(pstate);
free(pmapper);
}
// ----------------------------------------------------------------
static sllv_t* mapper_grep_process(lrec_t* pinrec, context_t* pctx, void* pvstate) {
if (pinrec == NULL) // end of input stream
return sllv_single(NULL);
mapper_grep_state_t* pstate = (mapper_grep_state_t*)pvstate;
char* line = lrec_sprint(pinrec,
pstate->pwriter_opts->ors,
pstate->pwriter_opts->ofs,
pstate->pwriter_opts->ops);
int matches = regmatch_or_die(&pstate->regex, line, 0, NULL);
sllv_t* poutrecs = NULL;
if (matches ^ pstate->exclude) {
poutrecs = sllv_single(pinrec);
} else {
lrec_free(pinrec);
}
free(line);
return poutrecs;
}