cmdline: allow quoted strings
[libcmdline.git] / src / lib / cmdline_parse.c
index 215077e..37ccc23 100644 (file)
@@ -64,6 +64,7 @@
 #include <inttypes.h>
 #include <ctype.h>
 #include <termios.h>
+#include <errno.h>
 
 #include <netinet/in.h>
 
@@ -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];