Use already existing expansion logic also for ex commands.
authorDaniel Carl <danielcarl@gmx.de>
Sat, 3 May 2014 13:22:25 +0000 (15:22 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Wed, 4 Jun 2014 20:05:04 +0000 (22:05 +0200)
src/ascii.h
src/ex.c
src/setting.c
src/util.c
src/util.h

index 928e128..8f6ae9c 100644 (file)
@@ -26,9 +26,6 @@
 #define VB_SPACE  0x08
 #define VB_PUNKT  0x10
 #define VB_CTRL   0x20
-#define VB_IDENT  0x40
-#define VB_ALPHA  (VB_UPPER|VB_LOWER)
-#define VB_ALNUM  (VB_ALPHA|VB_DIGIT)
 
 #define U  VB_UPPER
 #define L  VB_LOWER
 #define P  VB_PUNKT
 #define S  VB_SPACE
 #define C  VB_CTRL
-#define I  VB_IDENT
-#define LI VB_LOWER|VB_IDENT
-#define UI VB_UPPER|VB_IDENT
 #define SC VB_SPACE|VB_CTRL
-#define PI VB_PUNKT|VB_IDENT
 static const unsigned char chartable[256] = {
 /* 0x00-0x0f */  C,  C,  C,  C,  C,  C,  C,  C,  C, SC, SC,  C, SC, SC,  C,  C,
 /* 0x10-0x1f */  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,
 /* 0x20-0x2f */  S,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
 /* 0x30-0x3f */  D,  D,  D,  D,  D,  D,  D,  D,  D,  D,  P,  P,  P,  P,  P,  P,
-/* 0x40-0x4f */ PI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI,
-/* 0x50-0x5f */ UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI,  P,  P,  P,  P, PI,
-/* 0x60-0x6f */  P, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI,
-/* 0x70-0x7f */ LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI,  P,  P,  P,  P,  C,
+/* 0x40-0x4f */  P,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,
+/* 0x50-0x5f */  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  P,  P,  P,  P,  P,
+/* 0x60-0x6f */  P,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,
+/* 0x70-0x7f */  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  P,  P,  P,  P,  C,
 /* 0x80-0x8f */  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
 /* 0x90-0x9f */  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
 /* 0xa0-0xaf */  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
@@ -65,20 +58,18 @@ static const unsigned char chartable[256] = {
 #undef P
 #undef S
 #undef C
-#undef I
-#undef LI
-#undef UI
 #undef SC
 
-#define VB_IS_UPPER(c) ((chartable[(unsigned char)c] & VB_UPPER) != 0)
-#define VB_IS_LOWER(c) ((chartable[(unsigned char)c] & VB_LOWER) != 0)
-#define VB_IS_DIGIT(c) ((chartable[(unsigned char)c] & VB_DIGIT) != 0)
-#define VB_IS_PUNKT(c) ((chartable[(unsigned char)c] & VB_PUNKT) != 0)
-#define VB_IS_SPACE(c) ((chartable[(unsigned char)c] & VB_SPACE) != 0)
-#define VB_IS_CTRL(c)  ((chartable[(unsigned char)c] & VB_CTRL) != 0)
-#define VB_IS_IDENT(c) ((chartable[(unsigned char)c] & VB_IDENT) != 0)
-#define VB_IS_ALPHA(c) ((chartable[(unsigned char)c] & VB_ALPHA) != 0)
-#define VB_IS_ALNUM(c) ((chartable[(unsigned char)c] & VB_ALNUM) != 0)
+#define VB_IS_UPPER(c)     ((chartable[(unsigned char)c] & VB_UPPER) != 0)
+#define VB_IS_LOWER(c)     ((chartable[(unsigned char)c] & VB_LOWER) != 0)
+#define VB_IS_DIGIT(c)     ((chartable[(unsigned char)c] & VB_DIGIT) != 0)
+#define VB_IS_PUNKT(c)     ((chartable[(unsigned char)c] & VB_PUNKT) != 0)
+#define VB_IS_SPACE(c)     ((chartable[(unsigned char)c] & VB_SPACE) != 0)
+#define VB_IS_CTRL(c)      ((chartable[(unsigned char)c] & VB_CTRL) != 0)
+#define VB_IS_SEPARATOR(c) (VB_IS_SPACE(c) || c == '"' || c == '\'')
+#define VB_IS_ALPHA(c)     (VB_IS_LOWER(c) || VB_IS_UPPER(c))
+#define VB_IS_ALNUM(c)     (VB_IS_ALPHA(c) || VB_IS_DIGIT(c))
+#define VB_IS_IDENT(c)     (VB_IS_ALNUM(c) || c == '_')
 
 /* CSI (control sequence introducer) is the first byte of a control sequence
  * and is always followed by two bytes. */
index 8ad0096..81167da 100644 (file)
--- a/src/ex.c
+++ b/src/ex.c
@@ -117,7 +117,6 @@ static gboolean parse_command_name(const char **input, ExArg *arg);
 static gboolean parse_bang(const char **input, ExArg *arg);
 static gboolean parse_lhs(const char **input, ExArg *arg);
 static gboolean parse_rhs(const char **input, ExArg *arg);
-static void expand_input(const char **input, ExArg *arg);
 static void skip_whitespace(const char **input);
 static void free_cmdarg(ExArg *arg);
 static gboolean execute(const ExArg *arg);
@@ -645,7 +644,8 @@ static gboolean parse_lhs(const char **input, ExArg *arg)
  */
 static gboolean parse_rhs(const char **input, ExArg *arg)
 {
-    char quote = '\\';
+    char quote   = '\\';
+    int expflags = UTIL_EXP_TILDE|UTIL_EXP_DOLLAR|UTIL_EXP_SPECIAL;
 
     if (!*input || !**input) {
         return false;
@@ -670,9 +670,17 @@ static gboolean parse_rhs(const char **input, ExArg *arg)
             }
         } else { /* unquoted char */
             /* check for expansion placeholder */
-            if (arg->flags & EX_FLAG_EXP && strchr("%~", **input)) {
-                /* handle expansion */
-                expand_input(input, arg);
+            if (arg->flags & EX_FLAG_EXP) {
+                util_parse_expansion(input, arg->rhs, expflags);
+
+                if (VB_IS_SPACE(**input)) {
+                    /* add tilde expansion for next loop needs to be first
+                     * char or to be after a space */
+                    expflags |= UTIL_EXP_TILDE;
+                } else {
+                    /* remove tile expansion for next loop */
+                    expflags &= ~UTIL_EXP_TILDE;
+                }
             } else {
                 g_string_append_c(arg->rhs, **input);
             }
@@ -682,28 +690,6 @@ static gboolean parse_rhs(const char **input, ExArg *arg)
     return true;
 }
 
-static void expand_input(const char **input, ExArg *arg)
-{
-    const char *uri;
-    switch (**input) {
-        case '%':
-            if ((uri = GET_URI())) {
-                /* TODO check for modifiers like :h:t:r:e */
-                g_string_append(arg->rhs, uri);
-            }
-            break;
-
-        case '~':
-            (*input)++;
-            /* expand only ~/ because ~user is not handled at the moment */
-            if (**input == '/') {
-                g_string_append(arg->rhs, g_get_home_dir());
-                g_string_append_c(arg->rhs, **input);
-            }
-            break;
-    }
-}
-
 /**
  * Executes the command given by ExArg.
  */
index 9b3e252..36ceea1 100644 (file)
@@ -641,7 +641,7 @@ static SettingStatus ca_bundle(const Setting *s, const SettingType type)
         print_value(s, vb.config.cafile);
     } else {
         /* expand the given file and set it to the file database */
-        expanded         = util_expand(s->arg.s);
+        expanded         = util_expand(s->arg.s, UTIL_EXP_TILDE|UTIL_EXP_DOLLAR);
         vb.config.tls_db = g_tls_file_database_new(expanded, &error);
         g_free(expanded);
         if (error) {
index 64b2fd2..69c2b59 100644 (file)
@@ -26,6 +26,8 @@
 #include "ascii.h"
 #include "completion.h"
 
+extern VbCore vb;
+
 char *util_get_config_dir(void)
 {
     char *path = g_build_filename(g_get_user_config_dir(), PROJECT, NULL);
@@ -302,9 +304,10 @@ gboolean util_create_tmp_file(const char *content, char **file)
 char *util_build_path(const char *path, const char *dir)
 {
     char *fullPath = NULL, *fexp, *dexp, *p;
+    int expflags   = UTIL_EXP_TILDE|UTIL_EXP_DOLLAR;
 
     /* if the path could be expanded */
-    if ((fexp = util_expand(path))) {
+    if ((fexp = util_expand(path, expflags))) {
         if (*fexp == '/') {
             /* path is already absolute, no need to use given dir - there is
              * no need to free fexp, bacuse this should be done by the caller
@@ -312,7 +315,7 @@ char *util_build_path(const char *path, const char *dir)
             fullPath = fexp;
         } else if (dir && *dir) {
             /* try to expand also the dir given - this may be ~/path */
-            if ((dexp = util_expand(dir))) {
+            if ((dexp = util_expand(dir, expflags))) {
                 /* use expanded dir and append expanded path */
                 fullPath = g_build_filename(dexp, fexp, NULL);
                 g_free(dexp);
@@ -341,80 +344,128 @@ char *util_build_path(const char *path, const char *dir)
  *
  * Returned path must be g_freed.
  */
-char *util_expand(const char *src)
+char *util_expand(const char *src, int expflags)
 {
-    GString *dst  = g_string_new("");
-    GString *name;
-    gboolean start = true;  /* is start of string of subpart */
-    const char *env;
+    const char **input = &src;
     char *result;
+    GString *dst = g_string_new("");
+    int flags    = expflags;
+
+    while (**input) {
+        util_parse_expansion(input, dst, flags);
+        if (VB_IS_SEPARATOR(**input)) {
+            /* after space the tilde expansion is allowed */
+            flags = expflags;
+        } else {
+            /* remove tile expansion for next loop */
+            flags &= ~UTIL_EXP_TILDE;
+        }
+        /* move pointer to the next char */
+        (*input)++;
+    }
+
+    result = dst->str;
+    g_string_free(dst, false);
+
+    return result;
+}
+
+/**
+ * Reads given input and try to parse ~/, ~user, $VAR or ${VAR} expansion
+ * from the start of the input and moves the input pointer to the first
+ * not expanded char. If no expansion pattern was found, the first char is
+ * appended to given GString.
+ *
+ * @input:  String pointer with the content to be parsed.
+ * @str:    GString that will be filled with expanded content.
+ * @flags   Flags that determine which expansion are processed.
+ * Returns true if input started with expandable pattern.
+ */
+gboolean util_parse_expansion(const char **input, GString *str, int flags)
+{
+    GString *name;
+    const char *env, *prev;
     struct passwd *pwd;
+    gboolean expanded = false;
 
-    while (*src) {
-        /* expand ~/path and ~user */
-        if (*src == '~' && start) {
-            /* skip the ~ */
-            src++;
-            if (*src == '/') {
-                g_string_append(dst, util_get_home_dir());
-            } else {
-                name = g_string_new("");
-                /* look ahead to / space or end of string */
-                while (*src && *src != '/' && !VB_IS_SPACE(*src)) {
-                    g_string_append_c(name, *src);
-                    src++;
-                }
-                /* append the name to the destination string */
-                if ((pwd = getpwnam(name->str))) {
-                    g_string_append(dst, pwd->pw_dir);
-                }
-                g_string_free(name, true);
-            }
-        } else if (*src == '$') {
+    prev = *input;
+    if (flags & UTIL_EXP_TILDE && **input == '~') {
+        /* skip ~ */
+        (*input)++;
+
+        if (**input == '/') {
+            g_string_append(str, util_get_home_dir());
+            expanded = true;
+        } else {
+            /* look ahead to / space or end of string to get a possible
+             * username for ~user pattern */
             name = g_string_new("");
-            /* skip the $ */
-            src++;
-            /* look for ${VAR}*/
-            if (*src == '{') {
-                /* skip { */
-                src++;
-                /* look ahead to } or end of string */
-                while (*src && *src != '}') {
-                    g_string_append_c(name, *src);
-                    src++;
-                }
-                /* if the } was reached - skip this */
-                if (*src == '}') {
-                    src++;
-                }
-            } else { /* process $VAR */
-                /* look ahead to /, space or end of string */
-                while (*src && VB_IS_IDENT(*src)) {
-                    g_string_append_c(name, *src);
-                    src++;
-                }
+            /* current char is ~ that is skipped to get the user name */
+            while (VB_IS_IDENT(**input)) {
+                g_string_append_c(name, **input);
+                (*input)++;
             }
-            /* append the variable to the destination string */
-            if ((env = g_getenv(name->str))) {
-                g_string_append(dst, env);
+            /* append the name to the destination string */
+            if ((pwd = getpwnam(name->str))) {
+                g_string_append(str, pwd->pw_dir);
+                expanded = true;
             }
             g_string_free(name, true);
-        } else if (!VB_IS_ALNUM(*src)) {
-            /* if we match non alnum char - mark this as beginning of new part */
-            start = true;
-        } else {
-            /* we left the start of phrase behind */
-            start = false;
         }
-
-        g_string_append_c(dst, *src);
-        src++;
+        /* move pointer back to last expanded char */
+        (*input)--;
+    } else if (flags & UTIL_EXP_DOLLAR && **input == '$') {
+        /* skip the $ */
+        (*input)++;
+
+        name = g_string_new("");
+        /* look for ${VAR}*/
+        if (**input == '{') {
+            /* skip { */
+            (*input)++;
+
+            /* look ahead to } or end of string */
+            while (**input && **input != '}') {
+                g_string_append_c(name, **input);
+                (*input)++;
+            }
+            /* if the } was reached - skip this */
+            if (**input == '}') {
+                (*input)++;
+            }
+        } else { /* process $VAR */
+            /* look ahead to /, space or end of string */
+            while (VB_IS_IDENT(**input)) {
+                g_string_append_c(name, **input);
+                (*input)++;
+            }
+        }
+        /* append the variable to the destination string */
+        if ((env = g_getenv(name->str))) {
+            g_string_append(str, env);
+        }
+        /* move pointer back to last expanded char */
+        (*input)--;
+        /* variable are expanded even if they do not exists */
+        expanded = true;
+        g_string_free(name, true);
+    } else if (flags & UTIL_EXP_SPECIAL && **input == '%') {
+        const char *uri;
+        if ((uri = GET_URI())) {
+            /* TODO check for modifiers like :h:t:r:e */
+            g_string_append(str, uri);
+            expanded = true;
+        }
     }
 
-    result = dst->str;
-    g_string_free(dst, false);
+    if (!expanded) {
+        /* restore the pointer position if no expansion was found */
+        *input = prev;
+        /* take the char like it is */
+        g_string_append_c(str, **input);
+    }
 
-    return result;
+    return expanded;
 }
 
 /**
index ba74fb2..237f0ea 100644 (file)
 
 #include "main.h"
 
+enum {
+    UTIL_EXP_TILDE   = 0x01, /* ~/ and ~user expansion */
+    UTIL_EXP_DOLLAR  = 0x02, /* $ENV and ${ENV} expansion */
+    UTIL_EXP_SPECIAL = 0x04, /* expand % to current URI */
+};
+
 typedef gboolean (*Util_Comp_Func)(const char*, const char*);
 typedef void *(*Util_Content_Func)(const char*);
 
@@ -40,7 +46,8 @@ char* util_strcasestr(const char* haystack, const char* needle);
 char *util_str_replace(const char* search, const char* replace, const char* string);
 gboolean util_create_tmp_file(const char *content, char **file);
 char *util_build_path(const char *path, const char *dir);
-char *util_expand(const char *src);
+char *util_expand(const char *src, int expflags);
+gboolean util_parse_expansion(const char **input, GString *str, int flags);
 gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src);
 
 #endif /* end of include guard: _UTIL_H */