X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/5c1b7bfd2a2a1e3f19be8040ca7cda5947a853ab..37c817eb8159366539d4a9c69bd78e74d4941c06:/util.c diff --git a/util.c b/util.c index ddb150ad..3eefd4a4 100644 --- a/util.c +++ b/util.c @@ -553,6 +553,58 @@ void strlower(char *s) } } +/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If + * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both + * strings + 1 (if '/' was inserted), regardless of whether the whole thing + * fits into destsize (including the terminating '\0'). */ +size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2) +{ + size_t len = strlcpy(dest, p1, destsize); + if (len < destsize - 1) { + if (!len || dest[len-1] != '/') + dest[len++] = '/'; + if (len < destsize - 1) + len += strlcpy(dest + len, p2, destsize - len); + else { + dest[len] = '\0'; + len += strlen(p2); + } + } + else + len += strlen(p2) + 1; /* Assume we'd insert a '/'. */ + return len; +} + +/* Join any number of strings together, putting them in "dest". The return + * value is the length of all the strings, regardless of whether they fit in + * destsize (including the terminating '\0'). Your list of string pointers + * should end with a NULL to indicate the end of the list. */ +size_t stringjoin(char *dest, size_t destsize, ...) +{ + va_list ap; + size_t len, ret = 0; + const char *src; + + va_start(ap, destsize); + while (1) { + if (!(src = va_arg(ap, const char *))) + break; + len = strlen(src); + ret += len; + if (destsize > 1) { + if (len >= destsize) + len = destsize - 1; + memcpy(dest, src, len); + destsize -= len; + dest += len; + } + } + *dest = '\0'; + va_end(ap); + + return ret; +} + void clean_fname(char *name) { char *p; @@ -708,57 +760,65 @@ void sanitize_path(char *p, char *reldir) char curr_dir[MAXPATHLEN]; +unsigned int curr_dir_len; /** - * Like chdir() but can be reversed with pop_dir() if @p save is set. - * It is also much faster as it remembers where we have been. + * Like chdir(), but it keeps track of the current directory (in the + * global "curr_dir"), and ensures that the path size doesn't overflow. + * Also cleans the path using the clean_fname() function. **/ -char *push_dir(char *dir, int save) +int push_dir(char *dir) { - char *ret = curr_dir; static int initialised; + unsigned int len; if (!initialised) { initialised = 1; getcwd(curr_dir, sizeof(curr_dir)-1); + curr_dir_len = strlen(curr_dir); } - if (!dir) return NULL; /* this call was probably just to initialize */ + if (!dir) /* this call was probably just to initialize */ + return 0; - if (chdir(dir)) return NULL; + len = strlen(dir); + if (len == 1 && *dir == '.') + return 1; - if (save) { - ret = strdup(curr_dir); - } + if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir) + return 0; + + if (chdir(dir)) + return 0; if (*dir == '/') { - strlcpy(curr_dir, dir, sizeof(curr_dir)); - } else if (dir[0] != '.' || dir[1] != '\0') { - strlcat(curr_dir,"/", sizeof(curr_dir)); - strlcat(curr_dir,dir, sizeof(curr_dir)); + memcpy(curr_dir, dir, len + 1); + curr_dir_len = len; + } else { + curr_dir[curr_dir_len++] = '/'; + memcpy(curr_dir + curr_dir_len, dir, len + 1); + curr_dir_len += len; } clean_fname(curr_dir); - return ret; + return 1; } -/** Reverse a push_dir() call */ +/** + * Reverse a push_dir() call. You must pass in an absolute path + * that was copied from a prior value of "curr_dir". + **/ int pop_dir(char *dir) { - int ret; - - ret = chdir(dir); - if (ret) { - free(dir); - return ret; - } - - strlcpy(curr_dir, dir, sizeof(curr_dir)); + if (chdir(dir)) + return 0; - free(dir); + curr_dir_len = strlcpy(curr_dir, dir, sizeof curr_dir); + if (curr_dir_len >= sizeof curr_dir) + curr_dir_len = sizeof curr_dir - 1; - return 0; + return 1; } /**