aboutsummaryrefslogtreecommitdiffstats
path: root/src/clparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/clparser.c')
-rw-r--r--src/clparser.c412
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;
+}