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.
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 */
return ret;
}
+#if 0
static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
{
int i;
}
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;
return err;
close(fd);
return 1;
+#endif
}
__noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
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;
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;
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;
- 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:
- 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 (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);
+ free_argv(argv);
mutex_lock(mmd_mutex);
mmd->num_commands++;
mutex_unlock(mmd_mutex);
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++;
usage_txt=""
help_txt=""
perms_txt=""
- line_handler=0
template=0
template_name=""
template_prototype=""
D:)
desc_txt="$value"
;;
- L:)
- line_handler=1
- ;;
U:)
usage_txt="$value"
;;
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 ".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
{
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);
- free(argv);
- free(tmp);
+ free_argv(argv);
return ret;
}
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);
- argc += 1;
+ argc++;
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;
}
{
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;
- char *buf = para_strdup(mood_line);
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;
(mlt == ML_DENY? "deny" : "score"), mi->method);
ret = 1;
out:
- free(argv);
- free(buf);
+ free_argv(argv);
if (ret >= 0)
return ret;
if (mi) {
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);
- 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];
- 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*));
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;
}
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.
*
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;
+}
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(). */
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);