+
+static int show_filelist_p(void)
+{
+ return verbose && recurse && !am_server;
+}
+
+static void start_filelist_progress(char *kind)
+{
+ rprintf(FINFO, "%s ... ", kind);
+ if ((verbose > 1) || do_progress)
+ rprintf(FINFO, "\n");
+ rflush(FINFO);
+}
+
+
+static void emit_filelist_progress(const struct file_list *flist)
+{
+ rprintf(FINFO, " %d files...\r", flist->count);
+}
+
+
+static void maybe_emit_filelist_progress(const struct file_list *flist)
+{
+ if (do_progress && show_filelist_p() && ((flist->count % 100) == 0))
+ emit_filelist_progress(flist);
+}
+
+
+static void finish_filelist_progress(const struct file_list *flist)
+{
+ if (do_progress) {
+ /* This overwrites the progress line */
+ rprintf(FINFO, "%d file%sto consider\n",
+ flist->count, flist->count == 1 ? " " : "s ");
+ } else {
+ rprintf(FINFO, "done\n");
+ }
+}
+
+void show_flist_stats(void)
+{
+ /* Nothing yet */
+}
+
+
+static struct string_area *string_area_new(int size)
+{
+ struct string_area *a;
+
+ if (size <= 0)
+ size = ARENA_SIZE;
+ a = malloc(sizeof(*a));
+ if (!a)
+ out_of_memory("string_area_new");
+ a->current = a->base = malloc(size);
+ if (!a->current)
+ out_of_memory("string_area_new buffer");
+ a->end = a->base + size;
+ a->next = NULL;
+
+ return a;
+}
+
+static void string_area_free(struct string_area *a)
+{
+ struct string_area *next;
+
+ for (; a; a = next) {
+ next = a->next;
+ free(a->base);
+ }
+}
+
+static char *string_area_malloc(struct string_area **ap, int size)
+{
+ char *p;
+ struct string_area *a;
+
+ /* does the request fit into the current space? */
+ a = *ap;
+ if (a->current + size >= a->end) {
+ /* no; get space, move new string_area to front of the list */
+ a = string_area_new(size > ARENA_SIZE ? size : ARENA_SIZE);
+ a->next = *ap;
+ *ap = a;
+ }
+
+ /* have space; do the "allocation." */
+ p = a->current;
+ a->current += size;
+ return p;
+}
+
+static char *string_area_strdup(struct string_area **ap, const char *src)
+{
+ char *dest = string_area_malloc(ap, strlen(src) + 1);
+ return strcpy(dest, src);
+}
+
+static void list_file_entry(struct file_struct *f)
+{
+ char perms[11];
+
+ if (!f->basename)
+ /* this can happen if duplicate names were removed */
+ return;
+
+ permstring(perms, f->mode);
+
+ if (preserve_links && S_ISLNK(f->mode)) {
+ rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
+ perms,
+ (double) f->length, timestring(f->modtime),
+ f_name(f), f->link);
+ } else {
+ rprintf(FINFO, "%s %11.0f %s %s\n",
+ perms,
+ (double) f->length, timestring(f->modtime),
+ f_name(f));
+ }
+}
+
+
+/**
+ * Stat either a symlink or its referent, depending on the settings of
+ * copy_links, copy_unsafe_links, etc.
+ *
+ * @return -1 on error; or 0. If a symlink, then @p Linkbuf (of size
+ * MAXPATHLEN) contains the symlink target.
+ **/
+int readlink_stat(const char *path, STRUCT_STAT * buffer, char *linkbuf)
+{
+#if SUPPORT_LINKS
+ if (copy_links) {
+ return do_stat(path, buffer);
+ }
+ if (do_lstat(path, buffer) == -1) {
+ return -1;
+ }
+ if (S_ISLNK(buffer->st_mode)) {
+ int l;
+ l = readlink((char *) path, linkbuf, MAXPATHLEN - 1);
+ if (l == -1)
+ return -1;
+ linkbuf[l] = 0;
+ if (copy_unsafe_links && (topsrcname[0] != '\0') &&
+ unsafe_symlink(linkbuf, topsrcname)) {
+ return do_stat(path, buffer);
+ }
+ }
+ return 0;
+#else
+ return do_stat(path, buffer);
+#endif
+}
+
+int link_stat(const char *path, STRUCT_STAT * buffer)