From: Olivier Matz Date: Tue, 28 Dec 2010 22:49:35 +0000 (+0100) Subject: cmdline: allow quoted strings X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=5dbaf53aa2c6187d464108e360621ebfff6e0bc4;p=libcmdline.git cmdline: allow quoted strings Signed-off-by: Olivier Matz --- diff --git a/src/extension_example/parse_obj_list.c b/src/extension_example/parse_obj_list.c index 0781987..21fc019 100644 --- a/src/extension_example/parse_obj_list.c +++ b/src/extension_example/parse_obj_list.c @@ -46,7 +46,7 @@ struct cmdline_token_ops token_obj_list_ops = { .get_help = get_help_obj_list, }; -int +int parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) { struct token_obj_list *tk2 = (struct token_obj_list *)tk; @@ -54,27 +54,21 @@ parse_obj_list(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) struct object *o; unsigned int token_len = 0; - if (*buf == 0) - return -1; - - while(!cmdline_isendoftoken(buf[token_len])) - token_len++; + token_len = strlen(buf); SLIST_FOREACH(o, tkd->list, next) { - if (token_len != strlen(o->name)) - continue; - if (strncmp(buf, o->name, token_len)) + if (strcmp(buf, o->name)) continue; break; } if (!o) /* not found */ return -1; - + /* store the address of object in structure */ if (res) *(struct object **)res = o; - return token_len; + return token_len; } int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk) @@ -90,7 +84,7 @@ int complete_get_nb_obj_list(cmdline_parse_token_hdr_t *tk) return ret; } -int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, +int complete_get_elt_obj_list(cmdline_parse_token_hdr_t *tk, int idx, char *dstbuf, unsigned int size) { struct token_obj_list *tk2 = (struct token_obj_list *)tk; diff --git a/src/genconf/parse_confnode.c b/src/genconf/parse_confnode.c index b4da9dd..5c8202b 100644 --- a/src/genconf/parse_confnode.c +++ b/src/genconf/parse_confnode.c @@ -142,20 +142,13 @@ parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) struct confnode *n; struct confnode_list l; unsigned int token_len = 0; - char token[BUFSIZ]; + char token[CMDLINE_MAX_TOKEN_SIZE]; - if (*buf == 0) + /* if token is too big... */ + token_len = snprintf(token, sizeof(token), "%s", buf); + if (token_len >= sizeof(token)) return -1; - while(!cmdline_isendoftoken(buf[token_len])) - token_len++; - - if (token_len > sizeof(token) - 1) - return -1; - - memcpy(token, buf, token_len); - token[token_len] = '\0'; - TAILQ_INIT(&l); /* absolute path */ @@ -181,7 +174,7 @@ parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) if (res) *(struct confnode **)res = TAILQ_FIRST(&l); - return token_len; + return token_len; } int complete_get_nb_conf_node(cmdline_parse_token_hdr_t *tk) diff --git a/src/lib/cmdline.c b/src/lib/cmdline.c index 9ad2bbe..d44e44b 100644 --- a/src/lib/cmdline.c +++ b/src/lib/cmdline.c @@ -86,6 +86,8 @@ cmdline_valid_buffer(struct rdline *rdl, const char *buf, cmdline_printf(cl, "Command not found\n"); else if (ret == CMDLINE_PARSE_BAD_ARGS) cmdline_printf(cl, "Bad arguments\n"); + else if (ret == CMDLINE_PARSE_UNTERMINATED_QUOTE) + cmdline_printf(cl, "Unterminated quote\n"); } static int diff --git a/src/lib/cmdline_parse.c b/src/lib/cmdline_parse.c index 215077e..37ccc23 100644 --- a/src/lib/cmdline_parse.c +++ b/src/lib/cmdline_parse.c @@ -64,6 +64,7 @@ #include #include #include +#include #include @@ -123,6 +124,77 @@ nb_common_chars(const char * s1, const char * s2) return i; } +/* quote a string and escape original quotes */ +int cmdline_quote_token(char *dst, unsigned dstlen, const char *src) +{ + unsigned s = 0, d = 0; + + /* the 2 quotes + '\0' */ + if (dstlen < 3) + return -EMSGSIZE; + + dst[d++] = '"'; + while (src[s] != '\0') { + if (d >= (dstlen-2)) + return -EMSGSIZE; + + if (src[s] == '"') + dst[d++] = '\\'; + if (src[s] == '\\' && src[s+1] == '"') + dst[d++] = '\\'; + + dst[d++] = src[s++]; + } + + if (d >= (dstlen-2)) + return -EMSGSIZE; + dst[d++] = '"'; + dst[d++] = '\0'; + return s; +} + +/* remove quote and stop when we reach the end of token */ +// XXX ret val comment +int cmdline_unquote_token(char *dst, unsigned dstlen, + const char *src) +{ + unsigned s = 0, d = 0; + int quoted = 0; + + while (src[s] != '\0') { + if (d >= dstlen) + return -EMSGSIZE; + + if (cmdline_isendoftoken(src[s]) && quoted == 0) + break; + + if (src[s] == '\\' && src[s+1] == '"') { + dst[d++] = '"'; + s += 2; + continue; + } + if (src[s] == '\\' && src[s+1] == '\\') { + dst[d++] = '\\'; + s += 2; + continue; + } + if (src[s] == '"') { + s++; + quoted = !quoted; + continue; + } + dst[d++] = src[s++]; + } + + if (quoted) + return -EINVAL; + + if (d >= (dstlen-1)) + return -EMSGSIZE; + dst[d++] = '\0'; + return s; +} + /** * try to match the buffer with an instruction (only the first * nb_match_token tokens if != 0). Return 0 if we match all the @@ -135,8 +207,9 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf, unsigned int token_num=0; cmdline_parse_token_hdr_t * token_p; unsigned int i=0; - int n = 0; + int n = 0, res; struct cmdline_token_hdr token_hdr; + char token_str[128]; token_p = inst->tokens[token_num]; if (token_p) @@ -154,14 +227,17 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf, if ( isendofline(*buf) || iscomment(*buf) ) break; + n = cmdline_unquote_token(token_str, sizeof(token_str), buf); + if (n == -EINVAL) + return -EINVAL; + if (result_buf) - n = token_hdr.ops->parse(token_p, buf, + res = token_hdr.ops->parse(token_p, token_str, (char *)result_buf + token_hdr.offset); else - n = token_hdr.ops->parse(token_p, buf, NULL); - - if (n < 0) + res = token_hdr.ops->parse(token_p, token_str, NULL); + if (res < 0) break; debug_printf("TK parsed (len=%d)\n", n); @@ -176,7 +252,7 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf, /* does not match */ if (i==0) - return -1; + return -ENOENT; /* in case we want to match a specific num of token */ if (nb_match_token) { @@ -294,6 +370,12 @@ cmdline_parse(struct cmdline *cl, const char * buf) } } } + else if (tok == -EINVAL) { + err = CMDLINE_PARSE_UNTERMINATED_QUOTE; + f = NULL; + debug_printf("Unterminated quote\n"); + break; + } inst_num ++; inst = ctx[inst_num]; diff --git a/src/lib/cmdline_parse.h b/src/lib/cmdline_parse.h index 30fe8e9..146f15c 100644 --- a/src/lib/cmdline_parse.h +++ b/src/lib/cmdline_parse.h @@ -73,12 +73,15 @@ #define CMDLINE_PARSE_AMBIGUOUS -1 #define CMDLINE_PARSE_NOMATCH -2 #define CMDLINE_PARSE_BAD_ARGS -3 +#define CMDLINE_PARSE_UNTERMINATED_QUOTE -4 /* return status for completion */ #define CMDLINE_PARSE_COMPLETE_FINISHED 0 #define CMDLINE_PARSE_COMPLETE_AGAIN 1 #define CMDLINE_PARSE_COMPLETED_BUFFER 2 +#define CMDLINE_MAX_TOKEN_SIZE 128 /* including '\0' */ + /** * Stores a pointer to the ops struct, and the offset: the place to * write the parsed result in the destination structure. @@ -178,4 +181,10 @@ int cmdline_complete(struct cmdline *cl, const char *buf, int *state, * isendofline(c)) */ int cmdline_isendoftoken(char c); +/* quote a string and escape original quotes */ +int cmdline_quote_token(char *dst, unsigned dstlen, const char *src); + +/* remove quote and stop when we reach the end of token */ +int cmdline_unquote_token(char *dst, unsigned dstlen, const char *src); + #endif /* _CMDLINE_PARSE_H_ */ diff --git a/src/lib/cmdline_parse_etheraddr.c b/src/lib/cmdline_parse_etheraddr.c index 2cefc62..e0528c5 100644 --- a/src/lib/cmdline_parse_etheraddr.c +++ b/src/lib/cmdline_parse_etheraddr.c @@ -117,18 +117,11 @@ cmdline_parse_etheraddr(__attribute__((unused)) cmdline_parse_token_hdr_t *tk, struct ether_addr *etheraddr = res; struct ether_addr *tmp; - if (! *buf) - return -1; - - while (!cmdline_isendoftoken(buf[token_len])) - token_len++; - /* if token is too big... */ - if (token_len >= ETHER_ADDRSTRLEN) + token_len = snprintf(ether_str, sizeof(ether_str), "%s", buf); + if (token_len >= sizeof(ether_str)) return -1; - snprintf(ether_str, token_len+1, "%s", buf); - tmp = my_ether_aton(ether_str); if (tmp == NULL) return -1; diff --git a/src/lib/cmdline_parse_ipaddr.c b/src/lib/cmdline_parse_ipaddr.c index 276d644..37b2f3b 100644 --- a/src/lib/cmdline_parse_ipaddr.c +++ b/src/lib/cmdline_parse_ipaddr.c @@ -299,18 +299,11 @@ cmdline_parse_ipaddr(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) char *prefix, *prefix_end; long prefixlen; - if (! *buf) - return -1; - - while (!cmdline_isendoftoken(buf[token_len])) - token_len++; - /* if token is too big... */ - if (token_len >= INET6_ADDRSTRLEN+4) + token_len = snprintf(ip_str, sizeof(ip_str), "%s", buf); + if (token_len >= sizeof(ip_str)) return -1; - snprintf(ip_str, token_len+1, "%s", buf); - /* convert the network prefix */ if (tk2->ipaddr_data.flags & CMDLINE_IPADDR_NETWORK) { prefix = strrchr(ip_str, '/'); diff --git a/src/lib/cmdline_parse_num.c b/src/lib/cmdline_parse_num.c index f6eeb8b..cd3ca1e 100644 --- a/src/lib/cmdline_parse_num.c +++ b/src/lib/cmdline_parse_num.c @@ -137,7 +137,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res) memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd)); - while ( st != ERROR && c && ! cmdline_isendoftoken(c) ) { + while (st != ERROR && c != '\0') { debug_printf("%c %x -> ", c, c); switch (st) { case START: diff --git a/src/lib/cmdline_parse_string.c b/src/lib/cmdline_parse_string.c index f8fd44b..775024c 100644 --- a/src/lib/cmdline_parse_string.c +++ b/src/lib/cmdline_parse_string.c @@ -102,60 +102,51 @@ get_next_token(const char *s) return NULL; } +static int parse_fixed_string(struct cmdline_token_string_data *sd, + const char *buf, unsigned token_len) +{ + unsigned int conf_token_len; + const char *str; + + str = sd->str; + for (str = sd->str; str != NULL ; str = get_next_token(str)) { + + conf_token_len = get_token_len(str); + + /* if token from config is too big... */ + if (conf_token_len >= STR_TOKEN_SIZE - 1) + continue; + + /* compare conf token and user token */ + if (token_len == conf_token_len && + strncmp(buf, str, token_len) == 0) + return 0; + } + + return -1; +} + int cmdline_parse_string(cmdline_parse_token_hdr_t *tk, const char *buf, void *res) { struct cmdline_token_string *tk2 = (struct cmdline_token_string *)tk; struct cmdline_token_string_data *sd = &tk2->string_data;; unsigned int token_len; - const char *str; - if (! *buf) + token_len = strlen(buf); + + if (token_len >= (STR_TOKEN_SIZE - 1) || token_len == 0) return -1; /* fixed string */ if (sd->str) { - str = sd->str; - do { - token_len = get_token_len(str); - - /* if token is too big... */ - if (token_len >= STR_TOKEN_SIZE - 1) { - continue; - } - - if ( strncmp(buf, str, token_len) ) { - continue; - } - - if ( !cmdline_isendoftoken(*(buf+token_len)) ) { - continue; - } - - break; - } while ( (str = get_next_token(str)) != NULL ); - - if (!str) + if (parse_fixed_string(sd, buf, token_len) < 0) return -1; } - /* unspecified string */ - else { - token_len=0; - while(!cmdline_isendoftoken(buf[token_len]) && - token_len < (STR_TOKEN_SIZE-1)) - token_len++; - - /* return if token too long */ - if (token_len >= STR_TOKEN_SIZE - 1) { - return -1; - } - } - if (res) { - /* we are sure that token_len is < STR_TOKEN_SIZE-1 */ - strncpy(res, buf, token_len); - *((char *)res + token_len) = 0; - } + /* we already checked that token_len is < STR_TOKEN_SIZE-1 */ + if (res) + strcpy(res, buf); return token_len; }