#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,
#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. */
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);
*/
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;
}
} 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);
}
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.
*/
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) {
#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);
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
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);
*
* 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;
}
/**
#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*);
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 */