+
+ curr_dir_len = clean_fname(curr_dir, 1);
+
+ return 1;
+}
+
+/**
+ * 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)
+{
+ if (chdir(dir))
+ return 0;
+
+ 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 1;
+}
+
+/* Return the filename, turning any non-printable characters into escaped
+ * characters (e.g. \n -> \012, \ -> \\). This ensures that outputting it
+ * cannot generate an empty line nor corrupt the screen. This function can
+ * return only MAX_SAFE_NAMES values at a time! The returned value can be
+ * longer than MAXPATHLEN (because we may be trying to output an error about
+ * a too-long filename)! */
+char *safe_fname(const char *fname)
+{
+#define MAX_SAFE_NAMES 4
+ static char fbuf[MAX_SAFE_NAMES][MAXPATHLEN*2];
+ static int ndx = 0;
+ int limit = sizeof fbuf / MAX_SAFE_NAMES - 1;
+ char *t;
+
+ ndx = (ndx + 1) % MAX_SAFE_NAMES;
+ for (t = fbuf[ndx]; *fname; fname++) {
+ if (*fname == '\\') {
+ if ((limit -= 2) < 0)
+ break;
+ *t++ = '\\';
+ *t++ = '\\';
+ } else if (!isprint(*(uchar*)fname)) {
+ if ((limit -= 4) < 0)
+ break;
+ sprintf(t, "\\%03o", *(uchar*)fname);
+ t += 4;
+ } else {
+ if (--limit < 0)
+ break;
+ *t++ = *fname;
+ }
+ }
+ *t = '\0';
+
+ return fbuf[ndx];
+}
+
+/**
+ * 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);
+
+ fn = safe_fname(fn);
+ if (*fn == '/')
+ p1 = p2 = "";
+ else {
+ p1 = curr_dir;
+ for (p2 = p1; *p2 == '/'; p2++) {}
+ if (*p2)
+ p2 = "/";
+ }
+ if (module_id >= 0) {
+ m1 = " (in ";
+ m2 = lp_name(module_id);
+ m3 = ")";
+ if (p1 == curr_dir) {
+ if (!lp_use_chroot(module_id)) {
+ char *p = lp_path(module_id);
+ if (*p != '/' || p[1])
+ p1 += strlen(p);
+ }
+ }
+ } else
+ m1 = m2 = m3 = "";
+
+ asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3);
+
+ 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 (server_filter_list.head) {
+ static int len;
+ if (!len)
+ len = strlen(partial_dir);
+ t[len] = '\0';
+ if (check_filter(&server_filter_list, partial_fname, 1) < 0)
+ return NULL;
+ t[len] = '/';
+ if (check_filter(&server_filter_list, partial_fname, 0) < 0)
+ return NULL;
+ }
+
+ return partial_fname;
+}
+
+/* If no --partial-dir option was specified, we don't need to do anything
+ * (the partial-dir is essentially '.'), so just return success. */
+int handle_partial_dir(const char *fname, int create)
+{
+ char *fn, *dir;
+
+ if (fname != partial_fname)
+ return 1;
+ if (!create && *partial_dir == '/')
+ return 1;
+ if (!(fn = strrchr(partial_fname, '/')))
+ return 1;
+
+ *fn = '\0';
+ dir = partial_fname;
+ if (create) {
+ STRUCT_STAT st;
+ int statret = do_lstat(dir, &st);
+ if (statret == 0 && !S_ISDIR(st.st_mode)) {
+ if (do_unlink(dir) < 0)
+ return 0;
+ statret = -1;
+ }
+ if (statret < 0 && do_mkdir(dir, 0700) < 0)
+ return 0;
+ } else
+ do_rmdir(dir);
+ *fn = '/';
+
+ return 1;
+}
+
+/** We need to supply our own strcmp function for file list comparisons
+ to ensure that signed/unsigned usage is consistent between machines. */
+int u_strcmp(const char *cs1, const char *cs2)
+{
+ const uchar *s1 = (const uchar *)cs1;
+ const uchar *s2 = (const uchar *)cs2;
+
+ while (*s1 && *s2 && (*s1 == *s2)) {
+ s1++; s2++;
+ }
+
+ return (int)*s1 - (int)*s2;
+}
+
+
+
+/**
+ * Determine if a symlink points outside the current directory tree.
+ * This is considered "unsafe" because e.g. when mirroring somebody
+ * else's machine it might allow them to establish a symlink to
+ * /etc/passwd, and then read it through a web server.
+ *
+ * Null symlinks and absolute symlinks are always unsafe.
+ *
+ * Basically here we are concerned with symlinks whose target contains
+ * "..", because this might cause us to walk back up out of the
+ * transferred directory. We are not allowed to go back up and
+ * reenter.
+ *
+ * @param dest Target of the symlink in question.
+ *
+ * @param src Top source directory currently applicable. Basically this
+ * is the first parameter to rsync in a simple invocation, but it's
+ * modified by flist.c in slightly complex ways.
+ *
+ * @retval True if unsafe
+ * @retval False is unsafe
+ *
+ * @sa t_unsafe.c
+ **/
+int unsafe_symlink(const char *dest, const char *src)
+{
+ const char *name, *slash;
+ int depth = 0;
+
+ /* all absolute and null symlinks are unsafe */
+ if (!dest || !*dest || *dest == '/')
+ return 1;
+
+ /* find out what our safety margin is */
+ for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ if (strncmp(name, "../", 3) == 0) {
+ depth = 0;
+ } else if (strncmp(name, "./", 2) == 0) {
+ /* nothing */
+ } else {
+ depth++;
+ }
+ }
+ if (strcmp(name, "..") == 0)
+ depth = 0;
+
+ for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ if (strncmp(name, "../", 3) == 0) {
+ /* if at any point we go outside the current directory
+ then stop - it is unsafe */
+ if (--depth < 0)
+ return 1;
+ } else if (strncmp(name, "./", 2) == 0) {
+ /* nothing */
+ } else {
+ depth++;
+ }
+ }
+ if (strcmp(name, "..") == 0)
+ depth--;
+
+ return (depth < 0);
+}
+
+
+/**
+ * Return the date and time as a string
+ **/
+char *timestring(time_t t)
+{
+ static char TimeBuf[200];
+ struct tm *tm = localtime(&t);
+
+#ifdef HAVE_STRFTIME
+ strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm);
+#else
+ strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf);
+#endif
+
+ if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
+ TimeBuf[strlen(TimeBuf)-1] = 0;
+ }
+
+ return(TimeBuf);
+}
+
+
+/**
+ * Sleep for a specified number of milliseconds.
+ *
+ * Always returns TRUE. (In the future it might return FALSE if
+ * interrupted.)
+ **/
+int msleep(int t)
+{
+ int tdiff = 0;
+ struct timeval tval, t1, t2;
+
+ gettimeofday(&t1, NULL);
+
+ while (tdiff < t) {
+ tval.tv_sec = (t-tdiff)/1000;
+ tval.tv_usec = 1000*((t-tdiff)%1000);
+
+ errno = 0;
+ select(0,NULL,NULL, NULL, &tval);
+
+ gettimeofday(&t2, NULL);
+ tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
+ (t2.tv_usec - t1.tv_usec)/1000;
+ }
+
+ return True;
+}
+
+
+/**
+ * Determine if two file modification times are equivalent (either
+ * exact or in the modification timestamp window established by
+ * --modify-window).
+ *
+ * @retval 0 if the times should be treated as the same
+ *
+ * @retval +1 if the first is later
+ *
+ * @retval -1 if the 2nd is later
+ **/
+int cmp_modtime(time_t file1, time_t file2)
+{
+ if (file2 > file1) {
+ if (file2 - file1 <= modify_window)
+ return 0;
+ return -1;
+ }
+ if (file1 - file2 <= modify_window)
+ return 0;
+ return 1;
+}
+
+
+#ifdef __INSURE__XX
+#include <dlfcn.h>
+
+/**
+ This routine is a trick to immediately catch errors when debugging
+ with insure. A xterm with a gdb is popped up when insure catches
+ a error. It is Linux specific.
+**/
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
+{
+ static int (*fn)();
+ int ret;
+ char *cmd;
+
+ asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'",
+ getpid(), getpid(), getpid());
+
+ if (!fn) {
+ static void *h;
+ h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+ }
+
+ ret = fn(a1, a2, a3, a4, a5, a6);
+
+ system(cmd);
+
+ free(cmd);
+
+ return ret;
+}
+#endif
+
+
+#define MALLOC_MAX 0x40000000
+
+void *_new_array(unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ return malloc(size * num);
+}
+
+void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ /* No realloc should need this, but just in case... */
+ if (!ptr)
+ return malloc(size * num);
+ return realloc(ptr, size * num);
+}
+
+/* Take a filename and filename length and return the most significant
+ * filename suffix we can find. This ignores suffixes such as "~",
+ * ".bak", ".orig", ".~1~", etc. */
+const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
+{
+ const char *suf, *s;
+ BOOL had_tilde;
+ int s_len;
+
+ /* One or more dots at the start aren't a suffix. */
+ while (fn_len && *fn == '.') fn++, fn_len--;
+
+ /* Ignore the ~ in a "foo~" filename. */
+ if (fn_len > 1 && fn[fn_len-1] == '~')
+ fn_len--, had_tilde = True;
+ else
+ had_tilde = False;
+
+ /* Assume we don't find an suffix. */
+ suf = "";
+ *len_ptr = 0;
+
+ /* Find the last significant suffix. */
+ for (s = fn + fn_len; fn_len > 1; ) {
+ while (*--s != '.' && s != fn) {}
+ if (s == fn)
+ break;
+ s_len = fn_len - (s - fn);
+ fn_len = s - fn;
+ if (s_len == 4) {
+ if (strcmp(s+1, "bak") == 0
+ || strcmp(s+1, "old") == 0)
+ continue;
+ } else if (s_len == 5) {
+ if (strcmp(s+1, "orig") == 0)
+ continue;
+ } else if (s_len > 2 && had_tilde
+ && s[1] == '~' && isdigit(*(uchar*)(s+2)))
+ continue;
+ *len_ptr = s_len;
+ suf = s;
+ if (s_len == 1)
+ break;
+ /* Determine if the suffix is all digits. */
+ for (s++, s_len--; s_len > 0; s++, s_len--) {
+ if (!isdigit(*(uchar*)s))
+ return suf;
+ }
+ /* An all-digit suffix may not be that signficant. */
+ s = suf;
+ }
+
+ return suf;
+}
+
+/* This is an implementation of the Levenshtein distance algorithm. It
+ * was implemented to avoid needing a two-dimensional matrix (to save
+ * memory). It was also tweaked to try to factor in the ASCII distance
+ * between changed characters as a minor distance quantity. The normal
+ * Levenshtein units of distance (each signifying a single change between
+ * the two strings) are defined as a "UNIT". */
+
+#define UNIT (1 << 16)
+
+uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2)
+{
+ uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
+ int32 cost;
+ int i1, i2;
+
+ if (!len1 || !len2) {
+ if (!len1) {
+ s1 = s2;
+ len1 = len2;
+ }
+ for (i1 = 0, cost = 0; i1 < len1; i1++)
+ cost += s1[i1];
+ return (int32)len1 * UNIT + cost;
+ }
+
+ for (i2 = 0; i2 < len2; i2++)
+ a[i2] = (i2+1) * UNIT;
+
+ for (i1 = 0; i1 < len1; i1++) {
+ diag = i1 * UNIT;
+ above = (i1+1) * UNIT;
+ for (i2 = 0; i2 < len2; i2++) {
+ left = a[i2];
+ if ((cost = *((uchar*)s1+i1) - *((uchar*)s2+i2)) != 0) {
+ if (cost < 0)
+ cost = UNIT - cost;
+ else
+ cost = UNIT + cost;
+ }
+ diag_inc = diag + cost;
+ left_inc = left + UNIT + *((uchar*)s1+i1);
+ above_inc = above + UNIT + *((uchar*)s2+i2);
+ a[i2] = above = left < above
+ ? (left_inc < diag_inc ? left_inc : diag_inc)
+ : (above_inc < diag_inc ? above_inc : diag_inc);
+ diag = left;
+ }
+ }
+
+ return a[len2-1];