📄 util.c
字号:
len = strlen(arg); if (strpbrk(arg, "*?[")) { struct dirent *di; DIR *d; if (!(d = opendir(abpos ? glob.arg_buf : "."))) return; while ((di = readdir(d)) != NULL) { char *dname = d_name(di); if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) continue; if (!wildmatch(arg, dname)) continue; call_glob_match(dname, strlen(dname), 1, slash ? arg + len + 1 : NULL, abpos, fbpos); } closedir(d); } else { call_glob_match(arg, len, 0, slash ? arg + len + 1 : NULL, abpos, fbpos); } if (slash) *slash = '/';}static inline void call_glob_match(const char *name, int len, int from_glob, char *arg, int abpos, int fbpos){ char *use_buf; ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + len + 2); memcpy(glob.arg_buf + abpos, name, len); abpos += len; glob.arg_buf[abpos] = '\0'; if (fbpos >= 0) { ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2); memcpy(glob.filt_buf + fbpos, name, len); fbpos += len; glob.filt_buf[fbpos] = '\0'; use_buf = glob.filt_buf; } else use_buf = glob.arg_buf; if (from_glob || (arg && len)) { STRUCT_STAT st; int is_dir; if (do_stat(glob.arg_buf, &st) != 0) return; is_dir = S_ISDIR(st.st_mode) != 0; if (arg && !is_dir) return; if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, use_buf, is_dir) < 0) return; } if (arg) { glob.arg_buf[abpos++] = '/'; glob.arg_buf[abpos] = '\0'; if (fbpos >= 0) { glob.filt_buf[fbpos++] = '/'; glob.filt_buf[fbpos] = '\0'; } glob_match(arg, abpos, fbpos); } else { ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1); if (!(glob.argv[glob.argc++] = strdup(glob.arg_buf))) out_of_memory("glob_match"); }}/* This routine performs wild-card expansion of the pathname in "arg". Any * daemon-excluded files/dirs will not be matched by the wildcards. Returns 0 * if a wild-card string is the only returned item (due to matching nothing). */int glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p){ int ret, save_argc; char *s; if (!arg) { if (glob.filt_buf) free(glob.filt_buf); free(glob.arg_buf); memset(&glob, 0, sizeof glob); return -1; } if (sanitize_paths) s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); else { s = strdup(arg); if (!s) out_of_memory("glob_expand"); clean_fname(s, CFN_KEEP_DOT_DIRS | CFN_KEEP_TRAILING_SLASH | CFN_COLLAPSE_DOT_DOT_DIRS); } ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); *glob.arg_buf = '\0'; glob.argc = save_argc = *argc_p; glob.argv = *argv_p; glob.maxargs = *maxargs_p; ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, 100); glob_match(s, 0, -1); /* The arg didn't match anything, so add the failed arg to the list. */ if (glob.argc == save_argc) { ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1); glob.argv[glob.argc++] = s; ret = 0; } else { free(s); ret = 1; } *maxargs_p = glob.maxargs; *argv_p = glob.argv; *argc_p = glob.argc; return ret;}/* This routine is only used in daemon mode. */void glob_expand_module(char *base1, char *arg, char ***argv_p, int *argc_p, int *maxargs_p){ char *p, *s; char *base = base1; int base_len = strlen(base); if (!arg || !*arg) return; if (strncmp(arg, base, base_len) == 0) arg += base_len; if (!(arg = strdup(arg))) out_of_memory("glob_expand_module"); if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand_module"); base_len++; for (s = arg; *s; s = p + base_len) { if ((p = strstr(s, base)) != NULL) *p = '\0'; /* split it at this point */ glob_expand(s, argv_p, argc_p, maxargs_p); if (!p) break; } free(arg); free(base);}/** * Convert a string to lower case **/void strlower(char *s){ while (*s) { if (isUpper(s)) *s = toLower(s); 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 null-terminated * string fits into destsize. */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 the null- * terminated whole fits in destsize. Your list of string pointers must 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;}int count_dir_elements(const char *p){ int cnt = 0, new_component = 1; while (*p) { if (*p++ == '/') new_component = (*p != '.' || (p[1] != '/' && p[1] != '\0')); else if (new_component) { new_component = 0; cnt++; } } return cnt;}/* Turns multiple adjacent slashes into a single slash (possible exception: * the preserving of two leading slashes at the start), drops all leading or * interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop * a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes * a trailing slash (perhaps after removing the aforementioned dot) unless * CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements * (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the * resulting name would be empty, returns ".". */unsigned int clean_fname(char *name, int flags){ char *limit = name - 1, *t = name, *f = name; int anchored; if (!name) return 0; if ((anchored = *f == '/') != 0) { *t++ = *f++;#ifdef __CYGWIN__ /* If there are exactly 2 slashes at the start, preserve * them. Would break daemon excludes unless the paths are * really treated differently, so used this sparingly. */ if (*f == '/' && f[1] != '/') *t++ = *f++;#endif } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { *t++ = *f++; *t++ = *f++; } while (*f) { /* discard extra slashes */ if (*f == '/') { f++; continue; } if (*f == '.') { /* discard interior "." dirs */ if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) { f += 2; continue; } if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR) break; /* collapse ".." dirs */ if (flags & CFN_COLLAPSE_DOT_DOT_DIRS && f[1] == '.' && (f[2] == '/' || !f[2])) { char *s = t - 1; if (s == name && anchored) { f += 2; continue; } while (s > limit && *--s != '/') {} if (s != t - 1 && (s < name || *s == '/')) { t = s + 1; f += 2; continue; } limit = t + 2; } } while (*f && (*t++ = *f++) != '/') {} } if (t > name+anchored && t[-1] == '/' && !(flags & CFN_KEEP_TRAILING_SLASH)) t--; if (t == name) *t++ = '.'; *t = '\0'; return t - name;}/* Make path appear as if a chroot had occurred. This handles a leading * "/" (either removing it or expanding it) and any leading or embedded * ".." components that attempt to escape past the module's top dir. * * If dest is NULL, a buffer is allocated to hold the result. It is legal * to call with the dest and the path (p) pointing to the same buffer, but * rootdir will be ignored to avoid expansion of the string. * * The rootdir string contains a value to use in place of a leading slash. * Specify NULL to get the default of "module_dir". * * The depth var is a count of how many '..'s to allow at the start of the * path. * * We also clean the path in a manner similar to clean_fname() but with a * few differences: * * Turns multiple adjacent slashes into a single slash, gets rid of "." dir * elements (INCLUDING a trailing dot dir), PRESERVES a trailing slash, and * ALWAYS collapses ".." elements (except for those at the start of the * string up to "depth" deep). If the resulting name would be empty, * change it into a ".". */char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, int flags){ char *start, *sanp; int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); if (dest != p) { int plen = strlen(p); if (*p == '/') { if (!rootdir) rootdir = module_dir; rlen = strlen(rootdir); depth = 0; p++; } if (dest) { if (rlen + plen + 1 >= MAXPATHLEN) return NULL; } else if (!(dest = new_array(char, rlen + plen + 1))) out_of_memory("sanitize_path"); if (rlen) { memcpy(dest, rootdir, rlen); if (rlen > 1) dest[rlen++] = '/'; } } if (drop_dot_dirs) { while (*p == '.' && p[1] == '/') p += 2; } start = sanp = dest + rlen; /* This loop iterates once per filename component in p, pointing at * the start of the name (past any prior slash) for each iteration. */ while (*p) { /* discard leading or extra slashes */ if (*p == '/') { p++; continue; } if (drop_dot_dirs) { if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { /* skip "." component */ p++; continue; } } if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* ".." component followed by slash or end */ if (depth <= 0 || sanp != start) { p += 2; if (sanp != start) { /* back up sanp one level */ --sanp; /* now pointing at slash */ while (sanp > start && sanp[-1] != '/') sanp--; } continue; } /* allow depth levels of .. at the beginning */ depth--; /* move the virtual beginning to leave the .. alone */ start = sanp + 3; } /* copy one component through next slash */ while (*p && (*sanp++ = *p++) != '/') {} } if (sanp == dest) { /* ended up with nothing, so put in "." component */ *sanp++ = '.'; } *sanp = '\0'; return dest;}/* 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. */int change_dir(const char *dir, int set_path_only){ static int initialised; unsigned int len; if (!initialised) { initialised = 1; if (getcwd(curr_dir, sizeof curr_dir - 1) == NULL) { rsyserr(FERROR, errno, "getcwd()"); exit_cleanup(RERR_FILESELECT); } curr_dir_len = strlen(curr_dir); } if (!dir) /* this call was probably just to initialize */ return 0; len = strlen(dir); if (len == 1 && *dir == '.') return 1; if (*dir == '/') { if (len >= sizeof curr_dir) { errno = ENAMETOOLONG; return 0; } if (!set_path_only && chdir(dir)) return 0; memcpy(curr_dir, dir, len + 1); } else { if (curr_dir_len + 1 + len >= sizeof curr_dir) { errno = ENAMETOOLONG; return 0; } curr_dir[curr_dir_len] = '/'; memcpy(curr_dir + curr_dir_len + 1, dir, len + 1); if (!set_path_only && chdir(curr_dir)) { curr_dir[curr_dir_len] = '\0'; return 0; } } curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS); if (sanitize_paths) { if (module_dirlen > curr_dir_len) module_dirlen = curr_dir_len; curr_dir_depth = count_dir_elements(curr_dir + module_dirlen); } if (verbose >= 5 && !set_path_only) rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir); return 1;}/* This will make a relative path absolute and clean it up via clean_fname(). * Returns the string, which might be newly allocated, or NULL on error. */char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr){ unsigned int len; if (*path != '/') { /* Make path absolute. */ int len = strlen(path); if (curr_dir_len + 1 + len >= sizeof curr_dir) return NULL; curr_dir[curr_dir_len] = '/'; memcpy(curr_dir + curr_dir_len + 1, path, len + 1); if (!(path = strdup(curr_dir))) out_of_memory("normalize_path"); curr_dir[curr_dir_len] = '\0'; } else if (force_newbuf) { if (!(path = strdup(path))) out_of_memory("normalize_path"); } len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); if (len_ptr) *len_ptr = len; return path;}/** * Return a quoted string with the full pathname of the indicated filename. * The string " (in MODNAME)" may also be appended. The returned pointer * remains valid until the next time full_fname() is called. **/char *full_fname(const char *fn){ static char *result = NULL; char *m1, *m2, *m3; char *p1, *p2; if (result) free(result); if (*fn == '/') p1 = p2 = ""; else { p1 = curr_dir + module_dirlen; for (p2 = p1; *p2 == '/'; p2++) {} if (*p2) p2 = "/"; } if (module_id >= 0) { m1 = " (in "; m2 = lp_name(module_id); m3 = ")"; } else m1 = m2 = m3 = ""; if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) <= 0) out_of_memory("full_fname"); return result;}static char partial_fname[MAXPATHLEN];char *partial_dir_fname(const char *fname){ char *t = partial_fname; int sz = sizeof partial_fname; const char *fn; if ((fn = strrchr(fname, '/')) != NULL) { fn++; if (*partial_dir != '/') { int len = fn - fname; strncpy(t, fname, len); /* safe */ t += len; sz -= len; } } else fn = fname; if ((int)pathjoin(t, sz, partial_dir, fn) >= sz) return NULL; if (daemon_filter_list.head) { t = strrchr(partial_fname, '/'); *t = '\0'; if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0) return NULL; *t = '/'; if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0) return NULL; } return partial_fname;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -