diff options
Diffstat (limited to 'src/clparser.c')
-rw-r--r-- | src/clparser.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/src/clparser.c b/src/clparser.c new file mode 100644 index 0000000..6dbd62a --- /dev/null +++ b/src/clparser.c @@ -0,0 +1,412 @@ +/* clparser.c - Command line parser for ck -----------------------------*- C -*- + * + * This file is part of ck, the config keeper + * + * ----------------------------------------------------------------------------- + * + * Copyright (C) 2018 Anastasis Grammenos + * GPLv3 (see LICENCE for the full notice) + * + * -------------------------------------------------------------------------- */ +#include "ckutil.h" +#include "clparser.h" +#include "confparser.h" +#include "ckinfo.h" +#include "ckerrlog.h" +#include "actionhelper.h" + +ERRLOG(parser); + +/* accepted commands */ +/* [0] is the count */ +const char* const strINIT[] = {"3", "init", "i", "-i"}; +const char* const strADD[] = {"3", "add", "a", "-a"}; +const char* const strDEL[] = {"4", "delete", "del","d", "-d"}; +const char* const strEDIT[] = {"3", "edit", "e", "-e"}; +const char* const strLIST[] = {"5", "list", "ls", "l", "-l", "-ls"}; +const char* const strSEARCH[] = {"4", "search", "grep", "s", "-s"}; +const char* const strHELP[] = {"5", "help", "h", "-?", "-h", "--help"}; +const char* const strRESTORE[] = {"3", "restore","r", "-r"}; +const char* const strConfDir[] = {"2", "--config", "-c"}; +const char* const strVerbose1[] = {"2", "--verbose", "-v"}; +const char* const strVersion[] = {"2", "version", "--version"}; +const char* const strVerbose2[] = {"2", "--Verbose", "-V"}; + +/* Number of opts */ +static int optNum; + +/* holds the list of the opts + * as given by the user */ +static const char **opts; + +/* points to the current token */ +static const char *token; + +/* the position to be read */ +static int pos = 0; + +/* Advance one token. + * Returns 1 if it exists + * 0 otherwise */ +int next_token() { + if (pos < optNum) { + token = opts[pos]; + pos++; + return 1; + } + else { + token = NULL; + return 0; + } +} + +static void fill_args_list(int arg_num, UserOpt *opt) { + for (int i = 0; i < arg_num; i++) { + if (next_token()) { + list_add(opt->args, token); + } + } +} + +/* When starting to parse the action, + * `pos` should be at 2 + * like so "ck ACTION ..." + * ^ */ +static int parse_INIT(UserOpt *opt) { + /* INIT expects 2 arguments + * starting from 0 */ + int arg_num = 2; + if (optNum != pos /* already consumed */ + arg_num) { + opt->err = PERR_INIT_WRONG; + return -1; + } + + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_ADD(UserOpt *opt) { + /* ADD expects 2 to 4 arguments */ + if (optNum < pos + 2 + || optNum > pos + 4) { + opt->err = PERR_ADD_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_DEL(UserOpt *opt) { + /* DEL expects 1 to 2 arguments */ + if (optNum < pos + 1 + || optNum > pos + 2) { + opt->err = PERR_DEL_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_EDIT(UserOpt *opt) { + /* EDIT expects 1 to 2 arguments */ + if (optNum < pos + 1 + || optNum > pos + 2) { + opt->err = PERR_EDIT_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_LIST(UserOpt *opt) { + /* List expects 1 to 6 arguments */ + if (optNum < pos + 1 + || optNum > pos + 6) { + opt->err = PERR_LIST_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_SEARCH(UserOpt *opt) { + /* Search expects a maximum of 1 argument */ + if (optNum < pos + 1 + || optNum > pos + 1) { + opt->err = PERR_SEARCH_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_HELP(UserOpt *opt) { + /* Help expects a maximum of 1 argument */ + if (optNum < pos + 1 + || optNum > pos + 1) { + opt->err = PERR_HELP_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static int parse_RESTORE(UserOpt *opt) { + /* Restore expects 1 to 2 arguments */ + if (optNum < pos + 1 + || optNum > pos + 2) { + opt->err = PERR_RESTORE_WRONG; + return -1; + } + + int arg_num = optNum - pos; + fill_args_list(arg_num, opt); + return 0; +} + +static void determine_action(UserOpt *opt) { + /* get action */ + if (!next_token()) { + opt->action = CK_WRONG_ACTION; + return; + } + + char actionName[STR_S] = ""; + opt->action = parser_get_action(token, actionName); + if (opt->action != CK_WRONG_ACTION) { + LOG("Action to perform: %s", actionName); + } + return; +} + +static int parse_vals(UserOpt *opt) { + /* find the action */ + determine_action(opt); + if (opt->action == CK_WRONG_ACTION) { + opt->err = PERR_UNKNOWN_ACTION; + return -1; + } + + switch (opt->action) { +#define X(ACTION) \ + case CKA_##ACTION: \ + return parse_##ACTION(opt); + CK_ACTIONS +#undef X + default: + return -1; + } +} + +CkAction parser_get_action(const char *name, char *actionName) { + int i; +#define X(ACTION) \ + for (i = 1; i < atoi(str##ACTION[0]) + 1; i++) { \ + if (strcmp(name, str##ACTION[i]) == 0) { \ + if (actionName) { \ + strcpy(actionName, str##ACTION[1]); \ + } \ + return CKA_##ACTION; \ + } \ + } + CK_ACTIONS; +#undef X + return CK_WRONG_ACTION; +} + +static UserOpt make_empty_user_opt() { + UserOpt opt; + opt.action = CK_WRONG_ACTION; + opt.err = PERR_NOERR; + opt.confDir = NULL; + opt.args = list_make_new(); + return opt; +} + + +/* called to free the resources + * UserOpt holds */ +void free_user_opt(UserOpt *opt) { + if (opt->confDir != NULL) { + free(opt->confDir); + } + list_free(opt->args); +} + +/* find the correct config */ +static int get_config(UserOpt *opt) { + /* If it's a cli option */ + if (next_token()) { + for (int i = 1; i < atoi(strConfDir[0]) + 1; i++) { + if (strcmp(token, strConfDir[i]) == 0) { + if (!next_token()) { + ERR("Config needs a value"); + return -1; + } + char dir[STR_L] = ""; + realpath(token, dir); + if (!util_is_dir(dir)) { + ERR("%s is not a directory", token); + return -1; + } + opt->confDir = malloc(strlen(dir) + 1); + strcpy(opt->confDir, dir); + // remove trailing `/` + if (opt->confDir[strlen(dir) - 1] == '/') { + opt->confDir[strlen(dir) - 1] = '\0'; + } + return 0; + } + } + /* rewind */ + pos = pos - 1; + token = opts[pos]; + } + + return find_config(opt); +} + +static int version() { + /* get first token */ + if (next_token()) { + for (int i = 1; i < atoi(strVersion[0]) + 1; i++) { + if (strcmp(token, strVersion[i]) == 0) { + print_version(); + return 1; + } + } + // rewind + pos = pos - 1; + token = opts[pos]; + } + return 0; +} + +static void verbose() { + /* get first token */ + if (next_token()) { + for (int i = 1; i < atoi(strVerbose1[0]) + 1; i++) { + if (strcmp(token, strVerbose1[i]) == 0) { + errlog_set_verbose(1); + return; + } + } + + for (int i = 1; i < atoi(strVerbose2[0]) + 1; i++) { + if (strcmp(token, strVerbose2[i]) == 0) { + errlog_set_verbose(2); + return; + } + } + + // rewind + pos = pos - 1; + token = opts[pos]; + } +} + +char * get_possible_action_strings(char *dest, CkAction ckAction) { + char buf[STR_M] = ""; + switch (ckAction) { +#define X(ACTION) \ + case CKA_##ACTION: \ + strcpy(buf, "{ "); \ + for (int i = 1; i < atoi(str##ACTION[0]); i++) { \ + strcat(buf, str##ACTION[i]); \ + strcat(buf, ", "); \ + } \ + strcat(buf, str##ACTION[atoi(str##ACTION[0])]); \ + strcat(buf, " }"); \ + break; + CK_ACTIONS +#undef X + default: + dest = NULL; + return NULL; + } + + strcpy(dest, buf); + return dest; +} + +static void print_parser_error(UserOpt *opt) { + char names[STR_M] = ""; + get_possible_action_strings(names, opt->action); + + switch (opt->err) { + case PERR_NOERR: + return; + case PERR_UNKNOWN_ACTION: + ERR("Unknown action: %s", token); + return; +#define X(ACTION) \ + case PERR_ ##ACTION## _WRONG: \ + HELP("Usage:\n%s", names); \ + print_##ACTION##_help(); \ + break; + CK_ACTIONS; +#undef X + } +} + +static void print_parser_help() { + char names[STR_M] = ""; + ckhelp("ck - the config keeper"); + ckhelp("Usage:"); + ckhelp("Init\t%s", get_possible_action_strings(names, CKA_INIT)); + ckhelp("Add\t%s", get_possible_action_strings(names, CKA_ADD)); + ckhelp("Delete\t%s", get_possible_action_strings(names, CKA_DEL)); + ckhelp("Edit\t%s", get_possible_action_strings(names, CKA_EDIT)); + ckhelp("List\t%s", get_possible_action_strings(names, CKA_LIST)); + ckhelp("Search\t%s", get_possible_action_strings(names, CKA_SEARCH)); + ckhelp("Restore\t%s", get_possible_action_strings(names, CKA_RESTORE)); + ckhelp("Help\t%s", get_possible_action_strings(names, CKA_HELP)); + report_help(); +} + +int parse_action(int argc, const char **argv, UserOpt *opt) { + /* make empty user opt */ + *opt = make_empty_user_opt(); + opts = argv; + optNum = argc; + /* skip the program name */ + next_token(); + /* set verbose level */ + verbose(); + /* handle version info */ + if (version()) { + return -1; + } + /* figure what is the config file */ + if (get_config(opt)) { + return -1; + } + /* If the remaining arguments are < 1 + * print help and exit */ + if (optNum - pos < 1) { + print_parser_help(); + return -1; + } + /* parse values */ + if (parse_vals(opt)) { + print_parser_error(opt); + return -1; + } + if (opt->err == PERR_NOERR) { + return 0; + } + print_parser_error(opt); + return -1; +} |