mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-19 01:15:21 +00:00
134 lines
4.8 KiB
C
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;
|
|
}
|