/* This file is part of cfpeek -*- c -*-
Copyright (C) 2011-2021 Sergey Poznyakoff
Cfpeek 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 3, or (at your option)
any later version.
Cfpeek 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 cfpeek. If not, see .
*/
#ifdef HAVE_GETOPT_H
# include
#endif
#include
#include "grecs/gitid.h"
static struct grecs_txtacc *pp_cmd_acc;
char *program_name;
#define PREPROC_NOT_SET 0
#define PREPROC_SET 1
#define PREPROC_DEFAULT 2
static int preproc_settings = PREPROC_NOT_SET;
#define CFPEEK_NODE_FLAG_DEFAULT \
(GRECS_NODE_FLAG_PATH|GRECS_NODE_FLAG_VALUE|GRECS_NODE_FLAG_DESCEND)
static int flags_on, flags_off;
struct format_flag {
char *name;
char *arg;
char *descr;
int flag;
void (*handler)(const char *);
};
static void
set_parent_option(const char *str)
{
struct locate_instr *instr = new_locate_instr();
instr->cmd = locate_parent;
instr->v.s = grecs_strdup(str);
}
static void
set_child_option(const char *str)
{
struct locate_instr *instr = new_locate_instr();
instr->cmd = locate_child;
instr->v.s = grecs_strdup(str);
}
static void
set_sibling_option(const char *str)
{
struct locate_instr *instr = new_locate_instr();
instr->cmd = locate_sibling;
instr->v.s = grecs_strdup(str);
}
static void
set_up_option(const char *str)
{
struct locate_instr *instr = new_locate_instr();
instr->cmd = locate_up;
instr->v.n = atoi(str);
}
static void
set_down_option(const char *str)
{
struct locate_instr *instr = new_locate_instr();
instr->cmd = locate_down;
instr->v.n = atoi(str);
}
static void
set_delim_option(const char *str)
{
flags_on |= *str & 0xff;
}
struct format_flag format_flag_table[] = {
{ "parent", "NAME", "print the parent node with the given NAME",
0, set_parent_option },
{ "child", "NAME", "print the child node with the given NAME",
0, set_child_option },
{ "up", "NUMBER", "ascend NUMBER of nodes",
0, set_up_option },
{ "down", "NUMBER", "descend NUMBER of nodes",
0, set_down_option },
{ "sibling",
"NAME", "locate a sibling with the given NAME and print it",
0, set_sibling_option },
{ "delim", "CHAR", "set path component delimiter",
0, set_delim_option },
{ "locus", NULL, "print locations",
GRECS_NODE_FLAG_LOCUS },
{ "path", NULL, "print paths",
GRECS_NODE_FLAG_PATH },
{ "value", NULL, "print values",
GRECS_NODE_FLAG_VALUE },
{ "quote", NULL, "always quote values",
GRECS_NODE_FLAG_QUOTE },
{ "never-quote", NULL, "never quote values",
GRECS_NODE_FLAG_NOQUOTE },
{ "quote-hex", NULL, "show unprintable characters as hex",
GRECS_NODE_FLAG_QUOTE_HEX },
{ "descend", NULL, "descend into subnodes",
GRECS_NODE_FLAG_DESCEND },
{ "default", NULL, "set default options",
CFPEEK_NODE_FLAG_DEFAULT },
{ NULL }
};
static void
flags_help(FILE *stream)
{
struct format_flag *fp;
fprintf(stream, "[*] FLAGS is a comma-separated list of the following:\n");
for (fp = format_flag_table; fp->name; fp++) {
int n;
fprintf(stream, " ");
if (fp->arg)
n = fprintf(stream, "%s=%s", fp->name, fp->arg);
else
n = fprintf(stream, "[no]%s", fp->name);
if (n < 24)
n = 24 - n;
else
n = 2;
fprintf(stream, "%*s%s\n", n, "", fp->descr);
}
fputc('\n', stream);
}
static const char *
cmpasgn(const char *a, const char *b)
{
for (; *a && *b; a++, b++) {
if (*a != *b)
return NULL;
}
if (*a == 0 && *b == '=')
return b+1;
return NULL;
}
static void
set_flag(const char *str)
{
struct format_flag *fp;
for (fp = format_flag_table; fp->name; fp++) {
if (fp->arg) {
const char *arg = cmpasgn(fp->name, str);
if (arg) {
fp->handler(arg);
return;
}
} else if (strcmp(str, fp->name) == 0) {
if (fp->handler)
fp->handler(str);
else
flags_on |= fp->flag;
return;
} else if (strncmp(str, "no", 2) == 0 &&
strcmp(str + 2, fp->name) == 0) {
if (fp->handler)
fp->handler(str);
else
flags_off |= fp->flag;
return;
}
}
grecs_error(NULL, 0, "unknown option: %s", str);
}
OPTIONS_BEGIN("cfpeek",
[],
[],
[],
[],
[])
GROUP(Output control)
OPTION(format,H,[],
[])
BEGIN
int i;
struct wordsplit ws;
ws.ws_delim = ",";
if (wordsplit(optarg, &ws,
WRDSF_DELIM|WRDSF_SQUEEZE_DELIMS|
WRDSF_NOVAR|WRDSF_NOCMD|WRDSF_SHOWERR))
exit(EX_SOFTWARE);
for (i = 0; i < ws.ws_wordc; i++) {
set_flag(ws.ws_wordv[i]);
}
wordsplit_free(&ws);
END
OPTION(quiet,q,,
[])
BEGIN
quiet_option++;
END
GROUP(Modifiers)
OPTION(parser,p,[],
[])
BEGIN
if (strcasecmp(optarg, "GRECS") == 0) {
grecs_parser_fun = grecs_grecs_parser;
if (preproc_settings == PREPROC_NOT_SET)
preproc_settings = PREPROC_DEFAULT;
} else if (strcasecmp(optarg, "META1") == 0)
grecs_parser_fun = grecs_meta1_parser;
else if (strcasecmp(optarg, "BIND") == 0)
grecs_parser_fun = grecs_bind_parser;
else if (strcasecmp(optarg, "DHCPD") == 0)
grecs_parser_fun = grecs_dhcpd_parser;
else if (strcasecmp(optarg, "GIT") == 0)
grecs_parser_fun = grecs_git_parser;
else if (strcasecmp(optarg, "PATH") == 0)
grecs_parser_fun = grecs_path_parser;
else {
grecs_error(NULL, 0, "unsupported parser type");
exit(EX_USAGE);
}
END
OPTION(literal,L,,
[