diff --git a/include/glob.h b/include/glob.h index 76f6c1c6..477ddf2b 100644 --- a/include/glob.h +++ b/include/glob.h @@ -30,6 +30,7 @@ void globfree(glob_t *); #define GLOB_APPEND 0x20 #define GLOB_NOESCAPE 0x40 #define GLOB_PERIOD 0x80 +#define GLOB_TILDE 0x1000 #define GLOB_NOSPACE 1 #define GLOB_ABORTED 2 diff --git a/src/regex/glob.c b/src/regex/glob.c index 5b6ff124..16a0b1f7 100644 --- a/src/regex/glob.c +++ b/src/regex/glob.c @@ -8,6 +8,8 @@ #include #include #include "libc.h" +#include +#include struct match { @@ -154,13 +156,99 @@ static int sort(const void *a, const void *b) return strcmp(*(const char **)a, *(const char **)b); } +static int concat(char *buf, size_t len, const char *s1, const char *s2) +{ + size_t n1 = strnlen(s1, len); + len -= n1; + size_t n2 = strnlen(s2, len); + len -= n2; + if (!len) + return 0; + memcpy(buf, s1, n1); + memcpy(buf+n1, s2, n2+1); + return 1; +} + +/*"~" or "~/(...)" case*/ +static int expand_tilde_cur_user(const char *pat_after_tilde, char *new_pat, size_t new_pat_size) +{ + char *home; + struct passwd pw_store, *pw_result; + char pw_buf[LOGIN_NAME_MAX]; + + /*FIXME: add check for issetugid as in libc of openbsd?*/ + home = getenv("HOME"); + if(home == NULL) { + getpwuid_r(getuid(), &pw_store, pw_buf, sizeof(pw_buf), &pw_result); + if(pw_result == NULL) { + return 0; + } + home = pw_store.pw_dir; + } + + return concat(new_pat, new_pat_size, home, pat_after_tilde); +} + +/* "~user/(...) case*/ +static int expand_tilde_named_user(const char *pat_after_tilde, char *new_pat, size_t new_pat_size) +{ + struct passwd pw_store, *pw_result; + char pw_buf[1024], username[LOGIN_NAME_MAX]; + const char *slash_pos = strchr(pat_after_tilde, '/'); + if(slash_pos == NULL) { + return 0; + } + + ptrdiff_t pat_username_size = slash_pos - pat_after_tilde; + if(pat_username_size <= 0 || pat_username_size >= sizeof(username)) { + return 0; + } + strncpy(username, pat_after_tilde, pat_username_size); + username[pat_username_size] = '\0'; + + getpwnam_r(username, &pw_store, pw_buf, sizeof(pw_buf), &pw_result); + if (pw_result == NULL) + return 0; + + return concat(new_pat, new_pat_size, pw_store.pw_dir, slash_pos); +} + +/* expands: + * ~ into /home/user/ + * ~/asd into /home/user/asd + * ~user1/asd into /home/user1/asd + * the values for the home directory are taken from passwd + * + * returning true means successful expansion and that expanded_pat is valid + */ +static int expand_tilde(const char *pat, char *new_pat, int new_pat_size) +{ + const char *pat_after_tilde = pat + 1; + if(*pat_after_tilde == '\0' || *pat_after_tilde == '/') { + return expand_tilde_cur_user(pat_after_tilde, new_pat, new_pat_size); + } else { + return expand_tilde_named_user(pat_after_tilde, new_pat, new_pat_size); + } +} + int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) { - const char *p=pat, *d; + const char *p, *d; + const int tilde_flag = flags & GLOB_TILDE; + char new_pat[tilde_flag ? PATH_MAX : 1]; struct match head = { .next = NULL }, *tail = &head; size_t cnt, i; size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; int error = 0; + + /*even if expanding fails(e.g. expansion make pat too big) + * we should try to match the ~ or ~user literally*/ + int should_expand_tilde = tilde_flag && (pat[0] == '~'); + if(should_expand_tilde && expand_tilde(pat, new_pat, sizeof(new_pat))) { + p = new_pat; + } else { + p = pat; + } if (*p == '/') { for (; *p == '/'; p++);