+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct patch *patch)
+{
+ const char *prefix = "";
+ char *name = patch->new_name;
+ int len, max, add, del, total;
+
+ if (!name)
+ name = patch->old_name;
+
+ /*
+ * "scale" the filename
+ */
+ len = strlen(name);
+ max = max_len;
+ if (max > 50)
+ max = 50;
+ if (len > max) {
+ char *slash;
+ prefix = "...";
+ max -= 3;
+ name += len - max;
+ slash = strchr(name, '/');
+ if (slash)
+ name = slash;
+ }
+ len = max;
+
+ /*
+ * scale the add/delete
+ */
+ max = max_change;
+ if (max + len > 70)
+ max = 70 - len;
+
+ add = patch->lines_added;
+ del = patch->lines_deleted;
+ total = add + del;
+
+ if (max_change > 0) {
+ total = (total * max + max_change / 2) / max_change;
+ add = (add * max + max_change / 2) / max_change;
+ del = total - add;
+ }
+ printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+ len, name, patch->lines_added + patch->lines_deleted,
+ add, pluses, del, minuses);
+}
+
+static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+{
+ int fd;
+ unsigned long got;
+
+ switch (st->st_mode & S_IFMT) {
+ case S_IFLNK:
+ return readlink(path, buf, size);
+ case S_IFREG:
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return error("unable to open %s", path);
+ got = 0;
+ for (;;) {
+ int ret = read(fd, buf + got, size - got);
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ continue;
+ break;
+ }
+ if (!ret)
+ break;
+ got += ret;
+ }
+ close(fd);
+ return got;
+
+ default:
+ return -1;
+ }
+}
+
+static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line)
+{
+ int i;
+ unsigned long start, backwards, forwards;
+
+ if (fragsize > size)
+ return -1;
+
+ start = 0;
+ if (line > 1) {
+ unsigned long offset = 0;
+ i = line-1;
+ while (offset + fragsize <= size) {
+ if (buf[offset++] == '\n') {
+ start = offset;
+ if (!--i)
+ break;
+ }
+ }
+ }
+
+ /* Exact line number? */
+ if (!memcmp(buf + start, fragment, fragsize))
+ return start;
+
+ /*
+ * There's probably some smart way to do this, but I'll leave
+ * that to the smart and beautiful people. I'm simple and stupid.
+ */
+ backwards = start;
+ forwards = start;
+ for (i = 0; ; i++) {
+ unsigned long try;
+ int n;
+
+ /* "backward" */
+ if (i & 1) {
+ if (!backwards) {
+ if (forwards + fragsize > size)
+ break;
+ continue;
+ }
+ do {
+ --backwards;
+ } while (backwards && buf[backwards-1] != '\n');
+ try = backwards;
+ } else {
+ while (forwards + fragsize <= size) {
+ if (buf[forwards++] == '\n')
+ break;
+ }
+ try = forwards;
+ }
+
+ if (try + fragsize > size)
+ continue;
+ if (memcmp(buf + try, fragment, fragsize))
+ continue;
+ n = (i >> 1)+1;
+ if (i & 1)
+ n = -n;
+ return try;
+ }
+
+ /*
+ * We should start searching forward and backward.
+ */
+ return -1;
+}
+
+struct buffer_desc {
+ char *buffer;
+ unsigned long size;
+ unsigned long alloc;
+};
+
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
+{
+ char *buf = desc->buffer;
+ const char *patch = frag->patch;
+ int offset, size = frag->size;
+ char *old = xmalloc(size);
+ char *new = xmalloc(size);
+ int oldsize = 0, newsize = 0;
+
+ while (size > 0) {
+ int len = linelen(patch, size);
+ int plen;
+
+ if (!len)
+ break;
+
+ /*
+ * "plen" is how much of the line we should use for
+ * the actual patch data. Normally we just remove the
+ * first character on the line, but if the line is
+ * followed by "\ No newline", then we also remove the
+ * last one (which is the newline, of course).
+ */
+ plen = len-1;
+ if (len < size && patch[len] == '\\')
+ plen--;
+ switch (*patch) {
+ case ' ':
+ case '-':
+ memcpy(old + oldsize, patch + 1, plen);
+ oldsize += plen;
+ if (*patch == '-')
+ break;
+ /* Fall-through for ' ' */
+ case '+':
+ memcpy(new + newsize, patch + 1, plen);
+ newsize += plen;
+ break;
+ case '@': case '\\':
+ /* Ignore it, we already handled it */
+ break;
+ default:
+ return -1;
+ }
+ patch += len;
+ size -= len;
+ }
+
+ offset = find_offset(buf, desc->size, old, oldsize, frag->newpos);
+ if (offset >= 0) {
+ int diff = newsize - oldsize;
+ unsigned long size = desc->size + diff;
+ unsigned long alloc = desc->alloc;
+
+ if (size > alloc) {
+ alloc = size + 8192;
+ desc->alloc = alloc;
+ buf = xrealloc(buf, alloc);
+ desc->buffer = buf;
+ }
+ desc->size = size;
+ memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+ memcpy(buf + offset, new, newsize);
+ offset = 0;
+ }
+
+ free(old);
+ free(new);
+ return offset;
+}
+
+static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+{
+ struct fragment *frag = patch->fragments;
+
+ while (frag) {
+ if (apply_one_fragment(desc, frag) < 0)
+ return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
+ frag = frag->next;
+ }
+ return 0;
+}
+
+static int apply_data(struct patch *patch, struct stat *st)
+{
+ char *buf;
+ unsigned long size, alloc;
+ struct buffer_desc desc;
+
+ size = 0;
+ alloc = 0;
+ buf = NULL;
+ if (patch->old_name) {
+ size = st->st_size;
+ alloc = size + 8192;
+ buf = xmalloc(alloc);
+ if (read_old_data(st, patch->old_name, buf, alloc) != size)
+ return error("read of %s failed", patch->old_name);
+ }
+
+ desc.size = size;
+ desc.alloc = alloc;
+ desc.buffer = buf;
+ if (apply_fragments(&desc, patch) < 0)
+ return -1;
+ patch->result = desc.buffer;
+ patch->resultsize = desc.size;
+
+ if (patch->is_delete && patch->resultsize)
+ return error("removal patch leaves file contents");
+
+ return 0;
+}
+
+static int check_patch(struct patch *patch)
+{
+ struct stat st;
+ const char *old_name = patch->old_name;
+ const char *new_name = patch->new_name;
+
+ if (old_name) {
+ int changed;
+
+ if (lstat(old_name, &st) < 0)
+ return error("%s: %s", old_name, strerror(errno));
+ if (check_index) {
+ int pos = cache_name_pos(old_name, strlen(old_name));
+ if (pos < 0)
+ return error("%s: does not exist in index", old_name);
+ changed = ce_match_stat(active_cache[pos], &st);
+ if (changed)
+ return error("%s: does not match index", old_name);
+ }
+ if (patch->is_new < 0)
+ patch->is_new = 0;
+ st.st_mode = ntohl(create_ce_mode(st.st_mode));
+ if (!patch->old_mode)
+ patch->old_mode = st.st_mode;
+ if ((st.st_mode ^ patch->old_mode) & S_IFMT)
+ return error("%s: wrong type", old_name);
+ if (st.st_mode != patch->old_mode)
+ fprintf(stderr, "warning: %s has type %o, expected %o\n",
+ old_name, st.st_mode, patch->old_mode);
+ }
+
+ if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+ if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+ return error("%s: already exists in index", new_name);
+ if (!lstat(new_name, &st))
+ return error("%s: already exists in working directory", new_name);
+ if (errno != ENOENT)
+ return error("%s: %s", new_name, strerror(errno));
+ if (!patch->new_mode)
+ patch->new_mode = S_IFREG | 0644;
+ }
+
+ if (new_name && old_name) {
+ int same = !strcmp(old_name, new_name);
+ if (!patch->new_mode)
+ patch->new_mode = patch->old_mode;
+ if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
+ return error("new mode (%o) of %s does not match old mode (%o)%s%s",
+ patch->new_mode, new_name, patch->old_mode,
+ same ? "" : " of ", same ? "" : old_name);
+ }
+
+ if (apply_data(patch, &st) < 0)
+ return error("%s: patch does not apply", old_name);
+ return 0;
+}
+
+static int check_patch_list(struct patch *patch)