]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Replace split_args() by create_argv().
authorAndre Noll <maan@systemlinux.org>
Sun, 5 Jul 2009 16:11:32 +0000 (18:11 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 5 Jul 2009 16:11:32 +0000 (18:11 +0200)
The latter function is superior as it honors quotes and special characters like '\n'.
This allows for example to use whitespace characters in mood methods.
Fixup and simplify all callers accordingly.

The patch broke the grab command of para_audiod which is deactivated ATM and
will be fixed in a subsequent patch.

audiod.h
audiod_command.c
command.c
command_util.sh
exec.c
filter_common.c
mood.c
recv_common.c
string.c
string.h

index 6946fc7888487eb614ff5dc86a4ea3360d00a104..8108378a56efb234816b1c63a7863421e412b99c 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -34,12 +34,6 @@ struct audiod_command {
        const char *name;
        /** pointer to the function that handles the command */
        int (*handler)(int, int, char**);
        const char *name;
        /** pointer to the function that handles the command */
        int (*handler)(int, int, char**);
-       /**
-        * if the command prefers to handle the full line (rather than the usual
-        * argv[] array), it stores a pointer to the corresponding line handling
-        * function here. In this case, the above \a handler pointer must be NULL.
-        */
-       int (*line_handler)(int, char*);
        /** one-line description of the command */
        const char *description;
        /** summary of the command line options */
        /** one-line description of the command */
        const char *description;
        /** summary of the command line options */
index 98e83b3cefc1e40a17c7a7e902dcf914dd07a92c..e57283cebe32a7dab6955f9d519b2752bd09e967 100644 (file)
@@ -214,6 +214,7 @@ int com_stat(int fd, int argc, char **argv)
        return ret;
 }
 
        return ret;
 }
 
+#if 0
 static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
 {
        int i;
 static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
 {
        int i;
@@ -233,9 +234,14 @@ static struct filter_node *find_filter_node(int slot_num, int format, int filter
        }
        return NULL;
 }
        }
        return NULL;
 }
+#endif
 
 
-int com_grab(int fd, char *cmdline)
+int com_grab(int fd, __a_unused int argc, __a_unused char **argv)
 {
 {
+       client_write(fd, "grab is currently b0rken\n");
+       close(fd);
+       return 1;
+#if 0
        struct grab_client *gc;
        struct filter_node *fn;
        int i, err;
        struct grab_client *gc;
        struct filter_node *fn;
        int i, err;
@@ -269,6 +275,7 @@ err_out:
                return err;
        close(fd);
        return 1;
                return err;
        close(fd);
        return 1;
+#endif
 }
 
 __noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
 }
 
 __noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
@@ -345,7 +352,7 @@ static int check_perms(uid_t uid)
 int handle_connect(int accept_fd)
 {
        int i, argc, ret, clifd = -1;
 int handle_connect(int accept_fd)
 {
        int i, argc, ret, clifd = -1;
-       char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL;
+       char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
 
        struct sockaddr_un unix_addr;
        uid_t uid;
 
@@ -353,7 +360,7 @@ int handle_connect(int accept_fd)
        if (ret < 0)
                goto out;
        clifd = ret;
        if (ret < 0)
                goto out;
        clifd = ret;
-       ret = recv_cred_buffer(clifd, buf, MAXLINE - 1);
+       ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
        if (ret < 0)
                goto out;
        uid = ret;
        if (ret < 0)
                goto out;
        uid = ret;
@@ -361,36 +368,20 @@ int handle_connect(int accept_fd)
        ret = check_perms(uid);
        if (ret < 0)
                goto out;
        ret = check_perms(uid);
        if (ret < 0)
                goto out;
-       cmd = para_strdup(buf);
-       p = strchr(cmd, '\n');
-       if (!p)
-               p = "";
-       else {
-               *p = '\0';
-               p++;
-       }
-       for (i = 0; audiod_cmds[i].name; i++) {
-               int j;
-               if (strcmp(audiod_cmds[i].name, cmd))
+       ret = create_argv(buf, "\n", &argv);
+       if (ret < 0)
+               goto out;
+       argc = ret;
+       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
+       FOR_EACH_COMMAND(i) {
+               if (strcmp(audiod_cmds[i].name, argv[0]))
                        continue;
                        continue;
-               if (audiod_cmds[i].handler) {
-                       argc = split_args(buf, &argv, "\n");
-                       PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc);
-                       ret = audiod_cmds[i].handler(clifd, argc, argv);
-                       goto out;
-               }
-               for (j = 0; p[j]; j++)
-                       if (p[j] == '\n')
-                               p[j] = ' ';
-               PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p);
-               ret = audiod_cmds[i].line_handler(clifd, p);
+               ret = audiod_cmds[i].handler(clifd, argc, argv);
                goto out;
        }
        ret = -E_INVALID_AUDIOD_CMD;
 out:
                goto out;
        }
        ret = -E_INVALID_AUDIOD_CMD;
 out:
-       free(cmd);
-       free(buf);
-       free(argv);
+       free_argv(argv);
        if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
                char *tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
        if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
                char *tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
index f8b5e4ee50f47af295c2e4d2689d30fc031eb44f..70aefae10738cd0eaed813e47fe17602b0c531c7 100644 (file)
--- a/command.c
+++ b/command.c
@@ -824,10 +824,14 @@ __noreturn void handle_connect(int fd, const char *peername)
        if (ret < 0)
                goto err_out;
        /* valid command and sufficient perms */
        if (ret < 0)
                goto err_out;
        /* valid command and sufficient perms */
-       argc = split_args(command, &argv, "\n");
+       ret = create_argv(command, "\n", &argv);
+       if (ret < 0)
+               goto err_out;
+       argc = ret;
        PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u->name,
                        peername);
        ret = cmd->handler(&rc4c, argc, argv);
        PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name, u->name,
                        peername);
        ret = cmd->handler(&rc4c, argc, argv);
+       free_argv(argv);
        mutex_lock(mmd_mutex);
        mmd->num_commands++;
        mutex_unlock(mmd_mutex);
        mutex_lock(mmd_mutex);
        mmd->num_commands++;
        mutex_unlock(mmd_mutex);
@@ -839,7 +843,6 @@ net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 out:
        free(command);
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 out:
        free(command);
-       free(argv);
        mutex_lock(mmd_mutex);
        if (cmd && (cmd->perms & AFS_WRITE) && ret >= 0)
                mmd->events++;
        mutex_lock(mmd_mutex);
        if (cmd && (cmd->perms & AFS_WRITE) && ret >= 0)
                mmd->events++;
index a735d3c247204ff409804594446a38c3071a0d24..f388b2cfb152db7cf34e0b2f84313ba26a209903 100755 (executable)
@@ -63,7 +63,6 @@ read_one_command()
        usage_txt=""
        help_txt=""
        perms_txt=""
        usage_txt=""
        help_txt=""
        perms_txt=""
-       line_handler=0
        template=0
        template_name=""
        template_prototype=""
        template=0
        template_name=""
        template_prototype=""
@@ -90,9 +89,6 @@ read_one_command()
                D:)
                        desc_txt="$value"
                        ;;
                D:)
                        desc_txt="$value"
                        ;;
-               L:)
-                       line_handler=1
-                       ;;
                U:)
                        usage_txt="$value"
                        ;;
                U:)
                        usage_txt="$value"
                        ;;
@@ -209,12 +205,8 @@ dump_proto()
        else
                echo ' * \param fd The file descriptor to send output to.'
        fi
        else
                echo ' * \param fd The file descriptor to send output to.'
        fi
-       if test $line_handler -eq 0; then
-               echo ' * \param argc The number of arguments.'
-               echo ' * \param argv The argument vector.'
-       else
-               echo ' * \param cmdline The full command line.'
-       fi
+       echo ' * \param argc The number of arguments.'
+       echo ' * \param argv The argument vector.'
        echo ' * '
        echo " * Usage: $usage_txt"
        echo ' * '
        echo ' * '
        echo " * Usage: $usage_txt"
        echo ' * '
@@ -239,12 +231,7 @@ dump_array_member()
 {
        echo '{'
        echo ".name = \"$name_txt\","
 {
        echo '{'
        echo ".name = \"$name_txt\","
-       if test $line_handler -eq 0; then
-               echo ".handler = com_$name_txt,"
-       else
-               echo ".handler = NULL,"
-               echo ".line_handler = com_$name_txt,"
-       fi
+       echo ".handler = com_$name_txt,"
        if test -n "$perms_txt"; then
                echo ".perms = $perms_txt,"
        fi
        if test -n "$perms_txt"; then
                echo ".perms = $perms_txt,"
        fi
diff --git a/exec.c b/exec.c
index 86a483cfd8e704eda8907d7399e891f603633393..7fc4ae069e0ad70902aaa1c9ff2e9721212e7392 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -137,11 +137,11 @@ int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds)
 {
        int ret;
        char **argv;
 {
        int ret;
        char **argv;
-       char *tmp = para_strdup(cmdline);
 
 
-       split_args(tmp, &argv, " \t");
+       ret = create_argv(cmdline, " \t", &argv);
+       if (ret < 0)
+               return ret;
        ret = para_exec(pid, argv[0], argv, fds);
        ret = para_exec(pid, argv[0], argv, fds);
-       free(argv);
-       free(tmp);
+       free_argv(argv);
        return ret;
 }
        return ret;
 }
index 1b02e1cdfbd1c106ae8ec69125af108ae24b8d8f..2c74e8c702ad729928bbac1835a7f28d131e5d83 100644 (file)
@@ -208,15 +208,18 @@ static int parse_filter_args(int filter_num, char *options, void **conf)
        if (!f->parse_config)
                return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
 //     PARA_DEBUG_LOG("options: %s\n", options);
        if (!f->parse_config)
                return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
 //     PARA_DEBUG_LOG("options: %s\n", options);
-       argc = split_args(options, &argv, " \t");
-//             PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
+       argc = create_argv(options, " \t", &argv);
+       if (argc < 0)
+               return -E_BAD_FILTER_OPTIONS;
+       PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
        for (i = argc - 1; i >= 0; i--)
                argv[i + 1] = argv[i];
        argv[0] = para_strdup(f->name);
        for (i = argc - 1; i >= 0; i--)
                argv[i + 1] = argv[i];
        argv[0] = para_strdup(f->name);
-       argc += 1;
+       argc++;
        ret = f->parse_config(argc, argv, conf);
        ret = f->parse_config(argc, argv, conf);
-       free(argv[0]);
-       free(argv);
+       free(argv[argc - 1]);
+       argv[argc - 1] = NULL;
+       free_argv(argv);
        return ret < 0? ret : filter_num;
 }
 
        return ret < 0? ret : filter_num;
 }
 
diff --git a/mood.c b/mood.c
index 32a10e56503147819cbe0a27d3e2e96c1293de6e..cb1534bce4a243ce8ea4a10101a4c1a693300b3c 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -393,17 +393,17 @@ static int parse_mood_line(char *mood_line, void *data)
 {
        struct mood_line_parser_data *mlpd = data;
        char **argv;
 {
        struct mood_line_parser_data *mlpd = data;
        char **argv;
-       char *delim = " \t";
        unsigned num_words;
        char **w;
        int i, ret;
        enum mood_line_type mlt = ML_INVALID;
        struct mood_item *mi = NULL;
        unsigned num_words;
        char **w;
        int i, ret;
        enum mood_line_type mlt = ML_INVALID;
        struct mood_item *mi = NULL;
-       char *buf = para_strdup(mood_line);
 
        mlpd->line_num++;
 
        mlpd->line_num++;
-       num_words = split_args(buf, &argv, delim);
-       ret = 1;
+       ret = create_argv(mood_line, " \t", &argv);
+       if (ret < 0)
+               return ret;
+       num_words = ret;
        if (!num_words) /* empty line */
                goto out;
        w = argv;
        if (!num_words) /* empty line */
                goto out;
        w = argv;
@@ -494,8 +494,7 @@ success:
                (mlt == ML_DENY? "deny" : "score"), mi->method);
        ret = 1;
 out:
                (mlt == ML_DENY? "deny" : "score"), mi->method);
        ret = 1;
 out:
-       free(argv);
-       free(buf);
+       free_argv(argv);
        if (ret >= 0)
                return ret;
        if (mi) {
        if (ret >= 0)
                return ret;
        if (mi) {
index 953e2a1143ebd28424fb09a978611eaf52bf28d6..c2ca29db6e7edf1d957de7e8f5029c9651ec21b6 100644 (file)
@@ -35,16 +35,17 @@ static void *parse_receiver_args(int receiver_num, char *options)
        void *conf;
 
 
        void *conf;
 
 
-//     PARA_DEBUG_LOG("%s, options: %s\n", r->name,
-//             options? options : "(none)");
+       PARA_DEBUG_LOG("%s, options: %s\n", r->name,
+               options? options : "(none)");
        if (options) {
                PARA_DEBUG_LOG("options: %s\n", options);
        if (options) {
                PARA_DEBUG_LOG("options: %s\n", options);
-               argc = split_args(options, &argv, " \t");
+               argc = create_argv(options, " \t", &argv);
+               if (argc < 0)
+                       return NULL;
                for (i = argc - 1; i >= 0; i--)
                        argv[i + 1] = argv[i];
                for (i = argc - 1; i >= 0; i--)
                        argv[i + 1] = argv[i];
-               argv[0] = para_strdup(r->name);
-               argc += 1;
-               PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
+               argv[0] = NULL;
+               argc++;
        } else {
                argc = 1;
                argv = para_malloc(2 * sizeof(char*));
        } else {
                argc = 1;
                argv = para_malloc(2 * sizeof(char*));
@@ -52,7 +53,8 @@ static void *parse_receiver_args(int receiver_num, char *options)
                argv[1] = NULL;
        }
        conf = r->parse_config(argc, argv);
                argv[1] = NULL;
        }
        conf = r->parse_config(argc, argv);
-       free(argv[0]);
+       for (i = 1; i < argc; i++)
+               free(argv[i]);
        free(argv);
        return conf;
 }
        free(argv);
        return conf;
 }
index 51985404817d032261d0f19ba2ebce35682156dd..5479e5b4fb43f6f0bac87e6cfba42ec011f30beb 100644 (file)
--- a/string.c
+++ b/string.c
@@ -258,56 +258,6 @@ __must_check __malloc char *para_homedir(void)
        return para_strdup(pw? pw->pw_dir : "/tmp");
 }
 
        return para_strdup(pw? pw->pw_dir : "/tmp");
 }
 
-/**
- * Split string and return pointers to its parts.
- *
- * \param args The string to be split.
- * \param argv_ptr Pointer to the list of substrings.
- * \param delim Delimiter.
- *
- * This function modifies \a args by replacing each occurrence of \a delim by
- * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically
- * and these pointers are initialized to point to the broken-up substrings
- * within \a args. A pointer to this array is returned via \a argv_ptr.
- *
- * \return The number of substrings found in \a args.
- */
-unsigned split_args(char *args, char *** const argv_ptr, const char *delim)
-{
-       char *p;
-       char **argv;
-       size_t n = 0, i, j;
-
-       p = args + strspn(args, delim);
-       for (;;) {
-               i = strcspn(p, delim);
-               if (!i)
-                       break;
-               p += i;
-               n++;
-               p += strspn(p, delim);
-       }
-       *argv_ptr = para_malloc((n + 1) * sizeof(char *));
-       argv = *argv_ptr;
-       i = 0;
-       p = args + strspn(args, delim);
-       while (p) {
-               argv[i] = p;
-               j = strcspn(p, delim);
-               if (!j)
-                       break;
-               p += strcspn(p, delim);
-               if (*p) {
-                       *p = '\0';
-                       p++;
-                       p += strspn(p, delim);
-               }
-               i++;
-       }
-       argv[n] = NULL;
-       return n;
-}
-
 /**
  * Get the own hostname.
  *
 /**
  * Get the own hostname.
  *
@@ -637,3 +587,151 @@ int get_loglevel_by_name(const char *txt)
                return LL_EMERG;
        return -1;
 }
                return LL_EMERG;
        return -1;
 }
+
+static int get_next_word(const char *buf, const char *delim,  char **word)
+{
+       enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
+               LSF_SINGLE_QUOTE = 4, LSF_DOUBLE_QUOTE = 8};
+       const char *in;
+       char *out;
+       int ret, state = 0;
+
+       out = para_malloc(strlen(buf) + 1);
+       *out = '\0';
+       *word = out;
+       for (in = buf; *in; in++) {
+               const char *p;
+
+               switch (*in) {
+               case '\\':
+                       if (state & LSF_BACKSLASH) /* \\ */
+                               goto copy_char;
+                       state |= LSF_BACKSLASH;
+                       state |= LSF_HAVE_WORD;
+                       continue;
+               case 'n':
+               case 't':
+                       if (state & LSF_BACKSLASH) { /* \n or \t */
+                               *out++ = (*in == 'n')? '\n' : '\t';
+                               state &= ~LSF_BACKSLASH;
+                               continue;
+                       }
+                       goto copy_char;
+               case '"':
+                       if (state & LSF_BACKSLASH) /* \" */
+                               goto copy_char;
+                       if (state & LSF_SINGLE_QUOTE) /* '" */
+                               goto copy_char;
+                       if (state & LSF_DOUBLE_QUOTE) {
+                               state &= ~LSF_DOUBLE_QUOTE;
+                               continue;
+                       }
+                       state |= LSF_HAVE_WORD;
+                       state |= LSF_DOUBLE_QUOTE;
+                       continue;
+               case '\'':
+                       if (state & LSF_BACKSLASH) /* \' */
+                               goto copy_char;
+                       if (state & LSF_DOUBLE_QUOTE) /* "' */
+                               goto copy_char;
+                       if (state & LSF_SINGLE_QUOTE) {
+                               state &= ~LSF_SINGLE_QUOTE;
+                               continue;
+                       }
+                       state |= LSF_HAVE_WORD;
+                       state |= LSF_SINGLE_QUOTE;
+                       continue;
+               }
+               for (p = delim; *p; p++) {
+                       if (*in != *p)
+                               continue;
+                       if (state & LSF_BACKSLASH)
+                               goto copy_char;
+                       if (state & LSF_SINGLE_QUOTE)
+                               goto copy_char;
+                       if (state & LSF_DOUBLE_QUOTE)
+                               goto copy_char;
+                       if (state & LSF_HAVE_WORD)
+                               goto success;
+                       break;
+               }
+               if (*p) /* ignore delimiter at the beginning */
+                       continue;
+copy_char:
+               state |= LSF_HAVE_WORD;
+               *out++ = *in;
+               state &= ~LSF_BACKSLASH;
+       }
+       ret = 0;
+       if (!(state & LSF_HAVE_WORD))
+               goto out;
+       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (state & LSF_BACKSLASH) {
+               PARA_ERROR_LOG("trailing backslash\n");
+               goto out;
+       }
+       if ((state & LSF_SINGLE_QUOTE) || (state & LSF_DOUBLE_QUOTE)) {
+               PARA_ERROR_LOG("unmatched quote character\n");
+               goto out;
+       }
+success:
+       *out = '\0';
+       return in - buf;
+out:
+       free(*word);
+       *word = NULL;
+       return ret;
+}
+
+/**
+ * Free an array of words created by create_argv().
+ *
+ * \param argv A pointer previously obtained by \ref create_argv().
+ */
+void free_argv(char **argv)
+{
+       int i;
+
+       for (i = 0; argv[i]; i++)
+               free(argv[i]);
+       free(argv);
+}
+
+/**
+ * Split a buffer into words.
+ *
+ * This parser honors single and double quotes, backslash-escaped characters
+ * and special characters like \p \\n. The result contains pointers to copies
+ * of the words contained in \a buf and has to be freed by using \ref
+ * free_argv().
+ *
+ * \param buf The buffer to be split.
+ * \param delim Each character in this string is treated as a separator.
+ * \param result The array of words is returned here.
+ *
+ * \return Number of words in \a buf, negative on errors.
+ */
+int create_argv(const char *buf, const char *delim, char ***result)
+{
+       char *word, **argv = para_malloc(2 * sizeof(char *));
+       const char *p;
+       int ret, num_words;
+
+       for (p = buf, num_words = 0; ; p += ret, num_words++) {
+               ret = get_next_word(p, delim, &word);
+               if (ret < 0)
+                       goto err;
+               if (!ret)
+                       break;
+               argv = para_realloc(argv, (num_words + 2) * sizeof(char*));
+               argv[num_words] = word;
+       }
+       argv[num_words] = NULL;
+       *result = argv;
+       return num_words;
+err:
+       while (num_words > 0)
+               free(argv[--num_words]);
+       free(argv);
+       return ret;
+}
index d28b0ac4633e5457f97da5b00465973f3c6bba6c..b3b960ab20385ed6c8f63ae2a38deb2c73e49ac1 100644 (file)
--- a/string.h
+++ b/string.h
@@ -67,7 +67,6 @@ __must_check char *para_basename(const char *name);
 void chop(char *buf);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 void chop(char *buf);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
-unsigned split_args(char *args, char *** const argv_ptr, const char *delim);
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
 /** Used for for_each_line() and for_each_line_ro(). */
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
 /** Used for for_each_line() and for_each_line_ro(). */
@@ -80,3 +79,5 @@ int para_atoi64(const char *str, int64_t *result);
 int para_atoi32(const char *str, int32_t *value);
 int get_loglevel_by_name(const char *txt);
 int read_size_header(const char *buf);
 int para_atoi32(const char *str, int32_t *value);
 int get_loglevel_by_name(const char *txt);
 int read_size_header(const char *buf);
+int create_argv(const char *buf, const char *delim, char ***result);
+void free_argv(char **argv);