/* * cmdline.c * * Parse the command line based on a small set of tokens. * See the header file cmdline.h for info on how to use. * This is really just a small recursive descent parser. */ #include #include #include "mdx/cmdline.h" /* default tokens */ #define DEFAULT_PRE_OPTW_STR "--" #define DEFAULT_PRE_OPTC '-' #define DEFAULT_SEP_OPT_ARG ':' #define DEFAULT_SEP_ARG_ARG ',' #define DEFAULT_SEP_ARG_VAL '=' #define DEFAULT_SEP_VAL_VAL ',' /* allocate Cmdstr list with this maximum starting length */ #define INITMAX 10 /* prototypes for internal functions */ static int extend(Cmdline *cmd); static int parse_optw(Cmdline *cmd, const char *p, int narg); static int parse_optc(Cmdline *cmd, const char *p, int narg); static int parse_arg(Cmdline *cmd, const char *p, int narg); static int parse_val(Cmdline *cmd, const char *p, int narg); int cmdline_init(Cmdline *cmd, const char *optc_arg_str) { if ((cmd->cmdstr = (Cmdstr *) malloc(INITMAX * sizeof(Cmdstr)))==NULL) { return -1; } cmd->max = INITMAX; cmd->len = 0; cmd->optc_arg_str = optc_arg_str; cmd->pre_optw_str = DEFAULT_PRE_OPTW_STR; cmd->pre_optc = DEFAULT_PRE_OPTC; cmd->sep_opt_arg = DEFAULT_SEP_OPT_ARG; cmd->sep_arg_arg = DEFAULT_SEP_ARG_ARG; cmd->sep_arg_val = DEFAULT_SEP_ARG_VAL; cmd->sep_val_val = DEFAULT_SEP_VAL_VAL; return 0; } void cmdline_done(Cmdline *cmd) { int k; if (cmd != NULL && cmd->cmdstr != NULL) { for (k = 0; k < cmd->len; k++) { free(cmd->cmdstr[k].s); } free(cmd->cmdstr); } if (cmd != NULL) memset(cmd, 0, sizeof(Cmdline)); } int cmdline_custom(Cmdline *cmd, const char *pre_optw_str, int pre_optc, int sep_opt_arg, int sep_arg_arg, int sep_arg_val, int sep_val_val) { cmd->pre_optw_str = pre_optw_str; cmd->pre_optc = pre_optc; cmd->sep_opt_arg = sep_opt_arg; cmd->sep_arg_arg = sep_arg_arg; cmd->sep_arg_val = sep_arg_val; cmd->sep_val_val = sep_val_val; return 0; } Cmdstr cmdline_cmdstr(Cmdline *cmd, int k) { Cmdstr no_cmdstr = { NULL, CMD_NONE, 0 }; if (k >= 0 && k < cmd->len) { return cmd->cmdstr[k]; } return no_cmdstr; } int cmdline_parse(Cmdline *cmd, int argc, char **argv) { int slen = 0; int k; if (cmd->pre_optw_str != NULL) { slen = strlen(cmd->pre_optw_str); } for (k = 0; k < argc; k++) { /* check prefix for OPTW - call parse_optw() */ if (slen > 0 && strncmp(cmd->pre_optw_str, argv[k], slen)==0) { if (parse_optw(cmd, argv[k]+slen, k)) return -1; } /* check prefix for OPTC - call parse_optc() */ else if (cmd->pre_optc != 0 && argv[k][0] == cmd->pre_optc) { if (parse_optc(cmd, argv[k]+1, k)) return -1; } /* otherwise treat it as ARG - call parse_arg() */ else { if (parse_arg(cmd, argv[k], k)) return -1; } } return cmd->len; } int extend(Cmdline *cmd) { if (cmd->len == cmd->max) { void *tmp = realloc(cmd->cmdstr, 2*cmd->max*sizeof(Cmdstr)); if (tmp == NULL) return -1; cmd->cmdstr = (Cmdstr *) tmp; cmd->max *= 2; } return 0; } int parse_optw(Cmdline *cmd, const char *p, int narg) { char *q; if (p == NULL) return -1; if (strlen(p) == 0) { /* empty OPT - place the PREFIX as an ARG */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = strdup(cmd->pre_optw_str))==NULL) return -1; cmd->cmdstr[cmd->len].type = CMD_ARG; cmd->cmdstr[cmd->len].num = narg; cmd->len++; return 0; } q = strchr(p, cmd->sep_opt_arg); if (q == NULL) { /* place OPTW (no separator) */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = strdup(p))==NULL) return -1; cmd->cmdstr[cmd->len].type = CMD_OPTW; cmd->cmdstr[cmd->len].num = narg; cmd->len++; return 0; } else if (q == p) { /* no OPTW string preceding the separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_OPTW; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse ARG */ } else { /* place OPTW (precedes separator) */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = (char*) malloc(q-p+1))==NULL) return -1; strncpy(cmd->cmdstr[cmd->len].s, p, q-p); cmd->cmdstr[cmd->len].s[q-p] = '\0'; cmd->cmdstr[cmd->len].type = CMD_OPTW; cmd->cmdstr[cmd->len].num = narg; cmd->len++; } return parse_arg(cmd, q+1, narg); } int parse_optc(Cmdline *cmd, const char *p, int narg) { const char *q; int n, k; if (p == NULL) return -1; if (strlen(p) == 0) { /* empty OPT - place the PREFIX as an ARG */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = (char*) malloc(2))==NULL) return -1; cmd->cmdstr[cmd->len].s[0] = (char) cmd->pre_optc; cmd->cmdstr[cmd->len].s[1] = '\0'; cmd->cmdstr[cmd->len].type = CMD_ARG; cmd->cmdstr[cmd->len].num = narg; cmd->len++; return 0; } if ((q = strchr(p, cmd->sep_opt_arg))==NULL) { /* if no separator, make q point at end of string */ /* this works even if sep_opt_arg==0 */ q = p + strlen(p); } if (q == p) { /* no OPTC string preceding the separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_OPTC; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse ARG */ } /* number of singleton char opts in prefix of p that do not take an arg */ n = strcspn(p, cmd->optc_arg_str); /* assert: n <= q-p */ if (p[n] != cmd->sep_opt_arg) n++; for (k = 0; k < n; k++) { /* place OPTC (for each char) */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = (char*) malloc(2))==NULL) return -1; cmd->cmdstr[cmd->len].s[0] = p[k]; cmd->cmdstr[cmd->len].s[1] = '\0'; cmd->cmdstr[cmd->len].type = CMD_OPTC; cmd->cmdstr[cmd->len].num = narg; cmd->len++; } p += n; if (p < q) return parse_arg(cmd, p, narg); else if (*q != '\0') return parse_arg(cmd, q+1, narg); /* otherwise, no ARG to parse */ return 0; } int parse_arg(Cmdline *cmd, const char *p, int narg) { const char *q, *r, *s; if (p == NULL) return -1; if (strlen(p) == 0) { /* empty ARG - happens only if nothing follows separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_ARG; cmd->cmdstr[cmd->len].num = narg; cmd->len++; return 0; } if ((q = strchr(p, cmd->sep_arg_arg))==NULL) { /* if no separator, make q point at end of string */ /* this works even if sep_arg_arg==0 */ q = p + strlen(p); } if ((r = strchr(p, cmd->sep_arg_val))==NULL) { /* if no separator, make r point at end of string */ /* this works even if sep_arg_val==0 */ r = p + strlen(p); } /* set s to first separator found */ s = (q < r ? q : r); if (s == p) { /* no ARG string preceding separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_ARG; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse whatever is after separator */ } else { /* place ARG */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = (char*) malloc(s-p+1))==NULL) return -1; strncpy(cmd->cmdstr[cmd->len].s, p, s-p); cmd->cmdstr[cmd->len].s[s-p] = '\0'; cmd->cmdstr[cmd->len].type = CMD_ARG; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse whatever is after separator */ } if (*s == cmd->sep_arg_arg) { return parse_arg(cmd, s+1, narg); } else if (*s == cmd->sep_arg_val) { return parse_val(cmd, s+1, narg); } /* otherwise nothing left to parse */ return 0; } int parse_val(Cmdline *cmd, const char *p, int narg) { const char *q, *r, *s; if (p == NULL) return -1; if (strlen(p) == 0) { /* empty VAL - happens only if nothing follows separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_VAL; cmd->cmdstr[cmd->len].num = narg; cmd->len++; return 0; } if ((q = strchr(p, cmd->sep_val_val))==NULL) { /* if no separator, make q point at end of string */ /* this works even if sep_val_val==0 */ q = p + strlen(p); } if ((r = strchr(p, cmd->sep_arg_arg))==NULL) { /* if no separator, make r point at end of string */ /* this works even if sep_arg_arg==0 */ r = p + strlen(p); } /* set s to first separator found */ s = (q < r ? q : r); if (s == p) { /* no VAL string preceding separator - place ERR */ if (extend(cmd)) return -1; cmd->cmdstr[cmd->len].s = NULL; cmd->cmdstr[cmd->len].type = CMD_ERR_VAL; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse whatever is after separator */ } else { /* place VAL */ if (extend(cmd)) return -1; if ((cmd->cmdstr[cmd->len].s = (char*) malloc(s-p+1))==NULL) return -1; strncpy(cmd->cmdstr[cmd->len].s, p, s-p); cmd->cmdstr[cmd->len].s[s-p] = '\0'; cmd->cmdstr[cmd->len].type = CMD_VAL; cmd->cmdstr[cmd->len].num = narg; cmd->len++; /* still need to parse whatever is after separator */ } /* check for val_val separator first */ if (*s == cmd->sep_val_val) { return parse_val(cmd, s+1, narg); } else if (*s == cmd->sep_arg_arg) { return parse_arg(cmd, s+1, narg); } /* otherwise nothing left to parse */ return 0; }