popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
--- old/acls.c
+++ new/acls.c
-@@ -0,0 +1,1120 @@
+@@ -0,0 +1,1079 @@
+/*
+ * Handle passing Access Control Lists between systems.
+ *
+ {NULL, 0}, {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY
+};
+
-+/* === List functions === */
-+
-+#define EMPTY_LIST {NULL, 0, 0}
-+
-+typedef struct {
-+ void *items;
-+ size_t count;
-+ size_t malloced;
-+} item_list;
-+
-+static item_list access_acl_list = EMPTY_LIST;
-+static item_list default_acl_list = EMPTY_LIST;
-+
-+#define EXPAND_ITEM_LIST(lp, type, fast) (type*)expand_item_list(lp, sizeof (type), #type, fast)
-+
-+static void *expand_item_list(item_list *lp, size_t item_size, const char *desc, int fast)
-+{
-+ /* First time through, 0 <= 0, so list is expanded. */
-+ if (lp->malloced <= lp->count) {
-+ void *new_ptr;
-+ size_t new_size = lp->malloced;
-+ if (fast) {
-+ if (new_size < 1000)
-+ new_size += 1000;
-+ else
-+ new_size *= 2;
-+ } else
-+ new_size += 10;
-+ new_ptr = realloc_array(lp->items, char, new_size * item_size);
-+ if (verbose >= 4) {
-+ rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n",
-+ who_am_i(), desc, (double)new_size * item_size,
-+ new_ptr == lp->items ? " not" : "");
-+ }
-+ if (!new_ptr)
-+ out_of_memory("expand_item_list");
-+
-+ lp->items = new_ptr;
-+ lp->malloced = new_size;
-+ }
-+ return (char*)lp->items + (lp->count++ * item_size);
-+}
++static item_list access_acl_list = EMPTY_ITEM_LIST;
++static item_list default_acl_list = EMPTY_ITEM_LIST;
+
+/* === Calculations on ACL types === */
+
+ return True;
+}
+
-+static BOOL rsync_acls_equal(const rsync_acl *racl1, const rsync_acl *racl2)
++static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2)
+{
+ return (racl1->user_obj == racl2->user_obj
+ && racl1->group_obj == racl2->group_obj
+ * only meaningful for access ACLs! Note: the 1st arg is a fully-populated
+ * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means
+ * that it might have several of its perm objects set to NO_ENTRY. */
-+static BOOL rsync_acls_equal_enough(const rsync_acl *racl1,
-+ const rsync_acl *racl2, mode_t m)
++static BOOL rsync_acl_equal_enough(const rsync_acl *racl1,
++ const rsync_acl *racl2, mode_t m)
+{
+ if ((racl1->mask ^ racl2->mask) & NO_ENTRY)
+ return False; /* One has a mask and the other doesn't */
+ *racl = empty_rsync_acl;
+}
+
-+void free_acls(statx *sxp)
++void free_acl(statx *sxp)
+{
+ if (sxp->acc_acl) {
+ rsync_acl_free(sxp->acc_acl);
+/* Unpack system acl -> rsync acl verbatim. Return whether we succeeded. */
+static BOOL unpack_smb_acl(rsync_acl *racl, SMB_ACL_T sacl)
+{
-+ static item_list temp_ida_list = EMPTY_LIST;
++ static item_list temp_ida_list = EMPTY_ITEM_LIST;
+ SMB_ACL_TAG_T prior_list_type = 0;
+ SMB_ACL_ENTRY_T entry;
+ const char *errfun;
+ save_idas(&temp_ida_list, racl, prior_list_type);
+ prior_list_type = tag_type;
+ }
-+ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, 0);
++ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);
+ ida->id = *((id_t *)qualifier);
+ ida->access = access;
+ sys_acl_free_qualifier(qualifier, tag_type);
+ *match = racl_list->count - 1;
+ while (count--) {
+ rsync_acl *base = racl_list->items;
-+ if (rsync_acls_equal(base + *match, racl))
++ if (rsync_acl_equal(base + *match, racl))
+ return *match;
+ if (!(*match)--)
+ *match = racl_list->count - 1;
+
+/* Turn the ACL data in statx into cached ACL data, setting the index
+ * values in the file struct. */
-+void cache_acls(struct file_struct *file, statx *sxp)
++void cache_acl(struct file_struct *file, statx *sxp)
+{
+ SMB_ACL_TYPE_T type;
+ rsync_acl *racl;
+ if (ndx == -1) {
+ acl_duo *new_duo;
+ ndx = racl_list->count;
-+ new_duo = EXPAND_ITEM_LIST(racl_list, acl_duo, 1);
++ new_duo = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ new_duo->racl = *racl;
+ new_duo->sacl = NULL;
+ *racl = empty_rsync_acl;
+}
+
+/* Return the ACL(s) for the given filename. */
-+int get_acls(const char *fname, statx *sxp)
++int get_acl(const char *fname, statx *sxp)
+{
+ SMB_ACL_TYPE_T type;
+
+ rsync_acl *racl = new(rsync_acl);
+
+ if (!racl)
-+ out_of_memory("get_acls");
++ out_of_memory("get_acl");
+ if (type == SMB_ACL_TYPE_ACCESS)
+ sxp->acc_acl = racl;
+ else
+
+ sys_acl_free_acl(sacl);
+ if (!ok) {
-+ free_acls(sxp);
++ free_acl(sxp);
+ return -1;
+ }
+ } else if (errno == ENOTSUP) {
+ if (type == SMB_ACL_TYPE_ACCESS)
+ rsync_acl_fake_perms(racl, sxp->st.st_mode);
+ } else {
-+ rsyserr(FERROR, errno, "get_acls: sys_acl_get_file(%s, %s)",
++ rsyserr(FERROR, errno, "get_acl: sys_acl_get_file(%s, %s)",
+ fname, str_acl_type(type));
-+ free_acls(sxp);
++ free_acl(sxp);
+ return -1;
+ }
+ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode));
+ }
+}
+
-+/* Send the ACLs from the statx structure down the indicated file descriptor.
++/* Send the ACL from the statx structure down the indicated file descriptor.
+ * This also frees the ACL data. */
-+void send_acls(statx *sxp, int f)
++void send_acl(statx *sxp, int f)
+{
+ SMB_ACL_TYPE_T type;
+ rsync_acl *racl, *new_racl;
+ if ((ndx = find_matching_rsync_acl(type, racl_list, racl)) != -1) {
+ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd');
+ write_int(f, ndx);
-+ rsync_acl_free(racl);
+ } else {
-+ new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1);
++ new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000);
+ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D');
+ send_rsync_acl(f, racl);
+ *new_racl = *racl;
+ racl_list = &default_acl_list;
+ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode));
+
-+ free_acls(sxp);
++ free_acl(sxp);
+}
+
+/* === Receive functions === */
+
+static void receive_rsync_acl(rsync_acl *racl, int f, SMB_ACL_TYPE_T type)
+{
-+ static item_list temp_ida_list = EMPTY_LIST;
++ static item_list temp_ida_list = EMPTY_ITEM_LIST;
+ SMB_ACL_TAG_T tag_type = 0, prior_list_type = 0;
+ uchar computed_mask_bits = 0;
+ id_access *ida;
+ save_idas(&temp_ida_list, racl, prior_list_type);
+ prior_list_type = tag_type;
+ }
-+ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, 0);
++ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);
+ ida->access = access;
+ ida->id = read_int(f);
+ computed_mask_bits |= access;
+}
+
+/* Receive the ACL info the sender has included for this file-list entry. */
-+void receive_acls(struct file_struct *file, int f)
++void receive_acl(struct file_struct *file, int f)
+{
+ SMB_ACL_TYPE_T type;
+ item_list *racl_list;
+ if (tag == 'A' || tag == 'D') {
+ acl_duo *duo_item;
+ ndx = racl_list->count;
-+ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1);
++ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+ receive_rsync_acl(&duo_item->racl, f, type);
+ duo_item->sacl = NULL;
+ } else {
+ ndx = read_int(f);
-+ if ((size_t)ndx >= racl_list->count) {
++ if (ndx < 0 || (size_t)ndx >= racl_list->count) {
+ rprintf(FERROR, "receive_acl %s: %s ACL index %d out of range\n",
+ f_name(file, NULL), str_acl_type(type), ndx);
+ exit_cleanup(RERR_STREAMIO);
+ *
+ * Returns 1 for unchanged, 0 for changed, -1 for failed. Call this
+ * with fname set to NULL to just check if the ACLs are unchanged. */
-+int set_acls(const char *fname, const struct file_struct *file, statx *sxp)
++int set_acl(const char *fname, const struct file_struct *file, statx *sxp)
+{
+ int unchanged = 1;
+ SMB_ACL_TYPE_T type;
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ duo_item = access_acl_list.items;
+ duo_item += ndx;
-+ eq = rsync_acls_equal_enough(sxp->acc_acl, &duo_item->racl, file->mode);
++ eq = rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, file->mode);
+ } else {
+ duo_item = default_acl_list.items;
+ duo_item += ndx;
-+ eq = rsync_acls_equal(sxp->def_acl, &duo_item->racl);
++ eq = rsync_acl_equal(sxp->def_acl, &duo_item->racl);
+ }
+ if (eq)
+ continue;
+ continue;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls) {
-+ get_acls(rel, &sx);
-+ cache_acls(file, &sx);
++ get_acl(rel, &sx);
++ cache_acl(file, &sx);
+ }
+#endif
+ set_file_attrs(fullpath, file, NULL, 0);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls) {
-+ get_acls(fname, &sx);
-+ cache_acls(file, &sx);
++ get_acl(fname, &sx);
++ cache_acl(file, &sx);
+ }
+#endif
+
permstring(permbuf, f->mode);
-+ /* TODO: indicate '+' if the entry has ACLs. */
++ /* TODO: indicate '+' if the entry has an ACL. */
+
#ifdef SUPPORT_LINKS
if (preserve_links && S_ISLNK(f->mode)) {
+#ifdef SUPPORT_ACLS
+ /* We need one or two index int32s when we're preserving ACLs. */
+ if (preserve_acls)
-+ xtra_len = (S_ISDIR(mode) ? 2 : 1) * sizeof (int32);
++ xtra_len = (S_ISDIR(mode) ? 2 : 1) * 4;
+ else
+ xtra_len = 0;
+#endif
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ receive_acls(file, f);
++ receive_acl(file, f);
+#endif
+
return file;
}
-@@ -733,6 +758,9 @@ struct file_struct *make_file(char *fnam
- char thisname[MAXPATHLEN];
- char linkname[MAXPATHLEN];
- int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
-+#ifdef SUPPORT_ACLS
-+ int xtra_len;
-+#endif
- char *basename, *dirname, *bp;
-
- if (!flist || !flist->count) /* Ignore lastdir when invalid. */
-@@ -848,8 +876,19 @@ struct file_struct *make_file(char *fnam
- sum_len = always_checksum && am_sender && S_ISREG(st.st_mode)
- ? MD4_SUM_LENGTH : 0;
-
-+#ifdef SUPPORT_ACLS
-+ /* We need one or two index int32s when we're preserving ACLs. */
-+ if (preserve_acls)
-+ xtra_len = (S_ISDIR(st.st_mode) ? 2 : 1) * sizeof (int32);
-+ else
-+ xtra_len = 0;
-+#endif
-+
- alloc_len = file_struct_len + dirname_len + basename_len
-- + linkname_len + sum_len;
-+#ifdef SUPPORT_ACLS
-+ + xtra_len
-+#endif
-+ + linkname_len + sum_len;
- if (flist)
- bp = pool_alloc(flist->file_pool, alloc_len, "make_file");
- else {
-@@ -860,6 +899,9 @@ struct file_struct *make_file(char *fnam
- file = (struct file_struct *)bp;
- memset(bp, 0, file_struct_len);
- bp += file_struct_len;
-+#ifdef SUPPORT_ACLS
-+ bp += xtra_len;
-+#endif
-
- file->flags = flags;
- file->modtime = st.st_mtime;
-@@ -952,6 +994,9 @@ static struct file_struct *send_file_nam
+@@ -952,6 +977,9 @@ static struct file_struct *send_file_nam
unsigned short flags)
{
struct file_struct *file;
file = make_file(fname, flist, stp, flags,
f == -2 ? SERVER_FILTERS : ALL_FILTERS);
-@@ -961,6 +1006,15 @@ static struct file_struct *send_file_nam
+@@ -961,6 +989,15 @@ static struct file_struct *send_file_nam
if (chmod_modes && !S_ISLNK(file->mode))
file->mode = tweak_mode(file->mode, chmod_modes);
+ if (preserve_acls) {
+ sx.st.st_mode = file->mode;
+ sx.acc_acl = sx.def_acl = NULL;
-+ if (get_acls(fname, &sx) < 0)
++ if (get_acl(fname, &sx) < 0)
+ return NULL;
+ }
+#endif
maybe_emit_filelist_progress(flist->count + flist_count_offset);
flist_expand(flist);
-@@ -968,6 +1022,15 @@ static struct file_struct *send_file_nam
+@@ -968,6 +1005,15 @@ static struct file_struct *send_file_nam
if (file->basename[0]) {
flist->files[flist->count++] = file;
send_file_entry(file, f);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ send_acls(&sx, f);
++ send_acl(&sx, f);
+#endif
+ } else {
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ free_acls(&sx);
++ free_acl(&sx);
+#endif
}
return file;
return 0;
+#ifdef SUPPORT_ACLS
-+ if (preserve_acls && set_acls(NULL, file, sxp) == 0)
++ if (preserve_acls && set_acl(NULL, file, sxp) == 0)
+ return 0;
+#endif
+
+ && sxp->st.st_gid != file->gid)
iflags |= ITEM_REPORT_GROUP;
+#ifdef SUPPORT_ACLS
-+ if (preserve_acls && set_acls(NULL, file, sxp) == 0)
++ if (preserve_acls && set_acl(NULL, file, sxp) == 0)
+ iflags |= ITEM_REPORT_ACL;
+#endif
} else
- if (!unchanged_attrs(file, stp))
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ get_acls(cmpbuf, sxp);
++ get_acl(cmpbuf, sxp);
+#endif
+ if (!unchanged_attrs(file, sxp))
continue;
+ } else if (itemizing) {
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !ACL_READY(*sxp))
-+ get_acls(fname, sxp);
++ get_acl(fname, sxp);
+#endif
+ itemize(file, ndx, 0, sxp, 0, 0, NULL);
+ }
+ if (itemizing) {
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !ACL_READY(*sxp))
-+ get_acls(fname, sxp);
++ get_acl(fname, sxp);
+#endif
+ itemize(file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
+ }
+ continue;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ get_acls(fnamebuf, &sx);
++ get_acl(fnamebuf, &sx);
+#endif
+ if (!unchanged_attrs(file, &sx))
continue;
- itemize(file, ndx, 0, &st, changes, 0, lp);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ get_acls(fname, &sx);
++ get_acl(fname, &sx);
+#endif
+ itemize(file, ndx, 0, &sx, changes, 0, lp);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ free_acls(&sx);
++ free_acl(&sx);
+#endif
}
if (verbose > 1 && maybe_ATTRS_REPORT) {
- itemize(file, ndx, statret, &st,
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && statret == 0)
-+ get_acls(fname, &sx);
++ get_acl(fname, &sx);
+#endif
+ itemize(file, ndx, statret, &sx,
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
}
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && statret == 0)
-+ get_acls(fname, &sx);
++ get_acl(fname, &sx);
+#endif
if (statret != 0
- || (st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS)
- itemize(file, ndx, real_ret, &real_st,
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && real_ret == 0)
-+ get_acls(fname, &real_sx);
++ get_acl(fname, &real_sx);
+#endif
+ itemize(file, ndx, real_ret, &real_sx,
0, 0, NULL);
+ sx.acc_acl = real_sx.acc_acl;
+ sx.def_acl = real_sx.def_acl;
+ } else
-+ free_acls(&real_sx);
++ free_acl(&real_sx);
+ }
+#endif
}
statret = real_ret = -1;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && ACL_READY(sx))
-+ free_acls(&sx);
++ free_acl(&sx);
+#endif
goto notify_others;
}
- itemize(file, -1, real_ret, &real_st, iflags, fnamecmp_type,
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && real_ret == 0)
-+ get_acls(fname, &real_sx);
++ get_acl(fname, &real_sx);
+#endif
+ itemize(file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
fuzzy_file ? fuzzy_file->basename : NULL);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ free_acls(&real_sx);
++ free_acl(&real_sx);
+#endif
}
+ cleanup:
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ free_acls(&sx);
++ free_acl(&sx);
+#endif
+ return;
}
- itemize(file, ndx, statret, st,
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !ACL_READY(*sxp))
-+ get_acls(fname, sxp);
++ get_acl(fname, sxp);
+#endif
+ itemize(file, ndx, statret, sxp,
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
+ sxp->st = st3;
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ get_acls(cmpbuf, sxp);
++ get_acl(cmpbuf, sxp);
+#endif
+ if (unchanged_attrs(file, sxp))
break;
- itemize(file, ndx, statret, st,
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && statret == 0 && !ACL_READY(*sxp))
-+ get_acls(fname, sxp);
++ get_acl(fname, sxp);
+#endif
+ itemize(file, ndx, statret, sxp,
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
+ hlink1, &st, itemizing, code);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls)
-+ free_acls(&sx);
++ free_acl(&sx);
+#endif
file->F_HLINDEX = FINISHED_LINK;
} while (!(file->flags & FLAG_HLINK_EOL));
+}
--- old/lib/sysacls.h
+++ new/lib/sysacls.h
-@@ -0,0 +1,28 @@
+@@ -0,0 +1,33 @@
++#if defined SUPPORT_ACLS && defined HAVE_SYS_ACL_H
++#include <sys/acl.h>
++#endif
++#include "smb_acls.h"
++
+#define SMB_MALLOC(cnt) new_array(char, cnt)
+#define SMB_MALLOC_P(obj) new_array(obj, 1)
+#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt)
- if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times))
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !ACL_READY(*sxp))
-+ get_acls(fname, sxp);
++ get_acl(fname, sxp);
+#endif
+
+ if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
}
+#ifdef SUPPORT_ACLS
-+ /* It's OK to call set_acls() now, even for a dir, as the generator
++ /* It's OK to call set_acl() now, even for a dir, as the generator
+ * will enable owner-writability using chmod, if necessary.
+ *
-+ * If set_acl changes permission bits in the process of setting
++ * If set_acl() changes permission bits in the process of setting
+ * an access ACL, it changes sxp->st.st_mode so we know whether we
-+ * need to chmod. */
-+ if (preserve_acls && set_acls(fname, file, sxp) == 0)
++ * need to chmod(). */
++ if (preserve_acls && set_acl(fname, file, sxp) == 0)
+ updated = 1;
+#endif
+
+ cleanup:
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && sxp == &sx2)
-+ free_acls(&sx2);
++ free_acl(&sx2);
+#endif
return updated;
}
#define GID_NONE ((gid_t)-1)
#define HL_CHECK_MASTER 0
-@@ -660,6 +669,21 @@ struct chmod_mode_struct;
+@@ -645,6 +654,17 @@ struct stats {
- #define UNUSED(x) x __attribute__((__unused__))
+ struct chmod_mode_struct;
-+#if defined SUPPORT_ACLS && defined HAVE_SYS_ACL_H
-+#include <sys/acl.h>
-+#endif
-+#include "smb_acls.h"
++#define EMPTY_ITEM_LIST {NULL, 0, 0}
+
++typedef struct {
++ void *items;
++ size_t count;
++ size_t malloced;
++} item_list;
++
++#define EXPAND_ITEM_LIST(lp, type, incr) \
++ (type*)expand_item_list(lp, sizeof (type), #type, incr)
++
+ #include "byteorder.h"
+ #include "lib/mdfour.h"
+ #include "lib/wildmatch.h"
+@@ -660,6 +680,16 @@ struct chmod_mode_struct;
+
+ #define UNUSED(x) x __attribute__((__unused__))
+
+typedef struct {
+ STRUCT_STAT st;
+#ifdef SUPPORT_ACLS
+
+#endif /* No ACLs. */
+#endif /* _SMB_ACLS_H */
+--- old/t_stub.c
++++ new/t_stub.c
+@@ -78,3 +78,7 @@ struct filter_list_struct server_filter_
+ return NULL;
+ }
+
++ const char *who_am_i(void)
++{
++ return "test";
++}
--- old/testsuite/acls.test
+++ new/testsuite/acls.test
@@ -0,0 +1,34 @@
/* Now convert all the uids/gids from sender values to our values. */
if (am_root && preserve_uid && !numeric_ids) {
for (i = 0; i < flist->count; i++)
+--- old/util.c
++++ new/util.c
+@@ -1446,3 +1446,31 @@ int bitbag_next_bit(struct bitbag *bb, i
+
+ return -1;
+ }
++
++void *expand_item_list(item_list *lp, size_t item_size,
++ const char *desc, int incr)
++{
++ /* First time through, 0 <= 0, so list is expanded. */
++ if (lp->malloced <= lp->count) {
++ void *new_ptr;
++ size_t new_size = lp->malloced;
++ if (incr < 0)
++ new_size -= incr; /* increase slowly */
++ else if (new_size < (size_t)incr)
++ new_size += incr;
++ else
++ new_size *= 2;
++ new_ptr = realloc_array(lp->items, char, new_size * item_size);
++ if (verbose >= 4) {
++ rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n",
++ who_am_i(), desc, (double)new_size * item_size,
++ new_ptr == lp->items ? " not" : "");
++ }
++ if (!new_ptr)
++ out_of_memory("expand_item_list");
++
++ lp->items = new_ptr;
++ lp->malloced = new_size;
++ }
++ return (char*)lp->items + (lp->count++ * item_size);
++}