/* This file is part of cfpeek -*- c -*- Copyright (C) 2011-2012, 2015 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,, []) BEGIN mode = MODE_LITERAL; END OPTION(reduce,r,, []) BEGIN reduce_option = 1; END OPTION(sort,S,, []) BEGIN sort_option = 1; END OPTION(matches,m,NUMBER, []) BEGIN max_matches = atoi(optarg); END OPTION(set,s,PATH=VAL, []) BEGIN struct wordsplit ws; ws.ws_delim = "="; if (wordsplit(optarg, &ws, WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_CESCAPES | WRDSF_DELIM)) { grecs_error(NULL, 0, "cannot split string: %s", wordsplit_strerror(&ws)); exit(EX_SOFTWARE); } if (ws.ws_wordc != 2) { grecs_error(NULL, 0, "set: invalid syntax"); exit(EX_USAGE); } OPTNODE(ws.ws_wordv[0], ws.ws_wordv[1]); wordsplit_free(&ws); END GROUP(Scripting) OPTION(lang,l,NAME, [