From: Olivier Matz Date: Mon, 21 Feb 2011 22:59:47 +0000 (+0100) Subject: cmdline: add a new 'file' token X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=946714d0ae0dc9293752b7fa9789b874df0f575d;p=libcmdline.git cmdline: add a new 'file' token Signed-off-by: Olivier Matz --- diff --git a/build/lib/Makefile b/build/lib/Makefile index 882b67c..56187d6 100755 --- a/build/lib/Makefile +++ b/build/lib/Makefile @@ -4,6 +4,7 @@ SRC += cmdline_parse.c cmdline_parse_num.c SRC += cmdline_parse_string.c cmdline_rdline.c SRC += cmdline_parse_ipaddr.c SRC += cmdline_parse_etheraddr.c +SRC += cmdline_parse_file.c OBJS = $(SRC:%.c=%.o) diff --git a/src/lib/cmdline_parse_file.c b/src/lib/cmdline_parse_file.c new file mode 100644 index 0000000..1c8d2e2 --- /dev/null +++ b/src/lib/cmdline_parse_file.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdline_parse.h" +#include "cmdline_parse_file.h" + +struct cmdline_complete_file_callback { + char *token; + DIR *dir; +}; + +static int +cmdline_parse_file(cmdline_parse_token_hdr_t *tk, const char *buf, void *res, + unsigned ressize) +{ + struct cmdline_token_file *tk2 = (struct cmdline_token_file *)tk; + struct cmdline_token_file_data *sd = &tk2->file_data;; + unsigned int token_len; + struct stat st; + int flags, ret; + char *tmp, *dname; + + if (res && ressize < FILENAME_SIZE) + return -1; + + token_len = strlen(buf); + if (token_len >= (FILENAME_SIZE - 1) || token_len == 0) + return -1; + + flags = sd->flags; + + if (flags & PARSE_FILE_F_CREATE) { + /* the directory must exist */ + tmp = strdup(buf); + dname = dirname(tmp); + ret = stat(dname, &st); + if (ret != 0) + ret = lstat(dname, &st); + free(tmp); + if (ret != 0) + return -1; + if (!S_ISDIR(st.st_mode)) + return -1; + } + else { + ret = stat(buf, &st); + if (ret != 0) + return -1; + if (flags & PARSE_FILE_F_DIRECTORY) + if (!S_ISDIR(st.st_mode)) + return -1; + } + + /* we already checked that token_len is < FILENAME_SIZE-1 */ + if (res) + strcpy(res, buf); + + return 0; +} + +/* + * This function is quite similar to dirname(3) except that: + * - it allocates the returned string and don't modify the argument + * - the result of dirname2() is not exactly the same than dirname() + * path dirname2 + * "/usr/lib" "/usr" + * "/usr/" "/usr" + * "/usr" "/" + * "usr" "." or "" if allow_empty == 1 + * "" "." or "" if allow_empty == 1 + * "/" "/" + * "." "." or "" if allow_empty == 1 + * "./" "." + * ".." "." or "" if allow_empty == 1 + * "../" ".." + */ +static char * +dirname2(const char *path, int allow_empty) +{ + char *s; + char *last_slash; + int len; + + len = strlen(path); + if (len == 0 && allow_empty == 0) { + s = strdup("."); + return s; + } + + s = strdup(path); + last_slash = strrchr(s, '/'); + if (last_slash == NULL) { + if (allow_empty) + s[0] = '\0'; + else + strcpy(s, "."); + } + else if (last_slash == s) + s[1] = '\0'; + else { + *last_slash = '\0'; + } + + return s; +} + +static int +cmdline_complete_file_start(cmdline_parse_token_hdr_t *tk, + const char *tokstr, void **opaque) +{ + char *dname; + struct cmdline_complete_file_callback *cb; + + *opaque = NULL; + cb = malloc(sizeof(*cb)); + if (cb == NULL) + return -1; + memset(cb, 0, sizeof(*cb)); + *opaque = cb; + + cb->token = strdup(tokstr); + /* we need to copy again tokstr because dirname() alters the string */ + dname = dirname2(tokstr, 0); + cb->dir = opendir(dname); + free(dname); + + if (cb->dir == NULL) + return -1; + + return 0; +} + +static int +cmdline_complete_file_iterate(cmdline_parse_token_hdr_t *tk, void **opaque, + char *dstbuf, unsigned int size) +{ + struct cmdline_token_file *tk2 = (struct cmdline_token_file *)tk; + struct cmdline_token_file_data *sd = &tk2->file_data; + struct cmdline_complete_file_callback *cb; + struct dirent *de; + struct stat st; + char *dname; + int len; + int need_join_slash = 1; + int flags = sd->flags; + + cb = *opaque; + /* read next dir name, skipping "." and ".." */ + while (1) { + de = readdir(cb->dir); + if (de == NULL) + return -1; + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0) + continue; + + + /* do we need a / to join dirname and basename ? */ + dname = dirname2(cb->token, 1); + len = strlen(dname); + if (len == 0 || dname[len - 1] == '/') + need_join_slash = 0; + + /* keep one byte for potential '/' */ + len = snprintf(dstbuf, size-1, "%s%s%s", dname, + need_join_slash ? "/" : "", de->d_name); + free(dname); + if (len < 0 || len >= size-1) + continue; + + /* append '/' if it's a directory */ + if (stat(dstbuf, &st) != 0) { + if (lstat(dstbuf, &st) != 0) + return -1; + } + if (S_ISDIR(st.st_mode)) { + strcat(dstbuf, "/"); + return 1; /* intermediate completion */ + } + /* skip non-directories */ + else if (flags & PARSE_FILE_F_DIRECTORY) + continue; + break; + } + + return 0; +} + +static void +cmdline_complete_file_end(cmdline_parse_token_hdr_t *tk, void **opaque) +{ + struct cmdline_complete_file_callback *cb; + + cb = *opaque; + + if (cb == NULL) + return; + + if (cb->dir) + closedir(cb->dir); + if (cb->token) + free(cb->token); + + free(cb); +} + + +static int +cmdline_help_file(cmdline_parse_token_hdr_t *tk, char *dstbuf, + unsigned int size) +{ + struct cmdline_token_file *tk2 = (struct cmdline_token_file *)tk; + struct cmdline_token_file_data *sd = &tk2->file_data;; + int flags; + + flags = sd->flags; + if (flags & PARSE_FILE_F_DIRECTORY) + snprintf(dstbuf, size, "DIR"); + else + snprintf(dstbuf, size, "FILE"); + + return 0; +} + +struct cmdline_token_ops cmdline_token_file_ops = { + .parse = cmdline_parse_file, + .complete_start = cmdline_complete_file_start, + .complete_iterate = cmdline_complete_file_iterate, + .complete_end = cmdline_complete_file_end, + .help = cmdline_help_file, +}; diff --git a/src/lib/cmdline_parse_file.h b/src/lib/cmdline_parse_file.h new file mode 100644 index 0000000..7cd7b95 --- /dev/null +++ b/src/lib/cmdline_parse_file.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009, Olivier MATZ + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PARSE_FILE_H_ +#define _PARSE_FILE_H_ + +#include "cmdline_parse.h" + +/* size of a parsed file */ +#define FILENAME_SIZE CMDLINE_MAX_TOKEN_SIZE + +typedef char cmdline_filename_t[FILENAME_SIZE]; + +#define PARSE_FILE_F_CREATE 0x01 /* file does not necessarilly exist */ +#define PARSE_FILE_F_DIRECTORY 0x02 /* must be a directory */ +struct cmdline_token_file_data { + int flags; +}; + +struct cmdline_token_file { + struct cmdline_token_hdr hdr; + struct cmdline_token_file_data file_data; +}; +typedef struct cmdline_token_file cmdline_parse_token_file_t; + +extern struct cmdline_token_ops cmdline_token_file_ops; + +#define TOKEN_FILE_INITIALIZER(structure, field, node_flags) \ +{ \ + .hdr = { \ + .ops = &cmdline_token_file_ops, \ + .offset = offsetof(structure, field), \ + }, \ + .file_data = { \ + .flags = node_flags, \ + }, \ +} + +#endif /* _PARSE_FILE_H_ */