commit fd3a93f
xplshn
·
2026-06-30 19:56:41 +0000 UTC
parent b2a077b
portable diskutils: blkid, lsblk, fdisk | general cleanup
M
Makefile
+12,
-37
1@@ -123,8 +123,9 @@ POSIX_BIN_ALL =\
2 cmd/posix/date\
3 cmd/posix/dd\
4 cmd/posix/df\
5- cmd/posix/diff/diff\
6- cmd/posix/patch/patch\
7+ cmd/posix/diff\
8+ cmd/posix/patch\
9+ cmd/posix/diff3\
10 cmd/posix/dirname\
11 cmd/posix/du\
12 cmd/posix/echo\
13@@ -277,7 +278,6 @@ PSEUDO_BIN_ALL =\
14 cmd/extra/lsblk\
15 cmd/extra/fdisk\
16 cmd/extra/sync\
17- cmd/extra/diff3/diff3\
18 cmd/dev/ar/ar\
19 cmd/dev/as/as\
20 cmd/dev/ld/ld\
21@@ -296,20 +296,7 @@ MAKEOBJ =\
22 cmd/posix/make/posix.o\
23 cmd/posix/make/rules.o
24
25-DIFFOBJ =\
26- cmd/posix/diff/diff.o\
27- cmd/posix/diff/diffdir.o\
28- cmd/posix/diff/diffreg.o\
29- cmd/posix/diff/pr.o\
30- cmd/posix/diff/xmalloc.o
31-
32-PATCHOBJ =\
33- cmd/posix/patch/patch.o\
34- cmd/posix/patch/pch.o\
35- cmd/posix/patch/inp.o\
36- cmd/posix/patch/util.o\
37- cmd/posix/patch/backupfile.o\
38- cmd/posix/patch/mkpath.o
39+
40
41 BIN_basename_1 = cmd/posix/basename
42 BIN_cal_1 = cmd/posix/cal
43@@ -325,8 +312,8 @@ BIN_cut_1 = cmd/posix/cut
44 BIN_date_1 = cmd/posix/date
45 BIN_dd_1 = cmd/posix/dd
46 BIN_df_1 = cmd/posix/df
47-BIN_diff_1 = cmd/posix/diff/diff
48-BIN_patch_1 = cmd/posix/patch/patch
49+BIN_diff_1 = cmd/posix/diff
50+BIN_patch_1 = cmd/posix/patch
51 BIN_dirname_1 = cmd/posix/dirname
52 BIN_du_1 = cmd/posix/du
53 BIN_echo_1 = cmd/posix/echo
54@@ -475,7 +462,7 @@ BIN_blkid_1 = cmd/extra/blkid
55 BIN_lsblk_1 = cmd/extra/lsblk
56 BIN_fdisk_1 = cmd/extra/fdisk
57 BIN_sync_1 = cmd/extra/sync
58-BIN_diff3_1 = cmd/extra/diff3/diff3
59+BIN_diff3_1 = cmd/posix/diff3
60 BIN_ar_1 = cmd/dev/ar/ar
61 BIN_as_1 = cmd/dev/as/as
62 BIN_ld_1 = cmd/dev/ld/ld
63@@ -698,20 +685,8 @@ $(MAKEOBJ): cmd/posix/make/make.h
64 cmd/posix/make/make: $(MAKEOBJ) $(LIB)
65 $(CC) $(LDFLAGS) -o $@ $(MAKEOBJ) $(LIB) $(LDLIBS)
66
67-$(DIFFOBJ): cmd/posix/diff/diff.h cmd/posix/diff/pr.h cmd/posix/diff/xmalloc.h
68-
69-cmd/posix/diff/diff: $(DIFFOBJ) $(LIB)
70- $(CC) $(LDFLAGS) -o $@ $(DIFFOBJ) $(LIB) $(LDLIBS) -lm
71-
72-$(PATCHOBJ): cmd/posix/patch/common.h cmd/posix/patch/util.h \
73- cmd/posix/patch/pch.h cmd/posix/patch/inp.h \
74- cmd/posix/patch/backupfile.h cmd/posix/patch/pathnames.h
75-
76-cmd/posix/patch/%.o: cmd/posix/patch/%.c
77- $(CC) $(CPPFLAGS) -Icmd/posix/patch $(CFLAGS) -o $@ -c $<
78-
79-cmd/posix/patch/patch: $(PATCHOBJ) $(LIB)
80- $(CC) $(LDFLAGS) -o $@ $(PATCHOBJ) $(LIB) $(LDLIBS)
81+cmd/posix/diff: cmd/posix/diff.c $(LIB)
82+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ cmd/posix/diff.c $(LIB) $(LDLIBS) -lm
83
84 shared/libutf/libutf.a: $(LIBUTFOBJ)
85 $(AR) $(ARFLAGS) $@ $?
86@@ -760,12 +735,12 @@ man: scripts/mkman/mkman
87
88 clean:
89 rm -f shared/libutf/*.o shared/libutil/*.o shared/libredline/*.o
90- rm -f cmd/posix/*.o cmd/posix/diff/*.o cmd/posix/patch/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
91+ rm -f cmd/posix/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
92 rm -f cmd/linux/*.o cmd/net/*.o cmd/xsi/*.o cmd/pseudo/*.o
93- rm -f cmd/extra/*.o cmd/extra/diff3/*.o cmd/dev/ar/*.o cmd/dev/ld/*.o cmd/dev/cc/*.o cmd/dev/as/*.o cmd/dev/xcutil/*.o
94+ rm -f cmd/extra/*.o cmd/dev/ar/*.o cmd/dev/ld/*.o cmd/dev/cc/*.o cmd/dev/as/*.o cmd/dev/xcutil/*.o
95 rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL) $(LIB)
96 rm -f cmd/posix/make/make cmd/posix/getconf.h cmd/posix/bc.c
97- rm -f cmd/posix/patch/patch
98+ rm -f
99 rm -f cmd/posix/awk/awk cmd/posix/awk/maketab cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c
100 rm -f cmd/posix/sh/sh cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
101 rm -f cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h cmd/posix/sh/token.h
+6,
-47
1@@ -271,9 +271,6 @@ build_simple_tools() {
2 for src in cmd/"$cat"/*.c; do
3 [ -f "$src" ] || continue
4 base=${src##*/}; base=${base%.c}
5- if [ "$cat" = posix ] && [ "$base" = diff ]; then
6- continue
7- fi
8 var=$(cfgvar "$cat" "$base")
9 cfg_enabled "$var" || continue
10 compile_c "$src" "${src%.c}.o"
11@@ -282,45 +279,14 @@ build_simple_tools() {
12 drain
13 local bin
14 for bin in $staged; do
15- link_bin "$bin" "${bin}.o" -- $LIB
16+ if [ "${bin##*/}" = "diff" ]; then
17+ link_bin "$bin" "${bin}.o" -- $LIB -lm
18+ else
19+ link_bin "$bin" "${bin}.o" -- $LIB
20+ fi
21 done
22 }
23
24-# generated multi-file tools under cmd/posix
25-#
26-build_diff() {
27- cfg_enabled BUILD_POSIX_DIFF || return 0
28- local dir=cmd/posix/diff
29- local objs=""
30- EXTRA_HDR="$dir/diff.h $dir/pr.h $dir/xmalloc.h"
31- compile_c "$dir/diff.c" "$dir/diff.o"
32- compile_c "$dir/diffdir.c" "$dir/diffdir.o"
33- compile_c "$dir/diffreg.c" "$dir/diffreg.o"
34- compile_c "$dir/pr.c" "$dir/pr.o"
35- compile_c "$dir/xmalloc.c" "$dir/xmalloc.o"
36- EXTRA_HDR=
37- objs="$dir/diff.o $dir/diffdir.o $dir/diffreg.o $dir/pr.o $dir/xmalloc.o"
38- link_bin "$dir/diff" $objs -- $LIB -lm
39-}
40-
41-build_patch() {
42- cfg_enabled BUILD_POSIX_PATCH || return 0
43- local dir=cmd/posix/patch
44- EXTRA_HDR="$dir/common.h $dir/util.h $dir/pch.h $dir/inp.h $dir/backupfile.h $dir/pathnames.h"
45- objs_for "$dir" "" "-I$dir"
46- EXTRA_HDR=
47- link_bin "$dir/patch" $_objs -- $LIB
48-}
49-
50-build_diff3() {
51- cfg_enabled BUILD_PSEUDO_DIFF3 || return 0
52- local dir=cmd/extra/diff3
53- EXTRA_HDR="cmd/posix/diff/xmalloc.h"
54- compile_c "$dir/diff3.c" "$dir/diff3.o"
55- EXTRA_HDR=
56- link_bin "$dir/diff3" "$dir/diff3.o" cmd/posix/diff/xmalloc.o -- $LIB
57-}
58-
59 build_awk() {
60 cfg_enabled BUILD_POSIX_AWK || return 0
61 local dir=cmd/posix/awk
62@@ -415,8 +381,6 @@ build_posix() {
63 scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
64 }
65 build_simple_tools posix
66- build_diff
67- build_patch
68 build_awk
69 build_sh
70 build_make
71@@ -533,9 +497,6 @@ build_man() {
72 build_man_for "$(cfgvar "$cat" "$base")" "$src"
73 done
74 done
75- build_man_for BUILD_POSIX_DIFF cmd/posix/diff/diff.c
76- build_man_for BUILD_POSIX_PATCH cmd/posix/patch/patch.c
77- build_man_for BUILD_PSEUDO_DIFF3 cmd/extra/diff3/diff3.c
78 }
79
80 # install and clean
81@@ -560,7 +521,6 @@ do_clean() {
82 find cmd -type f ! -name '*.*' -perm -100 -exec rm -f {} +
83 rm -f shared/libaruuelf.so
84 rm -f cmd/posix/bc.c cmd/posix/getconf.h
85- rm -f cmd/posix/patch/patch
86 rm -f cmd/posix/awk/maketab cmd/posix/awk/proctab.c
87 rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h
88 rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
89@@ -582,14 +542,13 @@ case "$TARGET" in
90 build_simple_tools xsi
91 build_simple_tools pseudo
92 build_simple_tools extra
93- build_diff3
94 ;;
95 lib) build_lib ;;
96 posix) build_lib; build_posix ;;
97 dev) build_lib; build_dev ;;
98 make) build_lib; build_make ;;
99 linux|net|xsi|pseudo|extra)
100- build_lib; build_simple_tools "$TARGET"; [ "$TARGET" = extra ] && build_diff3 ;;
101+ build_lib; build_simple_tools "$TARGET" ;;
102 man) build_man ;;
103 install) do_install ;;
104 clean) do_clean ;;
+22,
-234
1@@ -8,43 +8,6 @@
2 #include <unistd.h>
3 #include <fcntl.h>
4
5-struct FsType {
6- const char *name;
7- size_t magic_offset;
8- size_t magic_len;
9- uint64_t magic1;
10- uint64_t magic2;
11- size_t uuid_offset;
12- size_t uuid_len;
13- size_t label_offset;
14- size_t label_len;
15-};
16-
17-static const struct FsType fstypes[] = {
18- /* ext2/3/4 */
19- { "ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16 },
20- /* vfat / fat32 */
21- { "vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11 },
22- /* vfat / fat12/16 */
23- { "vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11 },
24- /* ntfs */
25- { "ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0 },
26- /* xfs */
27- { "xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12 },
28- /* btrfs */
29- { "btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256 },
30- /* f2fs */
31- { "f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512 },
32- /* ufs1 */
33- { "ufs1", 9564, 4, 0x011954, 0x54190100, 0, 0, 0, 0 },
34- /* ufs2 */
35- { "ufs2", 66908, 4, 0x19540119, 0x19015419, 67056, 16, 67072, 32 },
36- /* fossil */
37- { "fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127 },
38- /* cwfs */
39- { "cwfs", 0, 4, 0xc1a551f5, 0xf551a5c1, 0, 0, 0, 0 },
40-};
41-
42 static char *oflag = "full";
43 static char *sflag = NULL;
44 static int Uflag = 0;
45@@ -56,35 +19,6 @@ usage(void)
46 eprintf("usage: %s [-o format] [-s tag] [-U] [-L] [device ...]\n", argv0);
47 }
48
49-static void
50-format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
51-{
52- size_t i;
53- char *p = out;
54-
55- if (strcmp(type, "vfat") == 0) {
56- snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
57- return;
58- }
59-
60- if (strcmp(type, "ntfs") == 0) {
61- for (i = 8; i > 0; i--) {
62- snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
63- p += 2;
64- }
65- return;
66- }
67-
68- for (i = 0; i < len; i++) {
69- if (i == 4 || i == 6 || i == 8 || i == 10) {
70- *p++ = '-';
71- }
72- snprintf(p, out_len - (p - out), "%02x", buf[i]);
73- p += 2;
74- }
75- *p = '\0';
76-}
77-
78 static void
79 print_tag(const char *devname, const char *tag, const char *value)
80 {
81@@ -100,192 +34,46 @@ print_tag(const char *devname, const char *tag, const char *value)
82 }
83 }
84
85-static int
86-detect_zfs(const unsigned char *buf, size_t len)
87-{
88- size_t offset;
89- uint64_t magic;
90-
91- for (offset = 131072; offset + 8 <= len; offset += 1024) {
92- magic = ((uint64_t)buf[offset + 7] << 56) |
93- ((uint64_t)buf[offset + 6] << 48) |
94- ((uint64_t)buf[offset + 5] << 40) |
95- ((uint64_t)buf[offset + 4] << 32) |
96- ((uint64_t)buf[offset + 3] << 24) |
97- ((uint64_t)buf[offset + 2] << 16) |
98- ((uint64_t)buf[offset + 1] << 8) |
99- (uint64_t)buf[offset];
100-
101- if (magic == 0x00bab10caULL || magic == 0x0cb1ba0000000000ULL ||
102- magic == 0x00bab10cULL || magic == 0x0cb1ba00ULL)
103- return 1;
104- }
105- return 0;
106-}
107-
108-static int
109-detect_plan9_other(const unsigned char *buf, size_t len, const char **name)
110-{
111- if (len >= 4) {
112- uint32_t magic = ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[1] << 8) | (uint32_t)buf[0];
113- if (magic == 0x686a6673 || magic == 0x73666a68) {
114- *name = "hjfs";
115- return 1;
116- }
117- if (magic == 0x67656673 || magic == 0x73666567) {
118- *name = "gefs";
119- return 1;
120- }
121- }
122- if (len >= 516) {
123- uint32_t magic = ((uint32_t)buf[515] << 24) | ((uint32_t)buf[514] << 16) | ((uint32_t)buf[513] << 8) | (uint32_t)buf[512];
124- if (magic == 0x686a6673 || magic == 0x73666a68) {
125- *name = "hjfs";
126- return 1;
127- }
128- if (magic == 0x67656673 || magic == 0x73666567) {
129- *name = "gefs";
130- return 1;
131- }
132- }
133- return 0;
134-}
135-
136 static int
137 do_blkid(const char *path)
138 {
139 struct BlockDev dev;
140- unsigned char *buf;
141- size_t read_size = 262144; /* 256kb */
142- size_t i, j;
143- int found = 0;
144+ char type[64] = {0};
145+ char label[256] = {0};
146+ char uuid[256] = {0};
147+ int res;
148
149 if (blockdev_open(&dev, path, 0) < 0)
150 return -1;
151
152- if (dev.size < read_size)
153- read_size = dev.size;
154-
155- buf = emalloc(read_size);
156- /* read sectors */
157- size_t sectors_to_read = (read_size + dev.sec_size - 1) / dev.sec_size;
158- if (blockdev_read(&dev, 0, buf, sectors_to_read) < 0) {
159- free(buf);
160- blockdev_close(&dev);
161- return -1;
162- }
163-
164- for (i = 0; i < LEN(fstypes); i++) {
165- const struct FsType *fs = &fstypes[i];
166- if (fs->magic_offset + fs->magic_len > read_size)
167- continue;
168-
169- uint64_t val = 0;
170- for (j = 0; j < fs->magic_len; j++) {
171- val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
172- }
173-
174- if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
175- const char *type_name = fs->name;
176- if (strcmp(fs->name, "ext") == 0) {
177- if (buf[1116] & 4)
178- type_name = "ext3";
179- else if (buf[1120] & 64)
180- type_name = "ext4";
181- else
182- type_name = "ext2";
183- }
184-
185- if (Uflag || Lflag) {
186- char label[256] = {0};
187- char uuid[256] = {0};
188- if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
189- snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
190- }
191- if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
192- format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
193- }
194- if (Uflag) {
195- printf("%s\n", uuid);
196- } else {
197- printf("%s\n", label);
198- }
199- found = 1;
200- break;
201+ res = blockdev_detect_fs(&dev, type, sizeof(type), label, sizeof(label), uuid, sizeof(uuid));
202+ if (res == 0) {
203+ if (Uflag || Lflag) {
204+ if (Uflag) {
205+ printf("%s\n", uuid);
206+ } else {
207+ printf("%s\n", label);
208 }
209-
210+ } else {
211 if (strcmp(oflag, "export") == 0) {
212 printf("DEVNAME=%s\n", path);
213 } else if (strcmp(oflag, "value") != 0) {
214 printf("%s:", path);
215 }
216
217- if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
218- char label[256];
219- snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
220- /* strip trailing spaces */
221- char *end = label + strlen(label) - 1;
222- while (end >= label && *end == ' ') {
223- *end = '\0';
224- end--;
225- }
226- if (label[0])
227- print_tag(path, "LABEL", label);
228- }
229-
230- if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
231- char uuid[256];
232- format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
233+ if (label[0])
234+ print_tag(path, "LABEL", label);
235+ if (uuid[0])
236 print_tag(path, "UUID", uuid);
237- }
238+ print_tag(path, "TYPE", type);
239
240- print_tag(path, "TYPE", type_name);
241 if (strcmp(oflag, "full") == 0)
242 printf("\n");
243-
244- found = 1;
245- break;
246 }
247 }
248
249- if (!found && detect_zfs(buf, read_size)) {
250- if (Uflag || Lflag) {
251- printf("\n");
252- } else {
253- if (strcmp(oflag, "export") == 0) {
254- printf("DEVNAME=%s\n", path);
255- } else if (strcmp(oflag, "value") != 0) {
256- printf("%s:", path);
257- }
258- print_tag(path, "TYPE", "zfs_member");
259- if (strcmp(oflag, "full") == 0)
260- printf("\n");
261- }
262- found = 1;
263- }
264-
265- if (!found) {
266- const char *p9_name = NULL;
267- if (detect_plan9_other(buf, read_size, &p9_name)) {
268- if (Uflag || Lflag) {
269- printf("\n");
270- } else {
271- if (strcmp(oflag, "export") == 0) {
272- printf("DEVNAME=%s\n", path);
273- } else if (strcmp(oflag, "value") != 0) {
274- printf("%s:", path);
275- }
276- print_tag(path, "TYPE", p9_name);
277- if (strcmp(oflag, "full") == 0)
278- printf("\n");
279- }
280- found = 1;
281- }
282- }
283-
284- free(buf);
285 blockdev_close(&dev);
286- return found ? 0 : -2;
287+ return res == 0 ? 0 : -2;
288 }
289
290 // ?man blkid: print block device attributes
291@@ -330,17 +118,17 @@ main(int argc, char *argv[])
292 return 1;
293 }
294 for (curr = list; curr; curr = curr->next) {
295- char devpath[128];
296- snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
297- do_blkid(devpath);
298+ do_blkid(curr->path);
299 struct BlockDevInfo *part;
300 for (part = curr->parts; part; part = part->next) {
301- snprintf(devpath, sizeof(devpath), "/dev/%s", part->name);
302- do_blkid(devpath);
303+ do_blkid(part->path);
304 }
305 }
306 blockdev_free_list(list);
307 }
308
309+ if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
310+ ret = 2;
311+
312 return ret;
313 }
+0,
-1250
1@@ -1,1250 +0,0 @@
2-/* $OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */
3-
4-/*
5- * Copyright (C) Caldera International Inc. 2001-2002.
6- * All rights reserved.
7- *
8- * Redistribution and use in source and binary forms, with or without
9- * modification, are permitted provided that the following conditions
10- * are met:
11- * 1. Redistributions of source code and documentation must retain the above
12- * copyright notice, this list of conditions and the following disclaimer.
13- * 2. Redistributions in binary form must reproduce the above copyright
14- * notice, this list of conditions and the following disclaimer in the
15- * documentation and/or other materials provided with the distribution.
16- * 3. All advertising materials mentioning features or use of this software
17- * must display the following acknowledgement:
18- * This product includes software developed or owned by Caldera
19- * International, Inc.
20- * 4. Neither the name of Caldera International, Inc. nor the names of other
21- * contributors may be used to endorse or promote products derived from
22- * this software without specific prior written permission.
23- *
24- * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
25- * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
26- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28- * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
29- * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
34- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35- * POSSIBILITY OF SUCH DAMAGE.
36- */
37-/*-
38- * Copyright (c) 1991, 1993
39- * The Regents of the University of California. All rights reserved.
40- *
41- * Redistribution and use in source and binary forms, with or without
42- * modification, are permitted provided that the following conditions
43- * are met:
44- * 1. Redistributions of source code must retain the above copyright
45- * notice, this list of conditions and the following disclaimer.
46- * 2. Redistributions in binary form must reproduce the above copyright
47- * notice, this list of conditions and the following disclaimer in the
48- * documentation and/or other materials provided with the distribution.
49- * 3. Neither the name of the University nor the names of its contributors
50- * may be used to endorse or promote products derived from this software
51- * without specific prior written permission.
52- *
53- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63- * SUCH DAMAGE.
64- */
65-
66-#include <sys/cdefs.h>
67-#include <sys/types.h>
68-#include <sys/wait.h>
69-
70-#include <ctype.h>
71-#include <err.h>
72-#include <fcntl.h>
73-#include <getopt.h>
74-#include <stdbool.h>
75-#include <stdio.h>
76-#include <stdlib.h>
77-#include <limits.h>
78-#include <inttypes.h>
79-#include <string.h>
80-#include <unistd.h>
81-#include <errno.h>
82-#include <poll.h>
83-
84-#include "util.h"
85-#include "../../posix/diff/xmalloc.h"
86-
87-// ?man diff3: 3-way differential file comparison
88-// ?man arguments: file1 file2 file3
89-// ?man synopsis: [-3AaEeimTXx] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
90-// ?man synopsis: [--help]
91-// ?man synopsis: [--version]
92-// ?man ## OPTIONS
93-// ?man ### -3, --easy-only
94-// ?man Produces an output script suitable for ed(1) with changes specific only to file3.
95-// ?man ### -A, --show-all
96-// ?man Output all changes, bracketing conflicts.
97-// ?man ### -a, --text
98-// ?man Treat all files as ASCII.
99-// ?man ### -E, --show-overlap
100-// ?man Similar to e and x, respectively, but treat overlapping changes (i.e., changes that would be noted with ==== in the normal listing) differently. The overlapping lines from both files will be inserted by the edit script, bracketed by "<<<<<<" and ">>>>>>" lines.
101-// ?man ### -X
102-// ?man Similar to e and x, respectively, but treat overlapping changes (i.e., changes that would be noted with ==== in the normal listing) differently. The overlapping lines from both files will be inserted by the edit script, bracketed by "<<<<<<" and ">>>>>>" lines.
103-// ?man ### -e, --ed
104-// ?man Produces output in a form suitable as an input script for the ed(1) utility. The script may then be used to merge differences common between all three files and differences specific to file1 and file3. In other words, the e option ignores differences specific to file1 and file2, and those specific to file2 and file3. It is useful for backing out changes specific to file2 only.
105-// ?man ### --help
106-// ?man Prints usage information and exits.
107-// ?man ### -i
108-// ?man Appends 'w' and 'q' ed(1) commands.
109-// ?man ### -L, --label
110-// ?man Defines labels to print instead of file names file1, file2 and file3.
111-// ?man ### -m, --merge
112-// ?man Merge output instead of generating ed script.
113-// ?man ### -T, --initial-tab
114-// ?man In the normal listing, use a tab instead of two spaces at the beginning of each line. In modes that produce an ed(1) script, this option changes nothing.
115-// ?man ### -x, --overlap-only
116-// ?man Produces an output script suitable for ed(1) with changes specific only to all three versions.
117-// ?man ### --diff-program program
118-// ?man Use program instead of the default diff(1) to compare files.
119-// ?man ### --strip-trailing-cr
120-// ?man Strip trailing carriage return on input files.
121-// ?man ### --version
122-// ?man Prints version information and exits.
123-// ?man ## DESCRIPTION
124-// ?man The diff3 utility compares the contents of three different versions of a file, file1, file2 and file3, writing the result to the standard output. The options describe different methods of merging and purging the separate versions into a new file. diff3 is used by rcs(1) to merge specific versions or create new versions.
125-// ?man
126-// ?man The E option is used by RCS merge(1) to ensure that overlapping changes in the merged files are preserved and brought to someone's attention.
127-// ?man
128-// ?man For example, suppose lines 7-8 are changed in both file1 and file2. Applying the edit script generated by the command
129-// ?man
130-// ?man $ diff3 -E file1 file2 file3
131-// ?man
132-// ?man to file1 results in the file:
133-// ?man
134-// ?man lines 1-6
135-// ?man of file1
136-// ?man <<<<<<< file1
137-// ?man lines 7-8
138-// ?man of file1
139-// ?man =======
140-// ?man lines 7-8
141-// ?man of file3
142-// ?man >>>>>>> file3
143-// ?man rest of file1
144-// ?man
145-// ?man The default output of diff3 makes notation of the differences between all files, and those differences specific to each pair of files. The changes are described by the commands necessary for ed(1) to create the desired target from the different versions. See diff(1) for a description of the commands.
146-// ?man ### ====
147-// ?man The lines beneath this notation are ranges of lines which are different between all files.
148-// ?man ### ====n
149-// ?man The lines beneath this notation are ranges of lines which are exclusively different in file n.
150-// ?man ## SEE ALSO
151-// ?man diff(1), ed(1), merge(1), rcs(1), sdiff(1)
152-// ?man ## HISTORY
153-// ?man A diff3 command appeared in AT&T Version 7 UNIX.
154-// ?man ## BUGS
155-// ?man The e option cannot catch and change lines which have "." as the first and only character on the line. The resulting script will fail on that line as "." is an ed(1) command.
156-
157-#ifndef __dead2
158-#define __dead2 __attribute__((__noreturn__))
159-#endif
160-
161-#ifndef __DECONST
162-#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
163-#endif
164-
165-/*
166- * "from" is first in range of changed lines; "to" is last+1
167- * from=to=line after point of insertion for added lines.
168- */
169-struct range {
170- int from;
171- int to;
172-};
173-
174-struct diff {
175-#define DIFF_TYPE1 1
176-#define DIFF_TYPE2 2
177-#define DIFF_TYPE3 3
178- int type;
179-#if DEBUG
180- char *line;
181-#endif /* DEBUG */
182-
183- /* Ranges as lines */
184- struct range old;
185- struct range new;
186-};
187-
188-#define EFLAG_NONE 0
189-#define EFLAG_OVERLAP 1
190-#define EFLAG_NOOVERLAP 2
191-#define EFLAG_UNMERGED 3
192-
193-static size_t szchanges;
194-
195-static struct diff *d13;
196-static struct diff *d23;
197-/*
198- * "de" is used to gather editing scripts. These are later spewed out in
199- * reverse order. Its first element must be all zero, the "old" and "new"
200- * components of "de" contain line positions. Array overlap indicates which
201- * sections in "de" correspond to lines that are different in all three files.
202- */
203-static struct diff *de;
204-static char *overlap;
205-static int overlapcnt;
206-static FILE *fp[3];
207-static int cline[3]; /* # of the last-read line in each file (0-2) */
208-static int sigpipe[2];
209-/*
210- * The latest known correspondence between line numbers of the 3 files
211- * is stored in last[1-3];
212- */
213-static int last[4];
214-static int Aflag, eflag, iflag, mflag, Tflag;
215-static int oflag; /* indicates whether to mark overlaps (-E or -X) */
216-static int strip_cr;
217-static char *f1mark, *f2mark, *f3mark;
218-static const char *oldmark = "<<<<<<<";
219-static const char *orgmark = "|||||||";
220-static const char *newmark = ">>>>>>>";
221-static const char *divider = "=======";
222-
223-static bool duplicate(struct range *, struct range *);
224-static int edit(struct diff *, bool, int, int);
225-static char *getchange(FILE *);
226-static char *get_line(FILE *, size_t *);
227-static int readin(int fd, struct diff **);
228-static int skip(int, int, const char *);
229-static void change(int, struct range *, bool);
230-static void keep(int, struct range *);
231-static void merge(int, int);
232-static void prange(struct range *, bool);
233-static void repos(int);
234-static void separate(const char *);
235-static void edscript(int) __dead2;
236-static void Ascript(int) __dead2;
237-static void mergescript(int) __dead2;
238-static void increase(void);
239-static void usage(void);
240-static void printrange(FILE *, struct range *);
241-
242-static const char diff3_version[] = "FreeBSD diff3 20240925";
243-
244-enum {
245- DIFFPROG_OPT,
246- STRIPCR_OPT,
247- HELP_OPT,
248- VERSION_OPT
249-};
250-
251-#define DIFF_PATH "/usr/bin/diff"
252-
253-#define OPTIONS "3aAeEiL:mTxX"
254-static struct option longopts[] = {
255- { "ed", no_argument, NULL, 'e' },
256- { "show-overlap", no_argument, NULL, 'E' },
257- { "overlap-only", no_argument, NULL, 'x' },
258- { "initial-tab", no_argument, NULL, 'T' },
259- { "text", no_argument, NULL, 'a' },
260- { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT },
261- { "show-all", no_argument, NULL, 'A' },
262- { "easy-only", no_argument, NULL, '3' },
263- { "merge", no_argument, NULL, 'm' },
264- { "label", required_argument, NULL, 'L' },
265- { "diff-program", required_argument, NULL, DIFFPROG_OPT },
266- { "help", no_argument, NULL, HELP_OPT},
267- { "version", no_argument, NULL, VERSION_OPT}
268-};
269-
270-static void
271-usage(void)
272-{
273- fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
274- "[-L label3] file1 file2 file3\n");
275-}
276-
277-// ?man diff3: 3-way differential file comparison
278-// ?man arguments: file1 file2 file3
279-// ?man synopsis: [-3AaEeimTXx] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
280-// ?man synopsis: [--help]
281-// ?man synopsis: [--version]
282-// ?man The diff3 utility compares the contents of three different versions of a file, file1, file2 and file3, writing the result to the standard output. The options describe different methods of merging and purging the separate versions into a new file. diff3 is used by rcs(1) to merge specific versions or create new versions.
283-// ?man ## OPTIONS
284-// ?man ### -3, --easy-only
285-// ?man Produces an output script suitable for ed(1) with changes specific only to file3.
286-// ?man ### -A, --show-all
287-// ?man Output all changes, bracketing conflicts.
288-// ?man ### -a, --text
289-// ?man Treat all files as ASCII.
290-// ?man ### -E, --show-overlap
291-// ?man Similar to e and x, respectively, but treat overlapping changes (i.e., changes that would be noted with ==== in the normal listing) differently. The overlapping lines from both files will be inserted by the edit script, bracketed by "<<<<<<" and ">>>>>>" lines.
292-// ?man ### -X
293-// ?man Similar to e and x, respectively, but treat overlapping changes (i.e., changes that would be noted with ==== in the normal listing) differently. The overlapping lines from both files will be inserted by the edit script, bracketed by "<<<<<<" and ">>>>>>" lines.
294-// ?man ### -e, --ed
295-// ?man Produces output in a form suitable as an input script for the ed(1) utility. The script may then be used to merge differences common between all three files and differences specific to file1 and file3. In other words, the e option ignores differences specific to file1 and file2, and those specific to file2 and file3. It is useful for backing out changes specific to file2 only.
296-// ?man ### --help
297-// ?man Prints usage information and exits.
298-// ?man ### -i
299-// ?man Appends 'w' and 'q' ed(1) commands.
300-// ?man ### -L, --label
301-// ?man Defines labels to print instead of file names file1, file2 and file3.
302-// ?man ### -m, --merge
303-// ?man Merge output instead of generating ed script.
304-// ?man ### -T, --initial-tab
305-// ?man In the normal listing, use a tab instead of two spaces at the beginning of each line. In modes that produce an ed(1) script, this option changes nothing.
306-// ?man ### -x, --overlap-only
307-// ?man Produces an output script suitable for ed(1) with changes specific only to all three versions.
308-// ?man ### --diff-program program
309-// ?man Use program instead of the default diff(1) to compare files.
310-// ?man ### --strip-trailing-cr
311-// ?man Strip trailing carriage return on input files.
312-// ?man ### --version
313-// ?man Prints version information and exits.
314-// ?man ## DESCRIPTION
315-// ?man The E option is used by RCS merge(1) to ensure that overlapping changes in the merged files are preserved and brought to someone's attention.
316-// ?man
317-// ?man For example, suppose lines 7-8 are changed in both file1 and file2. Applying the edit script generated by the command
318-// ?man
319-// ?man $ diff3 -E file1 file2 file3
320-// ?man
321-// ?man to file1 results in the file:
322-// ?man
323-// ?man lines 1-6
324-// ?man of file1
325-// ?man <<<<<<< file1
326-// ?man lines 7-8
327-// ?man of file1
328-// ?man =======
329-// ?man lines 7-8
330-// ?man of file3
331-// ?man >>>>>>> file3
332-// ?man rest of file1
333-// ?man
334-// ?man The default output of diff3 makes notation of the differences between all files, and those differences specific to each pair of files. The changes are described by the commands necessary for ed(1) to create the desired target from the different versions. See diff(1) for a description of the commands.
335-// ?man ### ====
336-// ?man The lines beneath this notation are ranges of lines which are different between all files.
337-// ?man ### ====n
338-// ?man The lines beneath this notation are ranges of lines which are exclusively different in file n.
339-// ?man ## SEE ALSO
340-// ?man diff(1), ed(1), merge(1), rcs(1), sdiff(1)
341-// ?man ## HISTORY
342-// ?man A diff3 command appeared in AT&T Version 7 UNIX.
343-// ?man ## BUGS
344-// ?man The e option cannot catch and change lines which have "." as the first and only character on the line. The resulting script will fail on that line as "." is an ed(1) command.
345-
346-static int
347-strtoi(char *str, char **end)
348-{
349- intmax_t num;
350-
351- errno = 0;
352- num = strtoimax(str, end, 10);
353- if ((end != NULL && *end == str) ||
354- num < 0 || num > INT_MAX ||
355- errno == EINVAL || errno == ERANGE)
356- err(1, "error in diff output");
357- return (int)num;
358-}
359-
360-/*
361- * Read diff hunks into the array pointed to by *dd.
362- *
363- * The output from `diff foo bar` consists of a series of hunks describing
364- * an addition (lines in bar not present in foo), change (lines in bar
365- * different from lines in foo), or deletion (lines in foo not present in
366- * bar). Each record starts with a line of the form:
367- *
368- * a[,b]xc[,d]
369- *
370- * where a, b, c, and d are nonnegative integers (b and d are printed only
371- * if they differ from a and c, respectively), and x is either 'a' for an
372- * addition, 'c' for a change, or 'd' for a deletion. This is then
373- * followed by a series of lines (which we ignore) giving the added,
374- * changed, or deleted text.
375- *
376- * For an addition, a == b is the last line in 'foo' before the addition,
377- * while c through d is the range of lines in 'bar' to be added to 'foo'.
378- *
379- * For a change, a through b is the range of lines in 'foo' to be replaced
380- * and c through d is the range of lines in 'bar' to replace them with.
381- *
382- * For a deletion, a through b is the range of lines in 'foo' to remove
383- * and c == d is the line in 'bar' which corresponds to the last line
384- * before the deletion.
385- *
386- * The observant reader will have noticed that x is not really needed and
387- * that we can fully describe any hunk using only a, b, c, and d:
388- *
389- * - an addition replaces a zero-length range in one file with a
390- * non-zero-length range from the other
391- *
392- * - a change replaces a non-zero-length range in one file with a
393- * non-zero-length range from the other
394- *
395- * - a deletion replaces a non-zero-length range in one file with a
396- * zero-length range from the other
397- */
398-static int
399-readin(int fd, struct diff **dd)
400-{
401- int a, b, c, d;
402- int i;
403- char kind, *p;
404- FILE *f;
405-
406- f = fdopen(fd, "r");
407- if (f == NULL)
408- err(2, "fdopen");
409- for (i = 0; (p = getchange(f)) != NULL; i++) {
410- if ((size_t)i >= szchanges - 1)
411- increase();
412-#if DEBUG
413- (*dd)[i].line = strdup(p);
414-#endif /* DEBUG */
415-
416- a = b = strtoi(p, &p);
417- if (*p == ',')
418- b = strtoi(p + 1, &p);
419- kind = *p++;
420- c = d = strtoi(p, &p);
421- if (*p == ',')
422- d = strtoi(p + 1, &p);
423- if (*p != '\n')
424- errx(1, "error in diff output");
425- if (kind == 'a')
426- a++;
427- else if (kind == 'c')
428- /* nothing */ ;
429- else if (kind == 'd')
430- c++;
431- else
432- errx(1, "error in diff output");
433- b++;
434- d++;
435- if (b < a || d < c)
436- errx(1, "error in diff output");
437- (*dd)[i].old.from = a;
438- (*dd)[i].old.to = b;
439- (*dd)[i].new.from = c;
440- (*dd)[i].new.to = d;
441- if (i > 0) {
442- if ((*dd)[i].old.from < (*dd)[i - 1].old.to ||
443- (*dd)[i].new.from < (*dd)[i - 1].new.to)
444- errx(1, "diff output out of order");
445- }
446- }
447- if (i > 0) {
448- (*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to;
449- (*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to;
450- }
451- fclose(f);
452- return (i);
453-}
454-
455-static void
456-diffexec(const char *diffprog, char **diffargv, int fd[])
457-{
458- switch (fork()) {
459- case 0:
460- close(fd[0]);
461- if (dup2(fd[1], STDOUT_FILENO) == -1)
462- err(2, "child could not duplicate descriptor");
463- close(fd[1]);
464- execvp(diffprog, diffargv);
465- err(2, "could not execute diff: %s", diffprog);
466- break;
467- case -1:
468- err(2, "could not fork");
469- break;
470- }
471- close(fd[1]);
472-}
473-
474-static char *
475-getchange(FILE *b)
476-{
477- char *line;
478-
479- while ((line = get_line(b, NULL)) != NULL) {
480- if (isdigit((unsigned char)line[0]))
481- return (line);
482- }
483- return (NULL);
484-}
485-
486-
487-static char *
488-get_line(FILE *b, size_t *n)
489-{
490- ssize_t len;
491- static char *buf = NULL;
492- static size_t bufsize = 0;
493-
494- if ((len = getline(&buf, &bufsize, b)) < 0)
495- return (NULL);
496-
497- if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
498- buf[len - 2] = '\n';
499- buf[len - 1] = '\0';
500- len--;
501- }
502-
503- if (n != NULL)
504- *n = len;
505-
506- return (buf);
507-}
508-
509-static void
510-merge(int m1, int m2)
511-{
512- struct diff *d1, *d2, *d3;
513- int j, t1, t2;
514- bool dup = false;
515-
516- d1 = d13;
517- d2 = d23;
518- j = 0;
519-
520- for (;;) {
521- t1 = (d1 < d13 + m1);
522- t2 = (d2 < d23 + m2);
523- if (!t1 && !t2)
524- break;
525-
526- /* first file is different from the others */
527- if (!t2 || (t1 && d1->new.to < d2->new.from)) {
528- /* stuff peculiar to 1st file */
529- if (eflag == EFLAG_NONE) {
530- separate("1");
531- change(1, &d1->old, false);
532- keep(2, &d1->new);
533- change(3, &d1->new, false);
534- } else if (eflag == EFLAG_OVERLAP) {
535- j = edit(d2, dup, j, DIFF_TYPE1);
536- }
537- d1++;
538- continue;
539- }
540- /* second file is different from others */
541- if (!t1 || (t2 && d2->new.to < d1->new.from)) {
542- if (eflag == EFLAG_NONE) {
543- separate("2");
544- keep(1, &d2->new);
545- change(3, &d2->new, false);
546- change(2, &d2->old, false);
547- } else if (Aflag || mflag) {
548- // XXX-THJ: What does it mean for the second file to differ?
549- if (eflag == EFLAG_UNMERGED)
550- j = edit(d2, dup, j, DIFF_TYPE2);
551- }
552- d2++;
553- continue;
554- }
555- /*
556- * Merge overlapping changes in first file
557- * this happens after extension (see below).
558- */
559- if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
560- d1[1].old.from = d1->old.from;
561- d1[1].new.from = d1->new.from;
562- d1++;
563- continue;
564- }
565-
566- /* merge overlapping changes in second */
567- if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
568- d2[1].old.from = d2->old.from;
569- d2[1].new.from = d2->new.from;
570- d2++;
571- continue;
572- }
573- /* stuff peculiar to third file or different in all */
574- if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
575- dup = duplicate(&d1->old, &d2->old);
576- /*
577- * dup = 0 means all files differ
578- * dup = 1 means files 1 and 2 identical
579- */
580- if (eflag == EFLAG_NONE) {
581- separate(dup ? "3" : "");
582- change(1, &d1->old, dup);
583- change(2, &d2->old, false);
584- d3 = d1->old.to > d1->old.from ? d1 : d2;
585- change(3, &d3->new, false);
586- } else {
587- j = edit(d1, dup, j, DIFF_TYPE3);
588- }
589- dup = false;
590- d1++;
591- d2++;
592- continue;
593- }
594- /*
595- * Overlapping changes from file 1 and 2; extend changes
596- * appropriately to make them coincide.
597- */
598- if (d1->new.from < d2->new.from) {
599- d2->old.from -= d2->new.from - d1->new.from;
600- d2->new.from = d1->new.from;
601- } else if (d2->new.from < d1->new.from) {
602- d1->old.from -= d1->new.from - d2->new.from;
603- d1->new.from = d2->new.from;
604- }
605- if (d1->new.to > d2->new.to) {
606- d2->old.to += d1->new.to - d2->new.to;
607- d2->new.to = d1->new.to;
608- } else if (d2->new.to > d1->new.to) {
609- d1->old.to += d2->new.to - d1->new.to;
610- d1->new.to = d2->new.to;
611- }
612- }
613-
614- if (mflag)
615- mergescript(j);
616- else if (Aflag)
617- Ascript(j);
618- else if (eflag)
619- edscript(j);
620-}
621-
622-static void
623-separate(const char *s)
624-{
625- printf("====%s\n", s);
626-}
627-
628-/*
629- * The range of lines rold.from thru rold.to in file i is to be changed.
630- * It is to be printed only if it does not duplicate something to be
631- * printed later.
632- */
633-static void
634-change(int i, struct range *rold, bool dup)
635-{
636-
637- printf("%d:", i);
638- last[i] = rold->to;
639- prange(rold, false);
640- if (dup)
641- return;
642- i--;
643- skip(i, rold->from, NULL);
644- skip(i, rold->to, " ");
645-}
646-
647-/*
648- * Print the range of line numbers, rold.from thru rold.to, as n1,n2 or
649- * n1.
650- */
651-static void
652-prange(struct range *rold, bool delete)
653-{
654-
655- if (rold->to <= rold->from)
656- printf("%da\n", rold->from - 1);
657- else {
658- printf("%d", rold->from);
659- if (rold->to > rold->from + 1)
660- printf(",%d", rold->to - 1);
661- if (delete)
662- printf("d\n");
663- else
664- printf("c\n");
665- }
666-}
667-
668-/*
669- * No difference was reported by diff between file 1 (or 2) and file 3,
670- * and an artificial dummy difference (trange) must be ginned up to
671- * correspond to the change reported in the other file.
672- */
673-static void
674-keep(int i, struct range *rnew)
675-{
676- int delta;
677- struct range trange;
678-
679- delta = last[3] - last[i];
680- trange.from = rnew->from - delta;
681- trange.to = rnew->to - delta;
682- change(i, &trange, true);
683-}
684-
685-/*
686- * skip to just before line number from in file "i". If "pr" is non-NULL,
687- * print all skipped stuff with string pr as a prefix.
688- */
689-static int
690-skip(int i, int from, const char *pr)
691-{
692- size_t j, n;
693- char *line;
694-
695- for (n = 0; cline[i] < from - 1; n += j) {
696- if ((line = get_line(fp[i], &j)) == NULL)
697- errx(EXIT_FAILURE, "logic error");
698- if (pr != NULL)
699- printf("%s%s", Tflag == 1 ? "\t" : pr, line);
700- cline[i]++;
701- }
702- return ((int) n);
703-}
704-
705-/*
706- * Return 1 or 0 according as the old range (in file 1) contains exactly
707- * the same data as the new range (in file 2).
708- */
709-static bool
710-duplicate(struct range *r1, struct range *r2)
711-{
712- int c, d;
713- int nchar;
714- int nline;
715-
716- if (r1->to-r1->from != r2->to-r2->from)
717- return (0);
718- skip(0, r1->from, NULL);
719- skip(1, r2->from, NULL);
720- nchar = 0;
721- for (nline = 0; nline < r1->to - r1->from; nline++) {
722- do {
723- c = getc(fp[0]);
724- d = getc(fp[1]);
725- if (c == -1 && d == -1)
726- break;
727- if (c == -1 || d == -1)
728- errx(EXIT_FAILURE, "logic error");
729- nchar++;
730- if (c != d) {
731- repos(nchar);
732- return (0);
733- }
734- } while (c != '\n');
735- }
736- repos(nchar);
737- return (1);
738-}
739-
740-static void
741-repos(int nchar)
742-{
743- int i;
744-
745- for (i = 0; i < 2; i++)
746- (void)fseek(fp[i], (long)-nchar, SEEK_CUR);
747-}
748-
749-/*
750- * collect an editing script for later regurgitation
751- */
752-static int
753-edit(struct diff *diff, bool dup, int j, int difftype)
754-{
755- if (!(eflag == EFLAG_UNMERGED ||
756- (!dup && eflag == EFLAG_OVERLAP ) ||
757- (dup && eflag == EFLAG_NOOVERLAP))) {
758- return (j);
759- }
760- j++;
761- overlap[j] = !dup;
762- if (!dup)
763- overlapcnt++;
764-
765- de[j].type = difftype;
766-#if DEBUG
767- de[j].line = strdup(diff->line);
768-#endif /* DEBUG */
769-
770- de[j].old.from = diff->old.from;
771- de[j].old.to = diff->old.to;
772- de[j].new.from = diff->new.from;
773- de[j].new.to = diff->new.to;
774- return (j);
775-}
776-
777-static void
778-printrange(FILE *p, struct range *r)
779-{
780- char *line = NULL;
781- size_t len = 0;
782- int i = 1;
783-
784- /* We haven't been asked to print anything */
785- if (r->from == r->to)
786- return;
787-
788- if (r->from > r->to)
789- errx(EXIT_FAILURE, "invalid print range");
790-
791- /*
792- * XXX-THJ: We read through all of the file for each range printed.
793- * This duplicates work and will probably impact performance on large
794- * files with lots of ranges.
795- */
796- fseek(p, 0L, SEEK_SET);
797- while (getline(&line, &len, p) > 0) {
798- if (i >= r->from)
799- printf("%s", line);
800- if (++i > r->to - 1)
801- break;
802- }
803- free(line);
804-}
805-
806-/* regurgitate */
807-static void
808-edscript(int n)
809-{
810- bool delete;
811- struct range *new, *old;
812-
813- for (; n > 0; n--) {
814- new = &de[n].new;
815- old = &de[n].old;
816-
817- delete = (new->from == new->to);
818- if (de[n].type == DIFF_TYPE1) {
819- if (delete)
820- printf("%dd\n", new->from - 1);
821- else if (old->from == new->from && old->to == new->to) {
822- printf("%dc\n", old->from);
823- printrange(fp[2], old);
824- printf(".\n");
825- }
826- continue;
827- } else {
828- if (!oflag || !overlap[n]) {
829- prange(old, delete);
830- } else {
831- printf("%da\n", old->to - 1);
832- printf("%s\n", divider);
833- }
834- printrange(fp[2], new);
835- if (!oflag || !overlap[n]) {
836- if (!delete)
837- printf(".\n");
838- } else {
839- printf("%s %s\n.\n", newmark, f3mark);
840- printf("%da\n%s %s\n.\n", old->from - 1,
841- oldmark, f1mark);
842- }
843- }
844- }
845- if (iflag)
846- printf("w\nq\n");
847-
848- exit(eflag == EFLAG_NONE ? overlapcnt : 0);
849-}
850-
851-/*
852- * Output an edit script to turn mine into yours, when there is a conflict
853- * between the 3 files bracket the changes. Regurgitate the diffs in reverse
854- * order to allow the ed script to track down where the lines are as changes
855- * are made.
856- */
857-static void
858-Ascript(int n)
859-{
860- int startmark;
861- bool deletenew;
862- bool deleteold;
863-
864- struct range *new, *old;
865-
866- for (; n > 0; n--) {
867- new = &de[n].new;
868- old = &de[n].old;
869- deletenew = (new->from == new->to);
870- deleteold = (old->from == old->to);
871-
872- if (de[n].type == DIFF_TYPE2) {
873- if (!oflag || !overlap[n]) {
874- prange(old, deletenew);
875- printrange(fp[2], new);
876- } else {
877- startmark = new->to - 1;
878-
879- printf("%da\n", startmark);
880- printf("%s %s\n", newmark, f3mark);
881-
882- printf(".\n");
883-
884- printf("%da\n", startmark -
885- (new->to - new->from));
886- printf("%s %s\n", oldmark, f2mark);
887- if (!deleteold)
888- printrange(fp[1], old);
889- printf("%s\n.\n", divider);
890- }
891-
892- } else if (de[n].type == DIFF_TYPE3) {
893- startmark = old->to - 1;
894-
895- if (!oflag || !overlap[n]) {
896- prange(old, deletenew);
897- printrange(fp[2], new);
898- } else {
899- printf("%da\n", startmark);
900- printf("%s %s\n", orgmark, f2mark);
901-
902- if (deleteold) {
903- struct range r;
904- r.from = old->from-1;
905- r.to = new->to;
906- printrange(fp[1], &r);
907- } else
908- printrange(fp[1], old);
909-
910- printf("%s\n", divider);
911- printrange(fp[2], new);
912- }
913-
914- if (!oflag || !overlap[n]) {
915- if (!deletenew)
916- printf(".\n");
917- } else {
918- printf("%s %s\n.\n", newmark, f3mark);
919-
920- /*
921- * Go to the start of the conflict in original
922- * file and append lines
923- */
924- printf("%da\n%s %s\n.\n",
925- startmark - (old->to - old->from),
926- oldmark, f1mark);
927- }
928- }
929- }
930- if (iflag)
931- printf("w\nq\n");
932-
933- exit(overlapcnt > 0);
934-}
935-
936-/*
937- * Output the merged file directly (don't generate an ed script). When
938- * regurgitating diffs we need to walk forward through the file and print any
939- * inbetween lines.
940- */
941-static void
942-mergescript(int i)
943-{
944- struct range r, *new, *old;
945- int n;
946- bool delete = false;
947-
948- r.from = 1;
949- r.to = 1;
950-
951- for (n = 1; n <= i; n++) {
952- new = &de[n].new;
953- old = &de[n].old;
954-
955- /*
956- * Print any lines leading up to here. If we are merging don't
957- * print deleted ranges.
958- */
959- delete = (new->from == new->to);
960- if (de[n].type == DIFF_TYPE1 && delete)
961- r.to = new->from - 1;
962- else if (de[n].type == DIFF_TYPE3 && (old->from == old->to)) {
963- r.from = old->from - 1;
964- r.to = new->from;
965- } else
966- r.to = old->from;
967-
968- printrange(fp[0], &r);
969- switch (de[n].type) {
970- case DIFF_TYPE1:
971- /* If this isn't a delete print it */
972- if (!delete)
973- printrange(fp[2], new);
974- break;
975- case DIFF_TYPE2:
976- printf("%s %s\n", oldmark, f2mark);
977- printrange(fp[1], old);
978- printf("%s\n", divider);
979- printrange(fp[2], new);
980- printf("%s %s\n", newmark, f3mark);
981- break;
982- case DIFF_TYPE3:
983- if (!oflag || !overlap[n]) {
984- printrange(fp[2], new);
985- } else {
986-
987- printf("%s %s\n", oldmark, f1mark);
988- printrange(fp[0], old);
989-
990- if (eflag != EFLAG_OVERLAP) {
991- printf("%s %s\n", orgmark, f2mark);
992- if (old->from == old->to) {
993- struct range or;
994- or.from = old->from - 1;
995- or.to = new->to;
996- printrange(fp[1], &or);
997- } else {
998- printrange(fp[1], old);
999- }
1000- }
1001-
1002- printf("%s\n", divider);
1003-
1004- printrange(fp[2], new);
1005- printf("%s %s\n", newmark, f3mark);
1006- }
1007- break;
1008- default:
1009- printf("Error: Unhandled diff type - exiting\n");
1010- exit(EXIT_FAILURE);
1011- }
1012-
1013- if (old->from == old->to)
1014- r.from = new->to;
1015- else
1016- r.from = old->to;
1017- }
1018-
1019- /*
1020- * Print from the final range to the end of 'myfile'. Any deletions or
1021- * additions to this file should have been handled by now.
1022- *
1023- * If the ranges are the same we need to rewind a line.
1024- * If the new range is 0 length (from == to), we need to use the old
1025- * range.
1026- */
1027- new = &de[n-1].new;
1028- old = &de[n-1].old;
1029-
1030- if (old->from == new->from && old->to == new->to)
1031- r.from--;
1032- else if (new->from == new->to)
1033- r.from = old->from;
1034-
1035- r.to = INT_MAX;
1036- printrange(fp[2], &r);
1037- exit(overlapcnt > 0);
1038-}
1039-
1040-static void
1041-increase(void)
1042-{
1043- struct diff *p;
1044- char *q;
1045- size_t newsz, incr;
1046-
1047- /* are the memset(3) calls needed? */
1048- newsz = szchanges == 0 ? 64 : 2 * szchanges;
1049- incr = newsz - szchanges;
1050-
1051- p = reallocarray(d13, newsz, sizeof(*p));
1052- if (p == NULL)
1053- err(1, NULL);
1054- memset(p + szchanges, 0, incr * sizeof(*p));
1055- d13 = p;
1056- p = reallocarray(d23, newsz, sizeof(*p));
1057- if (p == NULL)
1058- err(1, NULL);
1059- memset(p + szchanges, 0, incr * sizeof(*p));
1060- d23 = p;
1061- p = reallocarray(de, newsz, sizeof(*p));
1062- if (p == NULL)
1063- err(1, NULL);
1064- memset(p + szchanges, 0, incr * sizeof(*p));
1065- de = p;
1066- q = reallocarray(overlap, newsz, 1);
1067- if (q == NULL)
1068- err(1, NULL);
1069- memset(q + szchanges, 0, incr * 1);
1070- overlap = q;
1071- szchanges = newsz;
1072-}
1073-
1074-static void
1075-handle_sig(int signo)
1076-{
1077- write(sigpipe[1], &signo, sizeof(signo));
1078-}
1079-
1080-int
1081-main(int argc, char **argv)
1082-{
1083- int ch, nblabels, status, m, n, npe, nleft;
1084- char *labels[] = { NULL, NULL, NULL };
1085- const char *diffprog = DIFF_PATH;
1086- char *file1, *file2, *file3;
1087- char *diffargv[7];
1088- int diffargc = 0;
1089- int fd13[2], fd23[2], signo;
1090- pid_t wpid;
1091- struct pollfd pfd;
1092-
1093- nblabels = 0;
1094- eflag = EFLAG_NONE;
1095- oflag = 0;
1096- diffargv[diffargc++] = __DECONST(char *, diffprog);
1097- while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
1098- switch (ch) {
1099- case '3':
1100- eflag = EFLAG_NOOVERLAP;
1101- break;
1102- case 'a':
1103- diffargv[diffargc++] = __DECONST(char *, "-a");
1104- break;
1105- case 'A':
1106- Aflag = 1;
1107- break;
1108- case 'e':
1109- eflag = EFLAG_UNMERGED;
1110- break;
1111- case 'E':
1112- eflag = EFLAG_OVERLAP;
1113- oflag = 1;
1114- break;
1115- case 'i':
1116- iflag = 1;
1117- break;
1118- case 'L':
1119- oflag = 1;
1120- if (nblabels >= 3)
1121- errx(2, "too many file label options");
1122- labels[nblabels++] = optarg;
1123- break;
1124- case 'm':
1125- Aflag = 1;
1126- oflag = 1;
1127- mflag = 1;
1128- break;
1129- case 'T':
1130- Tflag = 1;
1131- break;
1132- case 'x':
1133- eflag = EFLAG_OVERLAP;
1134- break;
1135- case 'X':
1136- oflag = 1;
1137- eflag = EFLAG_OVERLAP;
1138- break;
1139- case DIFFPROG_OPT:
1140- diffprog = optarg;
1141- break;
1142- case STRIPCR_OPT:
1143- strip_cr = 1;
1144- diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr");
1145- break;
1146- case HELP_OPT:
1147- usage();
1148- exit(0);
1149- case VERSION_OPT:
1150- printf("%s\n", diff3_version);
1151- exit(0);
1152- }
1153- }
1154- argc -= optind;
1155- argv += optind;
1156-
1157- if (Aflag) {
1158- if (eflag == EFLAG_NONE)
1159- eflag = EFLAG_UNMERGED;
1160- oflag = 1;
1161- }
1162-
1163- if (argc != 3) {
1164- usage();
1165- exit(2);
1166- }
1167-
1168- /* TODO stdio */
1169- file1 = argv[0];
1170- file2 = argv[1];
1171- file3 = argv[2];
1172-
1173- if (oflag) {
1174- xasprintf(&f1mark, "%s",
1175- labels[0] != NULL ? labels[0] : file1);
1176- xasprintf(&f2mark, "%s",
1177- labels[1] != NULL ? labels[1] : file2);
1178- xasprintf(&f3mark, "%s",
1179- labels[2] != NULL ? labels[2] : file3);
1180- }
1181- fp[0] = fopen(file1, "r");
1182- if (fp[0] == NULL)
1183- err(2, "Can't open %s", file1);
1184-
1185- fp[1] = fopen(file2, "r");
1186- if (fp[1] == NULL)
1187- err(2, "Can't open %s", file2);
1188-
1189- fp[2] = fopen(file3, "r");
1190- if (fp[2] == NULL)
1191- err(2, "Can't open %s", file3);
1192-
1193- if (pipe(fd13))
1194- err(2, "pipe");
1195- if (pipe(fd23))
1196- err(2, "pipe");
1197- if (pipe(sigpipe))
1198- err(2, "pipe");
1199- if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC))
1200- err(2, "fcntl");
1201- if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC))
1202- err(2, "fcntl");
1203-
1204- pfd.fd = sigpipe[0];
1205- pfd.events = POLLIN;
1206- pfd.revents = 0;
1207-
1208- if (signal(SIGCHLD, handle_sig) == SIG_ERR)
1209- err(2, "signal");
1210-
1211- diffargv[diffargc] = file1;
1212- diffargv[diffargc + 1] = file3;
1213- diffargv[diffargc + 2] = NULL;
1214-
1215- nleft = 0;
1216- diffexec(diffprog, diffargv, fd13);
1217- nleft++;
1218-
1219- diffargv[diffargc] = file2;
1220- diffexec(diffprog, diffargv, fd23);
1221- nleft++;
1222-
1223- /* parse diffs */
1224- increase();
1225- m = readin(fd13[0], &d13);
1226- n = readin(fd23[0], &d23);
1227-
1228- /* waitpid */
1229- while (nleft > 0) {
1230- npe = poll(&pfd, 1, -1);
1231- if (npe == -1) {
1232- if (errno == EINTR) continue;
1233- err(2, "poll");
1234- }
1235- if (pfd.revents != POLLIN)
1236- continue;
1237- if (read(pfd.fd, &signo, sizeof(signo)) < 0)
1238- err(2, "read");
1239- while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {
1240- if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
1241- errx(2, "diff exited abnormally");
1242- else if (WIFSIGNALED(status))
1243- errx(2, "diff killed by signal %d",
1244- WTERMSIG(status));
1245- nleft--;
1246- }
1247- }
1248- merge(m, n);
1249-
1250- return (EXIT_SUCCESS);
1251-}
+355,
-83
1@@ -2,11 +2,11 @@
2 #include "util.h"
3 #include "diskutil.h"
4
5+#include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10-#include <fcntl.h>
11
12 struct MbrPartition {
13 uint8_t boot;
14@@ -49,6 +49,47 @@ struct GptPartition {
15 uint16_t name[36];
16 } __attribute__((packed));
17
18+struct BsdPartition {
19+ uint32_t size;
20+ uint32_t offset;
21+ uint16_t offset_h;
22+ uint16_t size_h;
23+ uint8_t fstype;
24+ uint8_t fragblock;
25+ uint16_t cpg;
26+} __attribute__((packed));
27+
28+struct BsdDisklabel {
29+ uint32_t d_magic;
30+ uint16_t d_type;
31+ uint16_t d_subtype;
32+ char d_typename[16];
33+ char d_packname[16];
34+ uint32_t d_secsize;
35+ uint32_t d_nsectors;
36+ uint32_t d_ntracks;
37+ uint32_t d_ncylinders;
38+ uint32_t d_secpercyl;
39+ uint32_t d_secperunit;
40+ uint8_t d_uid[8];
41+ uint32_t d_acylinders;
42+ uint16_t d_bstarth;
43+ uint16_t d_bendh;
44+ uint32_t d_bstart;
45+ uint32_t d_bend;
46+ uint32_t d_flags;
47+ uint32_t d_spare4[5];
48+ uint16_t d_secperunith;
49+ uint16_t d_version;
50+ uint32_t d_spare[4];
51+ uint32_t d_magic2;
52+ uint16_t d_checksum;
53+ uint16_t d_npartitions;
54+ uint32_t d_spare2;
55+ uint32_t d_spare3;
56+ struct BsdPartition d_partitions[16];
57+} __attribute__((packed));
58+
59 static uint32_t crc32_table[256];
60 static int crc32_table_initialized = 0;
61
62@@ -84,9 +125,11 @@ static int opt_list = 0;
63 static int opt_print = 0;
64 static int opt_init_gpt = 0;
65 static int opt_init_mbr = 0;
66+static int opt_init_bsd = 0;
67 static int opt_add = 0;
68 static int opt_del = 0;
69 static int opt_part_idx = -1;
70+static int opt_part_is_letter = 0;
71 static uint64_t opt_start = 0;
72 static uint64_t opt_end = 0;
73 static const char *opt_type = NULL;
74@@ -94,11 +137,11 @@ static const char *opt_type = NULL;
75 static void
76 usage(void)
77 {
78- eprintf("usage: %s [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]\n", argv0);
79+ eprintf("usage: %s [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]\n", argv0);
80 }
81
82 static void
83-print_mbr(struct MbrHeader *mbr, const char *path)
84+mbr_print(struct MbrHeader *mbr, const char *path)
85 {
86 int i;
87 printf("Disk: %s\n", path);
88@@ -118,7 +161,7 @@ print_mbr(struct MbrHeader *mbr, const char *path)
89 }
90
91 static void
92-print_gpt(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
93+gpt_print(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
94 {
95 uint32_t i;
96 char guid[37];
97@@ -147,7 +190,7 @@ print_gpt(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
98 }
99
100 static int
101-read_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
102+gpt_read(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
103 {
104 unsigned char sector[512];
105 size_t part_sectors;
106@@ -168,31 +211,31 @@ read_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts
107 }
108
109 static int
110-write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
111+gpt_write(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
112 {
113 unsigned char sector[512];
114- size_t part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
115+ struct MbrHeader pmbr;
116+ struct GptHeader backup_hdr;
117+ uint64_t limit;
118+ size_t part_sectors;
119+
120+ part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
121
122- /* update partition entries crc */
123 hdr->parts_crc = crc32(~0U, parts, hdr->num_parts * hdr->part_size) ^ ~0U;
124
125- /* calculate header crc (zeroing the crc field first) */
126 hdr->crc = 0;
127 hdr->crc = crc32(~0U, hdr, hdr->size) ^ ~0U;
128
129- /* write protective mbr to sector 0 */
130- struct MbrHeader pmbr;
131 memset(&pmbr, 0, sizeof(pmbr));
132 pmbr.parts[0].type = 0xEE;
133 pmbr.parts[0].start_lba = 1;
134- uint64_t limit = dev->size / dev->sec_size - 1;
135+ limit = dev->size / dev->sec_size - 1;
136 pmbr.parts[0].size = limit > 0xFFFFFFFFU ? 0xFFFFFFFFU : (uint32_t)limit;
137 pmbr.sig[0] = 0x55;
138 pmbr.sig[1] = 0xAA;
139 if (blockdev_write(dev, 0, &pmbr, 1) < 0)
140 return -1;
141
142- /* write primary header and partition entries */
143 memset(sector, 0, sizeof(sector));
144 memcpy(sector, hdr, sizeof(*hdr));
145 if (blockdev_write(dev, 1, sector, 1) < 0)
146@@ -200,8 +243,7 @@ write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *part
147 if (blockdev_write(dev, hdr->partition_lba, parts, part_sectors) < 0)
148 return -1;
149
150- /* write backup partition entries and backup header */
151- struct GptHeader backup_hdr = *hdr;
152+ backup_hdr = *hdr;
153 backup_hdr.current_lba = hdr->backup_lba;
154 backup_hdr.backup_lba = hdr->current_lba;
155 backup_hdr.partition_lba = backup_hdr.current_lba - part_sectors;
156@@ -220,6 +262,99 @@ write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *part
157 return 0;
158 }
159
160+static uint16_t
161+bsd_dkcksum(void *lp, size_t nparts)
162+{
163+ uint16_t *start;
164+ uint16_t *end;
165+ uint16_t sum = 0;
166+
167+ start = (uint16_t *)lp;
168+ end = (uint16_t *)((char *)lp + 148 + nparts * 16);
169+ while (start < end)
170+ sum ^= *start++;
171+ return sum;
172+}
173+
174+static int
175+bsd_is_partition_type(uint8_t type)
176+{
177+ return type == 0xa5 || type == 0xa6 || type == 0xa9;
178+}
179+
180+static int
181+bsd_read(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
182+{
183+ unsigned char sector[512];
184+ if (blockdev_read(dev, base_sec + 1, sector, 1) < 0)
185+ return -1;
186+ memcpy(lp, sector, sizeof(*lp));
187+ if (lp->d_magic != 0x82564557U || lp->d_magic2 != 0x82564557U)
188+ return -1;
189+ return 0;
190+}
191+
192+static int
193+bsd_write(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
194+{
195+ unsigned char sector[512];
196+ memset(sector, 0, sizeof(sector));
197+ lp->d_checksum = 0;
198+ lp->d_checksum = bsd_dkcksum(lp, lp->d_npartitions);
199+ memcpy(sector, lp, sizeof(*lp));
200+ if (blockdev_write(dev, base_sec + 1, sector, 1) < 0)
201+ return -1;
202+ return 0;
203+}
204+
205+static void
206+bsd_print(struct BsdDisklabel *lp, const char *path, uint64_t base_sec)
207+{
208+ uint64_t start, size;
209+ int i;
210+
211+ printf("Disk: %s (at sector %llu)\n", path, (unsigned long long)base_sec);
212+ printf("Partition table (BSD disklabel):\n");
213+ printf("%-5s %-12s %-12s %-10s\n", "Index", "Start", "Size", "Type");
214+ for (i = 0; i < lp->d_npartitions; i++) {
215+ struct BsdPartition *p = &lp->d_partitions[i];
216+ start = ((uint64_t)p->offset_h << 32) | p->offset;
217+ size = ((uint64_t)p->size_h << 32) | p->size;
218+ if (size == 0)
219+ continue;
220+ printf("%-5c %-12llu %-12llu 0x%02x\n",
221+ 'a' + i,
222+ (unsigned long long)start,
223+ (unsigned long long)size,
224+ p->fstype);
225+ }
226+}
227+
228+static int
229+bsd_find_label(struct BlockDev *dev, uint64_t *base_sec, struct BsdDisklabel *lp)
230+{
231+ struct MbrHeader mbr;
232+ int i;
233+
234+ if (bsd_read(dev, 0, lp) == 0) {
235+ *base_sec = 0;
236+ return 0;
237+ }
238+
239+ if (blockdev_read(dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
240+ for (i = 0; i < 4; i++) {
241+ if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
242+ if (bsd_read(dev, mbr.parts[i].start_lba, lp) == 0) {
243+ *base_sec = mbr.parts[i].start_lba;
244+ return 0;
245+ }
246+ }
247+ }
248+ }
249+
250+ return -1;
251+}
252+
253 static int
254 do_print(const char *path)
255 {
256@@ -227,17 +362,36 @@ do_print(const char *path)
257 struct MbrHeader mbr;
258 struct GptHeader gpt_hdr;
259 struct GptPartition gpt_parts[128];
260+ struct BsdDisklabel bsd_lp;
261+ int i;
262+ int found_bsd = 0;
263
264 if (blockdev_open(&dev, path, 0) < 0) {
265 weprintf("cannot open %s:", path);
266 return -1;
267 }
268
269- if (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0) {
270- print_gpt(&gpt_hdr, gpt_parts, path);
271+ if (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0) {
272+ gpt_print(&gpt_hdr, gpt_parts, path);
273+ } else if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
274+ for (i = 0; i < 4; i++) {
275+ if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
276+ if (bsd_read(&dev, mbr.parts[i].start_lba, &bsd_lp) == 0) {
277+ bsd_print(&bsd_lp, path, mbr.parts[i].start_lba);
278+ found_bsd = 1;
279+ }
280+ }
281+ }
282+ if (!found_bsd) {
283+ if (bsd_read(&dev, 0, &bsd_lp) == 0) {
284+ bsd_print(&bsd_lp, path, 0);
285+ } else {
286+ mbr_print(&mbr, path);
287+ }
288+ }
289 } else {
290- if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
291- print_mbr(&mbr, path);
292+ if (bsd_read(&dev, 0, &bsd_lp) == 0) {
293+ bsd_print(&bsd_lp, path, 0);
294 } else {
295 printf("Disk %s: unpartitioned or unknown format\n", path);
296 }
297@@ -254,14 +408,25 @@ do_fdisk(const char *path)
298 struct MbrHeader mbr;
299 struct GptHeader gpt_hdr;
300 struct GptPartition gpt_parts[128];
301+ struct BsdDisklabel lp;
302+ struct BsdDisklabel bsd_lp;
303+ struct GptPartition *gp;
304+ struct BsdPartition *bp;
305+ struct MbrPartition *mp;
306+ uint64_t base_sec;
307+ uint64_t limit;
308+ uint64_t bsd_base;
309+ uint64_t size;
310+ int idx;
311 int has_gpt = 0;
312+ int has_bsd = 0;
313
314 if (blockdev_open(&dev, path, 1) < 0) {
315 weprintf("cannot open %s:", path);
316 return -1;
317 }
318
319- has_gpt = (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0);
320+ has_gpt = (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0);
321
322 if (opt_init_mbr) {
323 memset(&mbr, 0, sizeof(mbr));
324@@ -291,7 +456,7 @@ do_fdisk(const char *path)
325 gpt_hdr.part_size = 128;
326 memset(gpt_parts, 0, sizeof(gpt_parts));
327
328- if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
329+ if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
330 weprintf("cannot write GPT to %s:", path);
331 blockdev_close(&dev);
332 return -1;
333@@ -301,6 +466,42 @@ do_fdisk(const char *path)
334 return 0;
335 }
336
337+ if (opt_init_bsd) {
338+ base_sec = 0;
339+ limit = dev.size / dev.sec_size;
340+
341+ if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
342+ if (!opt_part_is_letter && opt_part_idx >= 1 && opt_part_idx <= 4) {
343+ mp = &mbr.parts[opt_part_idx - 1];
344+ if (mp->size > 0) {
345+ base_sec = mp->start_lba;
346+ limit = base_sec + mp->size;
347+ }
348+ }
349+ }
350+
351+ memset(&lp, 0, sizeof(lp));
352+ lp.d_magic = 0x82564557U;
353+ lp.d_magic2 = 0x82564557U;
354+ lp.d_secsize = dev.sec_size;
355+ lp.d_npartitions = 8;
356+ lp.d_partitions[2].offset = (uint32_t)base_sec;
357+ lp.d_partitions[2].offset_h = (uint16_t)(base_sec >> 32);
358+ size = limit - base_sec;
359+ lp.d_partitions[2].size = (uint32_t)size;
360+ lp.d_partitions[2].size_h = (uint16_t)(size >> 32);
361+ lp.d_partitions[2].fstype = 0;
362+
363+ if (bsd_write(&dev, base_sec, &lp) < 0) {
364+ weprintf("cannot write BSD disklabel to %s:", path);
365+ blockdev_close(&dev);
366+ return -1;
367+ }
368+ printf("Initialized BSD disklabel on %s (at sector %llu)\n", path, (unsigned long long)base_sec);
369+ blockdev_close(&dev);
370+ return 0;
371+ }
372+
373 if (opt_add) {
374 if (opt_part_idx < 1) {
375 weprintf("add requires partition index (-n)\n");
376@@ -314,46 +515,76 @@ do_fdisk(const char *path)
377 blockdev_close(&dev);
378 return -1;
379 }
380- struct GptPartition *p = &gpt_parts[opt_part_idx - 1];
381- p->start_lba = opt_start;
382- p->end_lba = opt_end;
383- /* default type guid: linux filesystem data */
384- p->type_guid[0] = 0xAF; p->type_guid[1] = 0x3D; p->type_guid[2] = 0xC6; p->type_guid[3] = 0x0F;
385- p->type_guid[4] = 0x83; p->type_guid[5] = 0x84;
386- p->type_guid[6] = 0x72; p->type_guid[7] = 0x47;
387- p->type_guid[8] = 0x8E; p->type_guid[9] = 0x79;
388- p->type_guid[10] = 0x3D; p->type_guid[11] = 0x69; p->type_guid[12] = 0xD8; p->type_guid[13] = 0x47; p->type_guid[14] = 0x7D; p->type_guid[15] = 0xE4;
389-
390- if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
391+ gp = &gpt_parts[opt_part_idx - 1];
392+ gp->start_lba = opt_start;
393+ gp->end_lba = opt_end;
394+ gp->type_guid[0] = 0xAF; gp->type_guid[1] = 0x3D; gp->type_guid[2] = 0xC6; gp->type_guid[3] = 0x0F;
395+ gp->type_guid[4] = 0x83; gp->type_guid[5] = 0x84;
396+ gp->type_guid[6] = 0x72; gp->type_guid[7] = 0x47;
397+ gp->type_guid[8] = 0x8E; gp->type_guid[9] = 0x79;
398+ gp->type_guid[10] = 0x3D; gp->type_guid[11] = 0x69; gp->type_guid[12] = 0xD8; gp->type_guid[13] = 0x47; gp->type_guid[14] = 0x7D; gp->type_guid[15] = 0xE4;
399+
400+ if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
401 weprintf("cannot update GPT on %s:", path);
402 blockdev_close(&dev);
403 return -1;
404 }
405 printf("Added GPT partition %d to %s\n", opt_part_idx, path);
406 } else {
407- if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
408- weprintf("cannot read MBR from %s:", path);
409- blockdev_close(&dev);
410- return -1;
411- }
412- if (opt_part_idx > 4) {
413- weprintf("MBR partition index must be 1-4\n");
414- blockdev_close(&dev);
415- return -1;
416+ has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
417+ if (opt_part_is_letter || has_bsd) {
418+ if (!has_bsd) {
419+ weprintf("BSD disklabel not found on %s\n", path);
420+ blockdev_close(&dev);
421+ return -1;
422+ }
423+ idx = opt_part_idx - 1;
424+ if (idx >= 16) {
425+ weprintf("BSD partition index must be a-p\n");
426+ blockdev_close(&dev);
427+ return -1;
428+ }
429+ bp = &bsd_lp.d_partitions[idx];
430+ bp->offset = (uint32_t)opt_start;
431+ bp->offset_h = (uint16_t)(opt_start >> 32);
432+ size = opt_end - opt_start + 1;
433+ bp->size = (uint32_t)size;
434+ bp->size_h = (uint16_t)(size >> 32);
435+ bp->fstype = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 7;
436+ if (idx >= bsd_lp.d_npartitions)
437+ bsd_lp.d_npartitions = idx + 1;
438+
439+ if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
440+ weprintf("cannot update BSD disklabel on %s:", path);
441+ blockdev_close(&dev);
442+ return -1;
443+ }
444+ printf("Added BSD partition %c to %s\n", 'a' + idx, path);
445+ } else {
446+ if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
447+ weprintf("cannot read MBR from %s:", path);
448+ blockdev_close(&dev);
449+ return -1;
450+ }
451+ if (opt_part_idx > 4) {
452+ weprintf("MBR partition index must be 1-4\n");
453+ blockdev_close(&dev);
454+ return -1;
455+ }
456+ mp = &mbr.parts[opt_part_idx - 1];
457+ mp->start_lba = (uint32_t)opt_start;
458+ mp->size = (uint32_t)(opt_end - opt_start + 1);
459+ mp->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
460+ mbr.sig[0] = 0x55;
461+ mbr.sig[1] = 0xAA;
462+
463+ if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
464+ weprintf("cannot update MBR on %s:", path);
465+ blockdev_close(&dev);
466+ return -1;
467+ }
468+ printf("Added MBR partition %d to %s\n", opt_part_idx, path);
469 }
470- struct MbrPartition *p = &mbr.parts[opt_part_idx - 1];
471- p->start_lba = (uint32_t)opt_start;
472- p->size = (uint32_t)(opt_end - opt_start + 1);
473- p->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
474- mbr.sig[0] = 0x55;
475- mbr.sig[1] = 0xAA;
476-
477- if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
478- weprintf("cannot update MBR on %s:", path);
479- blockdev_close(&dev);
480- return -1;
481- }
482- printf("Added MBR partition %d to %s\n", opt_part_idx, path);
483 }
484 }
485
486@@ -371,30 +602,53 @@ do_fdisk(const char *path)
487 return -1;
488 }
489 memset(&gpt_parts[opt_part_idx - 1], 0, sizeof(struct GptPartition));
490- if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
491+ if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
492 weprintf("cannot update GPT on %s:", path);
493 blockdev_close(&dev);
494 return -1;
495 }
496 printf("Deleted GPT partition %d from %s\n", opt_part_idx, path);
497 } else {
498- if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
499- weprintf("cannot read MBR from %s:", path);
500- blockdev_close(&dev);
501- return -1;
502- }
503- if (opt_part_idx > 4) {
504- weprintf("MBR partition index must be 1-4\n");
505- blockdev_close(&dev);
506- return -1;
507- }
508- memset(&mbr.parts[opt_part_idx - 1], 0, sizeof(struct MbrPartition));
509- if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
510- weprintf("cannot update MBR on %s:", path);
511- blockdev_close(&dev);
512- return -1;
513+ has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
514+ if (opt_part_is_letter || has_bsd) {
515+ if (!has_bsd) {
516+ weprintf("BSD disklabel not found on %s\n", path);
517+ blockdev_close(&dev);
518+ return -1;
519+ }
520+ idx = opt_part_idx - 1;
521+ if (idx >= 16) {
522+ weprintf("BSD partition index must be a-p\n");
523+ blockdev_close(&dev);
524+ return -1;
525+ }
526+ memset(&bsd_lp.d_partitions[idx], 0, sizeof(struct BsdPartition));
527+ if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
528+ weprintf("cannot update BSD disklabel on %s:", path);
529+ blockdev_close(&dev);
530+ return -1;
531+ }
532+ printf("Deleted BSD partition %c from %s\n", 'a' + idx, path);
533+ } else {
534+ if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
535+ weprintf("cannot read MBR from %s:", path);
536+ blockdev_close(&dev);
537+ return -1;
538+ }
539+ if (opt_part_idx > 4) {
540+ weprintf("MBR partition index must be 1-4\n");
541+ blockdev_close(&dev);
542+ return -1;
543+ }
544+ mp = &mbr.parts[opt_part_idx - 1];
545+ memset(mp, 0, sizeof(struct MbrPartition));
546+ if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
547+ weprintf("cannot update MBR on %s:", path);
548+ blockdev_close(&dev);
549+ return -1;
550+ }
551+ printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
552 }
553- printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
554 }
555 }
556
557@@ -403,11 +657,13 @@ do_fdisk(const char *path)
558 }
559
560 // ?man fdisk: partition table manipulator
561-// ?man arguments: [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]
562-// ?man fdisk performs partition table operations for MBR and GPT disks
563+// ?man arguments: [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]
564+// ?man fdisk performs partition table operations for MBR, GPT and BSD disklabels
565 int
566 main(int argc, char *argv[])
567 {
568+ int ret = 0;
569+
570 ARGBEGIN {
571 // ?man -l:list partitions of all devices
572 case 'l':
573@@ -425,6 +681,10 @@ main(int argc, char *argv[])
574 case 'm':
575 opt_init_mbr = 1;
576 break;
577+ // ?man -s:initialize disk with BSD disklabel
578+ case 's':
579+ opt_init_bsd = 1;
580+ break;
581 // ?man -a:add partition
582 case 'a':
583 opt_add = 1;
584@@ -433,10 +693,18 @@ main(int argc, char *argv[])
585 case 'd':
586 opt_del = 1;
587 break;
588- // ?man -n:partition index
589- case 'n':
590- opt_part_idx = (int)estrtol(EARGF(usage()), 10);
591+ // ?man -n:partition index or letter
592+ case 'n': {
593+ char *arg = EARGF(usage());
594+ if (arg[0] >= 'a' && arg[0] <= 'p' && arg[1] == '\0') {
595+ opt_part_idx = arg[0] - 'a' + 1;
596+ opt_part_is_letter = 1;
597+ } else {
598+ opt_part_idx = (int)estrtol(arg, 10);
599+ opt_part_is_letter = 0;
600+ }
601 break;
602+ }
603 // ?man -b:starting sector LBA
604 case 'b':
605 opt_start = (uint64_t)estrtoul(EARGF(usage()), 10);
606@@ -445,7 +713,7 @@ main(int argc, char *argv[])
607 case 'e':
608 opt_end = (uint64_t)estrtoul(EARGF(usage()), 10);
609 break;
610- // ?man -t:partition type (MBR hex or GPT string)
611+ // ?man -t:partition type (MBR/BSD hex or GPT string)
612 case 't':
613 opt_type = EARGF(usage());
614 break;
615@@ -459,20 +727,24 @@ main(int argc, char *argv[])
616 if (!list)
617 return 1;
618 for (curr = list; curr; curr = curr->next) {
619- char devpath[128];
620- snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
621- do_print(devpath);
622+ do_print(curr->path);
623 }
624 blockdev_free_list(list);
625- return 0;
626+ if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
627+ ret = 2;
628+ return ret;
629 }
630
631 if (argc != 1)
632 usage();
633
634 if (opt_print) {
635- return do_print(argv[0]) < 0 ? 1 : 0;
636+ ret = do_print(argv[0]) < 0 ? 1 : 0;
637+ } else {
638+ ret = do_fdisk(argv[0]) < 0 ? 1 : 0;
639 }
640
641- return do_fdisk(argv[0]) < 0 ? 1 : 0;
642+ if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
643+ ret = 2;
644+ return ret;
645 }
+3,
-0
1@@ -78,6 +78,9 @@ main(int argc, char *argv[])
2 printf("%s\n", hdrbuf);
3
4 for (curr = list; curr; curr = curr->next) {
5+ if (strncmp(curr->name, "ram", 3) == 0)
6+ continue;
7+
8 print_line(max_len,
9 curr->name,
10 curr->major, curr->minor,
+2489,
-0
1@@ -0,0 +1,2489 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "util.h"
4+
5+#include <sys/stat.h>
6+#include <sys/wait.h>
7+
8+#include <ctype.h>
9+#include <dirent.h>
10+#include <err.h>
11+#include <errno.h>
12+#include <fcntl.h>
13+#include <fnmatch.h>
14+#include <getopt.h>
15+#include <limits.h>
16+#include <math.h>
17+#include <paths.h>
18+#include <poll.h>
19+#include <regex.h>
20+#include <signal.h>
21+#include <stdio.h>
22+#include <stdlib.h>
23+#include <string.h>
24+#include <stdint.h>
25+#include <time.h>
26+#include <unistd.h>
27+
28+#ifndef __dead
29+#define __dead __attribute__((__noreturn__))
30+#endif
31+
32+#define d_status d_type
33+
34+/* output format options */
35+#define D_NORMAL 0
36+#define D_EDIT -1
37+#define D_REVERSE 1
38+#define D_CONTEXT 2
39+#define D_UNIFIED 3
40+#define D_IFDEF 4
41+#define D_NREVERSE 5
42+#define D_BRIEF 6
43+#define D_GFORMAT 7
44+#define D_SIDEBYSIDE 8
45+
46+#define D_UNSET -2
47+
48+/* algorithms */
49+#define D_DIFFNONE 0
50+#define D_DIFFSTONE 1
51+#define D_DIFFMYERS 2
52+#define D_DIFFPATIENCE 3
53+
54+/* output flags */
55+#define D_HEADER 0x001
56+#define D_EMPTY1 0x002
57+#define D_EMPTY2 0x004
58+
59+/* command line flags */
60+#define D_FORCEASCII 0x008
61+#define D_FOLDBLANKS 0x010
62+#define D_MINIMAL 0x020
63+#define D_IGNORECASE 0x040
64+#define D_PROTOTYPE 0x080
65+#define D_EXPANDTABS 0x100
66+#define D_IGNOREBLANKS 0x200
67+#define D_STRIPCR 0x400
68+#define D_SKIPBLANKLINES 0x800
69+#define D_MATCHLAST 0x1000
70+
71+/* features supported by new algorithms */
72+#define D_NEWALGO_FLAGS (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS)
73+
74+/* status values for print_status and diffreg return values */
75+#define D_SAME 0
76+#define D_DIFFER 1
77+#define D_BINARY 2
78+#define D_MISMATCH1 3
79+#define D_MISMATCH2 4
80+#define D_SKIPPED1 5
81+#define D_SKIPPED2 6
82+#define D_ERROR 7
83+
84+/* color options */
85+#define COLORFLAG_NEVER 0
86+#define COLORFLAG_AUTO 1
87+#define COLORFLAG_ALWAYS 2
88+
89+#define FUNCTION_CONTEXT_SIZE 55
90+#define _PATH_PR "/usr/bin/pr"
91+
92+#ifndef roundup
93+#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
94+#endif
95+
96+struct Excludes {
97+ char *pattern;
98+ struct Excludes *next;
99+};
100+
101+struct Cand {
102+ int x;
103+ int y;
104+ int pred;
105+};
106+
107+struct Line {
108+ int serial;
109+ int value;
110+};
111+
112+struct ContextVec {
113+ int a;
114+ int b;
115+ int c;
116+ int d;
117+};
118+
119+struct Pr {
120+ int ostdout;
121+ pid_t cpid;
122+};
123+
124+struct Algorithm {
125+ const char *name;
126+ int id;
127+};
128+
129+static struct Algorithm algorithms[] = {
130+ {"stone", D_DIFFSTONE},
131+ {"myers", D_DIFFMYERS},
132+ {"patience", D_DIFFPATIENCE},
133+ {NULL, D_DIFFNONE}
134+};
135+
136+/* options */
137+#define OPTIONS "0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
138+enum {
139+ OPT_TSIZE = CHAR_MAX + 1,
140+ OPT_STRIPCR,
141+ OPT_IGN_FN_CASE,
142+ OPT_NO_IGN_FN_CASE,
143+ OPT_NORMAL,
144+ OPT_HELP,
145+ OPT_HORIZON_LINES,
146+ OPT_CHANGED_GROUP_FORMAT,
147+ OPT_SUPPRESS_COMMON,
148+ OPT_COLOR,
149+ OPT_NO_DEREFERENCE,
150+ OPT_VERSION,
151+};
152+
153+static struct option longopts[] = {
154+ { "algorithm", required_argument, 0, 'A' },
155+ { "text", no_argument, 0, 'a' },
156+ { "ignore-space-change", no_argument, 0, 'b' },
157+ { "context", optional_argument, 0, 'C' },
158+ { "ifdef", required_argument, 0, 'D' },
159+ { "minimal", no_argument, 0, 'd' },
160+ { "ed", no_argument, 0, 'e' },
161+ { "forward-ed", no_argument, 0, 'f' },
162+ { "show-function-line", required_argument, 0, 'F' },
163+ { "speed-large-files", no_argument, NULL, 'H' },
164+ { "ignore-blank-lines", no_argument, 0, 'B' },
165+ { "ignore-matching-lines", required_argument, 0, 'I' },
166+ { "ignore-case", no_argument, 0, 'i' },
167+ { "paginate", no_argument, NULL, 'l' },
168+ { "label", required_argument, 0, 'L' },
169+ { "new-file", no_argument, 0, 'N' },
170+ { "rcs", no_argument, 0, 'n' },
171+ { "unidirectional-new-file", no_argument, 0, 'P' },
172+ { "show-c-function", no_argument, 0, 'p' },
173+ { "brief", no_argument, 0, 'q' },
174+ { "recursive", no_argument, 0, 'r' },
175+ { "report-identical-files", no_argument, 0, 's' },
176+ { "starting-file", required_argument, 0, 'S' },
177+ { "expand-tabs", no_argument, 0, 't' },
178+ { "initial-tab", no_argument, 0, 'T' },
179+ { "unified", optional_argument, 0, 'U' },
180+ { "ignore-all-space", no_argument, 0, 'w' },
181+ { "width", required_argument, 0, 'W' },
182+ { "exclude", required_argument, 0, 'x' },
183+ { "exclude-from", required_argument, 0, 'X' },
184+ { "side-by-side", no_argument, NULL, 'y' },
185+ { "ignore-file-name-case", no_argument, NULL, OPT_IGN_FN_CASE },
186+ { "help", no_argument, NULL, OPT_HELP},
187+ { "horizon-lines", required_argument, NULL, OPT_HORIZON_LINES },
188+ { "no-dereference", no_argument, NULL, OPT_NO_DEREFERENCE},
189+ { "no-ignore-file-name-case", no_argument, NULL, OPT_NO_IGN_FN_CASE },
190+ { "normal", no_argument, NULL, OPT_NORMAL },
191+ { "strip-trailing-cr", no_argument, NULL, OPT_STRIPCR },
192+ { "tabsize", required_argument, NULL, OPT_TSIZE },
193+ { "changed-group-format", required_argument, NULL, OPT_CHANGED_GROUP_FORMAT},
194+ { "suppress-common-lines", no_argument, NULL, OPT_SUPPRESS_COMMON },
195+ { "color", optional_argument, NULL, OPT_COLOR },
196+ { "version", no_argument, NULL, OPT_VERSION},
197+ { NULL, 0, 0, '\0'}
198+};
199+
200+static const char diff_version[] = "FreeBSD diff 20240307";
201+int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
202+int ignore_file_case, suppress_common, color, noderef;
203+static int help = 0;
204+int diff_format, diff_context, diff_algorithm, status;
205+int diff_algorithm_set;
206+int tabsize = 8, width = 130;
207+static int colorflag = COLORFLAG_NEVER;
208+char *start, *ifdefname, *diffargs, *label[2];
209+char *ignore_pats, *most_recent_pat;
210+char *group_format = NULL;
211+const char *add_code, *del_code;
212+struct stat stb1, stb2;
213+struct Excludes *excludes_list;
214+regex_t ignore_re, most_recent_re;
215+
216+enum Readhash {
217+ RH_BINARY,
218+ RH_OK,
219+ RH_EOF
220+};
221+
222+static struct Line *file[2];
223+
224+static int *J;
225+static int *class;
226+static int *klist;
227+static int *member;
228+static int clen;
229+static int inifdef;
230+static size_t len[2];
231+static size_t pref, suff;
232+static size_t slen[2];
233+static int anychange;
234+static int hw, lpad, rpad;
235+static int edoffset;
236+static long *ixnew;
237+static long *ixold;
238+static struct Cand *clist;
239+static int clistlen;
240+static struct Line *sfile[2];
241+static int (*chrtran)(int);
242+static struct ContextVec *context_vec_start;
243+static struct ContextVec *context_vec_end;
244+static struct ContextVec *context_vec_ptr;
245+static char lastbuf[FUNCTION_CONTEXT_SIZE];
246+static int lastline;
247+static int lastmatchline;
248+
249+static int sigpipe[2] = {-1, -1};
250+static struct pollfd poll_fd;
251+
252+static void checked_regcomp(char const *, regex_t *);
253+static void usage(void);
254+static void conflicting_format(void);
255+static void push_excludes(char *);
256+static void push_ignore_pats(char *);
257+static void read_excludes_file(char *);
258+static void set_argstr(char **, char **);
259+static char *diff_splice(char *, char *);
260+static int do_color(void);
261+static int cup2low(int);
262+static int clow2low(int);
263+static void xasprintf(char **, const char *, ...);
264+
265+int diffreg(char *, char *, int, int);
266+void diffdir(char *, char *, int);
267+void print_status(int, char *, char *, const char *);
268+
269+static int selectfile(const struct dirent *);
270+static void diffit(struct dirent *, char *, size_t, struct dirent *, char *, size_t, int);
271+static void print_only(const char *, size_t, const char *);
272+
273+static FILE *opentemp(const char *);
274+static void output(char *, FILE *, char *, FILE *, int);
275+static void check(FILE *, FILE *, int);
276+static void range(int, int, const char *);
277+static void uni_range(int, int);
278+static void dump_context_vec(FILE *, FILE *, int);
279+static void dump_unified_vec(FILE *, FILE *, int);
280+static int prepare(int, FILE *, size_t, int);
281+static void prune(void);
282+static void equiv(struct Line *, int, struct Line *, int, int *);
283+static void unravel(int);
284+static void unsort(struct Line *, int, int *);
285+static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
286+static void sort(struct Line *, int);
287+static void print_header(const char *, const char *);
288+static void print_space(int, int, int);
289+static int ignoreline_pattern(char *);
290+static int ignoreline(char *, int);
291+static int asciifile(FILE *);
292+static int fetch(long *, int, int, FILE *, int, int, int);
293+static int newcand(int, int, int);
294+static int search(int *, int, int);
295+static int skipline(FILE *);
296+static int stone(int *, int, int *, int *, int);
297+static enum Readhash readhash(FILE *, int, unsigned *);
298+static int files_differ(FILE *, FILE *, int);
299+static char *match_function(const long *, int, FILE *);
300+static char *preadline(int, size_t, off_t);
301+
302+static void handle_sig(int);
303+struct Pr *start_pr(char *, char *);
304+void stop_pr(struct Pr *);
305+
306+static void
307+handle_sig(int signo)
308+{
309+ write(sigpipe[1], &signo, sizeof(signo));
310+}
311+
312+struct Pr *
313+start_pr(char *file1, char *file2)
314+{
315+ int pfd[2];
316+ pid_t pid;
317+ char *header;
318+ struct Pr *pr;
319+
320+ pr = ecalloc(1, sizeof(*pr));
321+ xasprintf(&header, "%s %s %s", diffargs, file1, file2);
322+ signal(SIGPIPE, SIG_IGN);
323+ fflush(stdout);
324+ if (pipe(pfd) == -1)
325+ err(2, "pipe");
326+ if (sigpipe[0] < 0) {
327+ if (pipe(sigpipe) == -1)
328+ err(2, "pipe");
329+ if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC) == -1)
330+ err(2, "fcntl");
331+ if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC) == -1)
332+ err(2, "fcntl");
333+ if (signal(SIGCHLD, handle_sig) == SIG_ERR)
334+ err(2, "signal");
335+ poll_fd.fd = sigpipe[0];
336+ poll_fd.events = POLLIN;
337+ }
338+ poll_fd.revents = 0;
339+ switch ((pid = fork())) {
340+ case -1:
341+ status |= 2;
342+ free(header);
343+ err(2, "no more processes");
344+ case 0:
345+ if (pfd[0] != STDIN_FILENO) {
346+ dup2(pfd[0], STDIN_FILENO);
347+ close(pfd[0]);
348+ }
349+ close(pfd[1]);
350+ execl(_PATH_PR, _PATH_PR, "-h", header, (char *)0);
351+ _exit(127);
352+ default:
353+ if (pfd[1] != STDOUT_FILENO) {
354+ pr->ostdout = dup(STDOUT_FILENO);
355+ dup2(pfd[1], STDOUT_FILENO);
356+ close(pfd[1]);
357+ }
358+ close(pfd[0]);
359+ free(header);
360+ pr->cpid = pid;
361+ }
362+ return pr;
363+}
364+
365+void
366+stop_pr(struct Pr *pr)
367+{
368+ int wstatus;
369+ int done = 0;
370+
371+ if (!pr)
372+ return;
373+
374+ fflush(stdout);
375+ if (pr->ostdout != STDOUT_FILENO) {
376+ close(STDOUT_FILENO);
377+ dup2(pr->ostdout, STDOUT_FILENO);
378+ close(pr->ostdout);
379+ }
380+ while (!done) {
381+ pid_t wpid;
382+ int npe = poll(&poll_fd, 1, -1);
383+ if (npe == -1) {
384+ if (errno == EINTR)
385+ continue;
386+ err(2, "poll");
387+ }
388+ if (poll_fd.revents != POLLIN)
389+ continue;
390+ if (read(poll_fd.fd, &npe, sizeof(npe)) < 0)
391+ err(2, "read");
392+ while ((wpid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
393+ if (wpid != pr->cpid)
394+ continue;
395+ if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
396+ errx(2, "pr exited abnormally");
397+ else if (WIFSIGNALED(wstatus))
398+ errx(2, "pr killed by signal %d", WTERMSIG(wstatus));
399+ done = 1;
400+ break;
401+ }
402+ }
403+ free(pr);
404+}
405+
406+// ?man diff: differential file and directory comparator
407+// ?man arguments: file1 file2
408+// ?man synopsis: [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--rcs] [--show-c-function] [--starting-file] [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [-I pattern] [-F pattern] [-L label] file1 file2
409+int
410+main(int argc, char **argv)
411+{
412+ const char *errstr = NULL;
413+ char *ep, **oargv;
414+ long l;
415+ int ch, dflags, lastch, gotstdin, prevoptind, newarg;
416+
417+ oargv = argv;
418+ gotstdin = 0;
419+ dflags = 0;
420+ lastch = '\0';
421+ prevoptind = 1;
422+ newarg = 1;
423+ diff_context = 3;
424+ diff_format = D_UNSET;
425+ diff_algorithm = D_DIFFMYERS;
426+ diff_algorithm_set = 0;
427+#define FORMAT_MISMATCHED(type) \
428+ (diff_format != D_UNSET && diff_format != (type))
429+ while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
430+ // ?man -0: context length
431+ // ?man -1: context length
432+ // ?man -2: context length
433+ // ?man -3: context length
434+ // ?man -4: context length
435+ // ?man -5: context length
436+ // ?man -6: context length
437+ // ?man -7: context length
438+ // ?man -8: context length
439+ // ?man -9: context length
440+ switch (ch) {
441+ case '0': case '1': case '2': case '3': case '4':
442+ case '5': case '6': case '7': case '8': case '9':
443+ if (newarg)
444+ usage();
445+ else if (lastch == 'c' || lastch == 'u')
446+ diff_context = 0;
447+ else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
448+ usage();
449+ diff_context = (diff_context * 10) + (ch - '0');
450+ break;
451+ // ?man -A:algo: algorithm (stone, myers, patience)
452+ case 'A':
453+ diff_algorithm = D_DIFFNONE;
454+ for (struct Algorithm *a = algorithms; a->name; a++) {
455+ if (strcasecmp(optarg, a->name) == 0) {
456+ diff_algorithm = a->id;
457+ diff_algorithm_set = 1;
458+ break;
459+ }
460+ }
461+ if (diff_algorithm == D_DIFFNONE) {
462+ printf("unknown algorithm: %s\n", optarg);
463+ usage();
464+ }
465+ break;
466+ // ?man -a: force text mode
467+ case 'a':
468+ dflags |= D_FORCEASCII;
469+ break;
470+ // ?man -b: ignore space changes
471+ case 'b':
472+ dflags |= D_FOLDBLANKS;
473+ break;
474+ // ?man -C:lines: context format
475+ case 'C':
476+ // ?man -c: context format
477+ case 'c':
478+ if (FORMAT_MISMATCHED(D_CONTEXT))
479+ conflicting_format();
480+ cflag = 1;
481+ diff_format = D_CONTEXT;
482+ if (optarg != NULL) {
483+ l = strtol(optarg, &ep, 10);
484+ if (*ep != '\0' || l < 0 || l >= INT_MAX)
485+ usage();
486+ diff_context = (int)l;
487+ }
488+ break;
489+ // ?man -d: minimal diff search
490+ case 'd':
491+ dflags |= D_MINIMAL;
492+ break;
493+ // ?man -D:name: ifdef format
494+ case 'D':
495+ if (FORMAT_MISMATCHED(D_IFDEF))
496+ conflicting_format();
497+ diff_format = D_IFDEF;
498+ ifdefname = optarg;
499+ break;
500+ // ?man -e: ed script format
501+ case 'e':
502+ if (FORMAT_MISMATCHED(D_EDIT))
503+ conflicting_format();
504+ diff_format = D_EDIT;
505+ break;
506+ // ?man -f: forward ed script format
507+ case 'f':
508+ if (FORMAT_MISMATCHED(D_REVERSE))
509+ conflicting_format();
510+ diff_format = D_REVERSE;
511+ break;
512+ // ?man -H: speed up large files (stub)
513+ case 'H':
514+ break;
515+ // ?man -h: backward compatibility (stub)
516+ case 'h':
517+ break;
518+ // ?man -B: ignore blank lines
519+ case 'B':
520+ dflags |= D_SKIPBLANKLINES;
521+ break;
522+ // ?man -F:pat: show function matching pattern
523+ case 'F':
524+ if (dflags & D_PROTOTYPE)
525+ conflicting_format();
526+ dflags |= D_MATCHLAST;
527+ most_recent_pat = estrdup(optarg);
528+ break;
529+ // ?man -I:pat: ignore pattern matching lines
530+ case 'I':
531+ push_ignore_pats(optarg);
532+ break;
533+ // ?man -i: ignore case
534+ case 'i':
535+ dflags |= D_IGNORECASE;
536+ break;
537+ // ?man -L:label: custom label
538+ case 'L':
539+ if (label[0] == NULL)
540+ label[0] = optarg;
541+ else if (label[1] == NULL)
542+ label[1] = optarg;
543+ else
544+ usage();
545+ break;
546+ // ?man -l: paginate output using pr
547+ case 'l':
548+ lflag = 1;
549+ break;
550+ // ?man -N: treat missing files as empty
551+ case 'N':
552+ Nflag = 1;
553+ break;
554+ // ?man -n: rcs format
555+ case 'n':
556+ if (FORMAT_MISMATCHED(D_NREVERSE))
557+ conflicting_format();
558+ diff_format = D_NREVERSE;
559+ break;
560+ // ?man -p: show C prototype context
561+ case 'p':
562+ if (dflags & D_MATCHLAST)
563+ conflicting_format();
564+ dflags |= D_PROTOTYPE;
565+ break;
566+ // ?man -P: treat missing destination files as empty
567+ case 'P':
568+ Pflag = 1;
569+ break;
570+ // ?man -r: recursive directory comparison
571+ case 'r':
572+ rflag = 1;
573+ break;
574+ // ?man -q: brief output (differ / same only)
575+ case 'q':
576+ if (FORMAT_MISMATCHED(D_BRIEF))
577+ conflicting_format();
578+ diff_format = D_BRIEF;
579+ break;
580+ // ?man -S:name: starting file in directory comparison
581+ case 'S':
582+ start = optarg;
583+ break;
584+ // ?man -s: report identical files
585+ case 's':
586+ sflag = 1;
587+ break;
588+ // ?man -T: initial tab alignment
589+ case 'T':
590+ Tflag = 1;
591+ break;
592+ // ?man -t: expand tabs in output
593+ case 't':
594+ dflags |= D_EXPANDTABS;
595+ break;
596+ // ?man -U:lines: unified context format
597+ case 'U':
598+ // ?man -u: unified context format
599+ case 'u':
600+ if (FORMAT_MISMATCHED(D_UNIFIED))
601+ conflicting_format();
602+ diff_format = D_UNIFIED;
603+ if (optarg != NULL) {
604+ l = strtol(optarg, &ep, 10);
605+ if (*ep != '\0' || l < 0 || l >= INT_MAX)
606+ usage();
607+ diff_context = (int)l;
608+ }
609+ break;
610+ // ?man -w: ignore all whitespace
611+ case 'w':
612+ dflags |= D_IGNOREBLANKS;
613+ break;
614+ // ?man -W:cols: column width for side-by-side
615+ case 'W':
616+ width = (int)strtonum(optarg, 1, INT_MAX, &errstr);
617+ if (errstr) {
618+ warnx("invalid width argument");
619+ usage();
620+ }
621+ break;
622+ // ?man -X:file: exclude patterns file
623+ case 'X':
624+ read_excludes_file(optarg);
625+ break;
626+ // ?man -x:pat: exclude pattern
627+ case 'x':
628+ push_excludes(optarg);
629+ break;
630+ // ?man -y: side by side format
631+ case 'y':
632+ if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
633+ conflicting_format();
634+ diff_format = D_SIDEBYSIDE;
635+ break;
636+ case OPT_CHANGED_GROUP_FORMAT:
637+ if (FORMAT_MISMATCHED(D_GFORMAT))
638+ conflicting_format();
639+ diff_format = D_GFORMAT;
640+ group_format = optarg;
641+ break;
642+ case OPT_HELP:
643+ help = 1;
644+ usage();
645+ break;
646+ case OPT_HORIZON_LINES:
647+ break;
648+ case OPT_IGN_FN_CASE:
649+ ignore_file_case = 1;
650+ break;
651+ case OPT_NO_IGN_FN_CASE:
652+ ignore_file_case = 0;
653+ break;
654+ case OPT_NORMAL:
655+ if (FORMAT_MISMATCHED(D_NORMAL))
656+ conflicting_format();
657+ diff_format = D_NORMAL;
658+ break;
659+ case OPT_TSIZE:
660+ tabsize = (int)strtonum(optarg, 1, INT_MAX, &errstr);
661+ if (errstr) {
662+ warnx("invalid tabsize argument");
663+ usage();
664+ }
665+ break;
666+ case OPT_STRIPCR:
667+ dflags |= D_STRIPCR;
668+ break;
669+ case OPT_SUPPRESS_COMMON:
670+ suppress_common = 1;
671+ break;
672+ case OPT_COLOR:
673+ if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
674+ colorflag = COLORFLAG_AUTO;
675+ else if (strncmp(optarg, "always", 6) == 0)
676+ colorflag = COLORFLAG_ALWAYS;
677+ else if (strncmp(optarg, "never", 5) == 0)
678+ colorflag = COLORFLAG_NEVER;
679+ else
680+ errx(2, "unsupported color option %s", optarg);
681+ break;
682+ case OPT_NO_DEREFERENCE:
683+ noderef = 1;
684+ break;
685+ case OPT_VERSION:
686+ printf("%s\n", diff_version);
687+ exit(0);
688+ default:
689+ usage();
690+ break;
691+ }
692+ lastch = ch;
693+ newarg = optind != prevoptind;
694+ prevoptind = optind;
695+ }
696+ if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
697+ diff_format = D_CONTEXT;
698+ if (diff_format == D_UNSET)
699+ diff_format = D_NORMAL;
700+ argc -= optind;
701+ argv += optind;
702+
703+ if (do_color()) {
704+ char *p;
705+ const char *env;
706+
707+ color = 1;
708+ add_code = "32";
709+ del_code = "31";
710+ env = getenv("DIFFCOLORS");
711+ if (env != NULL && *env != '\0' && (p = estrdup(env))) {
712+ add_code = p;
713+ strsep(&p, ":");
714+ if (p != NULL)
715+ del_code = p;
716+ }
717+ }
718+
719+ if (argc != 2)
720+ usage();
721+ checked_regcomp(ignore_pats, &ignore_re);
722+ checked_regcomp(most_recent_pat, &most_recent_re);
723+ if (strcmp(argv[0], "-") == 0) {
724+ fstat(STDIN_FILENO, &stb1);
725+ gotstdin = 1;
726+ } else if (stat(argv[0], &stb1) != 0) {
727+ if (!Nflag || errno != ENOENT)
728+ err(2, "%s", argv[0]);
729+ dflags |= D_EMPTY1;
730+ memset(&stb1, 0, sizeof(struct stat));
731+ }
732+
733+ if (strcmp(argv[1], "-") == 0) {
734+ fstat(STDIN_FILENO, &stb2);
735+ gotstdin = 1;
736+ } else if (stat(argv[1], &stb2) != 0) {
737+ if (!Nflag || errno != ENOENT)
738+ err(2, "%s", argv[1]);
739+ dflags |= D_EMPTY2;
740+ memset(&stb2, 0, sizeof(stb2));
741+ stb2.st_mode = stb1.st_mode;
742+ }
743+
744+ if (dflags & D_EMPTY1 && dflags & D_EMPTY2){
745+ warn("%s", argv[0]);
746+ warn("%s", argv[1]);
747+ exit(2);
748+ }
749+
750+ if (stb1.st_mode == 0)
751+ stb1.st_mode = stb2.st_mode;
752+
753+ if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
754+ errx(2, "cant compare - to directory");
755+ set_argstr(oargv, argv);
756+ if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
757+ if (diff_format == D_IFDEF)
758+ errx(2, "-D option not supported with directories");
759+ diffdir(argv[0], argv[1], dflags);
760+ } else {
761+ if (S_ISDIR(stb1.st_mode)) {
762+ argv[0] = diff_splice(argv[0], argv[1]);
763+ if (stat(argv[0], &stb1) == -1)
764+ err(2, "%s", argv[0]);
765+ }
766+ if (S_ISDIR(stb2.st_mode)) {
767+ argv[1] = diff_splice(argv[1], argv[0]);
768+ if (stat(argv[1], &stb2) == -1)
769+ err(2, "%s", argv[1]);
770+ }
771+ print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0], argv[1], "");
772+ }
773+ if (fflush(stdout) != 0)
774+ err(2, "stdout");
775+ exit(status);
776+}
777+
778+static void
779+checked_regcomp(char const *pattern, regex_t *comp)
780+{
781+ char buf[BUFSIZ];
782+ int error;
783+
784+ if (pattern == NULL)
785+ return;
786+
787+ error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
788+ if (error != 0) {
789+ regerror(error, comp, buf, sizeof(buf));
790+ if (*pattern != '\0')
791+ errx(2, "%s: %s", pattern, buf);
792+ else
793+ errx(2, "%s", buf);
794+ }
795+}
796+
797+static void
798+set_argstr(char **av, char **ave)
799+{
800+ size_t argsize;
801+ char **ap;
802+
803+ argsize = strlen("diff") + 1;
804+ for (ap = av + 1; ap < ave; ap++) {
805+ if (strcmp(*ap, "--") != 0)
806+ argsize += 1 + strlen(*ap);
807+ }
808+ diffargs = emalloc(argsize);
809+ strlcpy(diffargs, "diff", argsize);
810+ for (ap = av + 1; ap < ave; ap++) {
811+ if (strcmp(*ap, "--") != 0) {
812+ strlcat(diffargs, " ", argsize);
813+ strlcat(diffargs, *ap, argsize);
814+ }
815+ }
816+}
817+
818+static void
819+read_excludes_file(char *file)
820+{
821+ FILE *fp;
822+ char *pattern = NULL;
823+ size_t blen = 0;
824+ ssize_t len;
825+
826+ if (strcmp(file, "-") == 0)
827+ fp = stdin;
828+ else if ((fp = fopen(file, "r")) == NULL)
829+ err(2, "%s", file);
830+ while ((len = getline(&pattern, &blen, fp)) >= 0) {
831+ if ((len > 0) && (pattern[len - 1] == '\n'))
832+ pattern[len - 1] = '\0';
833+ push_excludes(pattern);
834+ pattern = NULL;
835+ blen = 0;
836+ }
837+ free(pattern);
838+ if (strcmp(file, "-") != 0)
839+ fclose(fp);
840+}
841+
842+static void
843+push_excludes(char *pattern)
844+{
845+ struct Excludes *entry;
846+
847+ entry = emalloc(sizeof(*entry));
848+ entry->pattern = pattern;
849+ entry->next = excludes_list;
850+ excludes_list = entry;
851+}
852+
853+static void
854+push_ignore_pats(char *pattern)
855+{
856+ size_t len;
857+
858+ if (ignore_pats == NULL)
859+ ignore_pats = estrdup(pattern);
860+ else {
861+ len = strlen(ignore_pats) + strlen(pattern) + 2;
862+ ignore_pats = ereallocarray(ignore_pats, 1, len);
863+ strlcat(ignore_pats, "|", len);
864+ strlcat(ignore_pats, pattern, len);
865+ }
866+}
867+
868+void
869+print_status(int val, char *path1, char *path2, const char *entry)
870+{
871+ if (label[0] != NULL)
872+ path1 = label[0];
873+ if (label[1] != NULL)
874+ path2 = label[1];
875+
876+ switch (val) {
877+ case D_BINARY:
878+ printf("Binary files %s%s and %s%s differ\n", path1, entry, path2, entry);
879+ break;
880+ case D_DIFFER:
881+ if (diff_format == D_BRIEF)
882+ printf("Files %s%s and %s%s differ\n", path1, entry, path2, entry);
883+ break;
884+ case D_SAME:
885+ if (sflag)
886+ printf("Files %s%s and %s%s are identical\n", path1, entry, path2, entry);
887+ break;
888+ case D_MISMATCH1:
889+ printf("File %s%s is directory, %s%s is file\n", path1, entry, path2, entry);
890+ break;
891+ case D_MISMATCH2:
892+ printf("File %s%s is file, %s%s is directory\n", path1, entry, path2, entry);
893+ break;
894+ case D_SKIPPED1:
895+ printf("File %s%s is not regular file or directory\n", path1, entry);
896+ break;
897+ case D_SKIPPED2:
898+ printf("File %s%s is not regular file or directory\n", path2, entry);
899+ break;
900+ }
901+}
902+
903+static void
904+usage(void)
905+{
906+ fprintf(stderr, "usage: diff [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] "
907+ "[-I pattern] [-F pattern] [-L label] file1 file2\n");
908+ exit(2);
909+}
910+
911+static void
912+conflicting_format(void)
913+{
914+ errx(2, "conflicting output format options");
915+}
916+
917+static char *
918+diff_splice(char *dir, char *file)
919+{
920+ char *p, *res;
921+
922+ p = strrchr(file, '/');
923+ p = p ? p + 1 : file;
924+ xasprintf(&res, "%s/%s", dir, p);
925+ return res;
926+}
927+
928+static void
929+xasprintf(char **strp, const char *fmt, ...)
930+{
931+ va_list ap;
932+ int r;
933+
934+ va_start(ap, fmt);
935+ r = vasprintf(strp, fmt, ap);
936+ va_end(ap);
937+ if (r == -1)
938+ err(2, "asprintf");
939+}
940+
941+static int
942+cup2low(int c)
943+{
944+ return tolower(c);
945+}
946+
947+static int
948+clow2low(int c)
949+{
950+ return c;
951+}
952+
953+static int
954+do_color(void)
955+{
956+ if (colorflag == COLORFLAG_ALWAYS)
957+ return 1;
958+ if (colorflag == COLORFLAG_NEVER)
959+ return 0;
960+ if (isatty(STDOUT_FILENO)) {
961+ char *term = getenv("COLORTERM");
962+ if (term && *term != '\0')
963+ return 1;
964+ }
965+ return 0;
966+}
967+
968+void
969+diffdir(char *p1, char *p2, int flags)
970+{
971+ struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
972+ struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
973+ size_t dirlen1, dirlen2;
974+ char path1[PATH_MAX], path2[PATH_MAX];
975+ int pos;
976+
977+ edp1 = edp2 = NULL;
978+ dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
979+ if (dirlen1 >= sizeof(path1) - 1) {
980+ errno = ENAMETOOLONG;
981+ warn("%s", p1);
982+ status |= 2;
983+ return;
984+ }
985+ if (path1[dirlen1 - 1] != '/') {
986+ path1[dirlen1++] = '/';
987+ path1[dirlen1] = '\0';
988+ }
989+ dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
990+ if (dirlen2 >= sizeof(path2) - 1) {
991+ errno = ENAMETOOLONG;
992+ warn("%s", p2);
993+ status |= 2;
994+ return;
995+ }
996+ if (path2[dirlen2 - 1] != '/') {
997+ path2[dirlen2++] = '/';
998+ path2[dirlen2] = '\0';
999+ }
1000+
1001+ pos = scandir(path1, &dirp1, selectfile, alphasort);
1002+ if (pos == -1) {
1003+ if (errno == ENOENT && (Nflag || Pflag))
1004+ pos = 0;
1005+ else {
1006+ warn("%s", path1);
1007+ goto closem;
1008+ }
1009+ }
1010+ dp1 = dirp1;
1011+ edp1 = dirp1 + pos;
1012+
1013+ pos = scandir(path2, &dirp2, selectfile, alphasort);
1014+ if (pos == -1) {
1015+ if (errno == ENOENT && Nflag)
1016+ pos = 0;
1017+ else {
1018+ warn("%s", path2);
1019+ goto closem;
1020+ }
1021+ }
1022+ dp2 = dirp2;
1023+ edp2 = dirp2 + pos;
1024+
1025+ if (start != NULL) {
1026+ while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
1027+ dp1++;
1028+ while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
1029+ dp2++;
1030+ }
1031+
1032+ while (dp1 != edp1 || dp2 != edp2) {
1033+ dent1 = dp1 != edp1 ? *dp1 : NULL;
1034+ dent2 = dp2 != edp2 ? *dp2 : NULL;
1035+
1036+ pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
1037+ ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name) :
1038+ strcmp(dent1->d_name, dent2->d_name);
1039+ if (pos == 0) {
1040+ diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1041+ dp1++;
1042+ dp2++;
1043+ } else if (pos < 0) {
1044+ if (Nflag)
1045+ diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1046+ else {
1047+ print_only(path1, dirlen1, dent1->d_name);
1048+ status |= 1;
1049+ }
1050+ dp1++;
1051+ } else {
1052+ if (Nflag || Pflag)
1053+ diffit(dent2, path1, dirlen1, dent1, path2, dirlen2, flags);
1054+ else {
1055+ print_only(path2, dirlen2, dent2->d_name);
1056+ status |= 1;
1057+ }
1058+ dp2++;
1059+ }
1060+ }
1061+
1062+closem:
1063+ if (dirp1 != NULL) {
1064+ for (dp1 = dirp1; dp1 < edp1; dp1++)
1065+ free(*dp1);
1066+ free(dirp1);
1067+ }
1068+ if (dirp2 != NULL) {
1069+ for (dp2 = dirp2; dp2 < edp2; dp2++)
1070+ free(*dp2);
1071+ free(dirp2);
1072+ }
1073+}
1074+
1075+static void
1076+diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
1077+ char *path2, size_t plen2, int flags)
1078+{
1079+ flags |= D_HEADER;
1080+ strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
1081+
1082+ if (ignore_file_case && strcasecmp(dp->d_name, dp2->d_name) == 0)
1083+ strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
1084+ else
1085+ strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
1086+
1087+ if (noderef) {
1088+ if (lstat(path1, &stb1) != 0) {
1089+ if (!(Nflag || Pflag) || errno != ENOENT) {
1090+ warn("%s", path1);
1091+ return;
1092+ }
1093+ flags |= D_EMPTY1;
1094+ memset(&stb1, 0, sizeof(stb1));
1095+ }
1096+
1097+ if (lstat(path2, &stb2) != 0) {
1098+ if (!Nflag || errno != ENOENT) {
1099+ warn("%s", path2);
1100+ return;
1101+ }
1102+ flags |= D_EMPTY2;
1103+ memset(&stb2, 0, sizeof(stb2));
1104+ stb2.st_mode = stb1.st_mode;
1105+ }
1106+ if (stb1.st_mode == 0)
1107+ stb1.st_mode = stb2.st_mode;
1108+ if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
1109+ if (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
1110+ char buf1[PATH_MAX];
1111+ char buf2[PATH_MAX];
1112+ ssize_t len1;
1113+ ssize_t len2;
1114+
1115+ len1 = readlink(path1, buf1, sizeof(buf1));
1116+ len2 = readlink(path2, buf2, sizeof(buf2));
1117+ if (len1 < 0 || len2 < 0) {
1118+ perror("reading links");
1119+ return;
1120+ }
1121+ buf1[len1] = '\0';
1122+ buf2[len2] = '\0';
1123+ if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
1124+ printf("Symbolic links %s and %s differ\n", path1, path2);
1125+ status |= 1;
1126+ }
1127+ return;
1128+ }
1129+ printf("File %s is a %s while file %s is a %s\n",
1130+ path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
1131+ S_ISDIR(stb1.st_mode) ? "directory" : "file",
1132+ path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
1133+ S_ISDIR(stb2.st_mode) ? "directory" : "file");
1134+ status |= 1;
1135+ return;
1136+ }
1137+ } else {
1138+ if (stat(path1, &stb1) != 0) {
1139+ if (!(Nflag || Pflag) || errno != ENOENT) {
1140+ warn("%s", path1);
1141+ return;
1142+ }
1143+ flags |= D_EMPTY1;
1144+ memset(&stb1, 0, sizeof(stb1));
1145+ }
1146+
1147+ if (stat(path2, &stb2) != 0) {
1148+ if (!Nflag || errno != ENOENT) {
1149+ warn("%s", path2);
1150+ return;
1151+ }
1152+ flags |= D_EMPTY2;
1153+ memset(&stb2, 0, sizeof(stb2));
1154+ stb2.st_mode = stb1.st_mode;
1155+ }
1156+ if (stb1.st_mode == 0)
1157+ stb1.st_mode = stb2.st_mode;
1158+ }
1159+ if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1160+ if (rflag)
1161+ diffdir(path1, path2, flags);
1162+ else
1163+ printf("Common subdirectories: %s and %s\n", path1, path2);
1164+ return;
1165+ }
1166+ if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1167+ dp->d_status = D_SKIPPED1;
1168+ else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1169+ dp->d_status = D_SKIPPED2;
1170+ else
1171+ dp->d_status = diffreg(path1, path2, flags, 0);
1172+ print_status(dp->d_status, path1, path2, "");
1173+}
1174+
1175+static int
1176+selectfile(const struct dirent *dp)
1177+{
1178+ struct Excludes *excl;
1179+
1180+ if (dp->d_fileno == 0)
1181+ return 0;
1182+
1183+ if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
1184+ (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
1185+ return 0;
1186+
1187+ for (excl = excludes_list; excl != NULL; excl = excl->next)
1188+ if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
1189+ return 0;
1190+
1191+ return 1;
1192+}
1193+
1194+void
1195+print_only(const char *path, size_t dirlen, const char *entry)
1196+{
1197+ if (dirlen > 1)
1198+ dirlen--;
1199+ printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
1200+}
1201+
1202+int
1203+diffreg(char *file1, char *file2, int flags, int capsicum)
1204+{
1205+ FILE *f1, *f2;
1206+ int i, rval;
1207+ struct Pr *pr = NULL;
1208+
1209+ f1 = f2 = NULL;
1210+ rval = D_SAME;
1211+ anychange = 0;
1212+ lastline = 0;
1213+ lastmatchline = 0;
1214+
1215+ if (diff_format == D_SIDEBYSIDE) {
1216+ if (flags & D_EXPANDTABS) {
1217+ if (width > 3)
1218+ hw = (width - 3) / 2;
1219+ else
1220+ hw = 0;
1221+ } else if (width <= 3 || width <= tabsize) {
1222+ hw = 0;
1223+ } else {
1224+ hw = (width - 3) / 2;
1225+ while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
1226+ hw--;
1227+ if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
1228+ width = roundup(hw + 3, tabsize) + hw;
1229+ }
1230+ lpad = (width - hw * 2 - 1) / 2;
1231+ rpad = (width - hw * 2 - 1) - lpad;
1232+ }
1233+
1234+ if (flags & D_IGNORECASE)
1235+ chrtran = cup2low;
1236+ else
1237+ chrtran = clow2low;
1238+ if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
1239+ return S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2;
1240+ if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
1241+ goto closem;
1242+
1243+ if (flags & D_EMPTY1)
1244+ f1 = fopen(_PATH_DEVNULL, "r");
1245+ else {
1246+ if (!S_ISREG(stb1.st_mode)) {
1247+ if ((f1 = opentemp(file1)) == NULL || fstat(fileno(f1), &stb1) == -1) {
1248+ warn("%s", file1);
1249+ rval = D_ERROR;
1250+ status |= 2;
1251+ goto closem;
1252+ }
1253+ } else if (strcmp(file1, "-") == 0)
1254+ f1 = stdin;
1255+ else
1256+ f1 = fopen(file1, "r");
1257+ }
1258+ if (f1 == NULL) {
1259+ warn("%s", file1);
1260+ rval = D_ERROR;
1261+ status |= 2;
1262+ goto closem;
1263+ }
1264+
1265+ if (flags & D_EMPTY2)
1266+ f2 = fopen(_PATH_DEVNULL, "r");
1267+ else {
1268+ if (!S_ISREG(stb2.st_mode)) {
1269+ if ((f2 = opentemp(file2)) == NULL || fstat(fileno(f2), &stb2) == -1) {
1270+ warn("%s", file2);
1271+ rval = D_ERROR;
1272+ status |= 2;
1273+ goto closem;
1274+ }
1275+ } else if (strcmp(file2, "-") == 0)
1276+ f2 = stdin;
1277+ else
1278+ f2 = fopen(file2, "r");
1279+ }
1280+ if (f2 == NULL) {
1281+ warn("%s", file2);
1282+ rval = D_ERROR;
1283+ status |= 2;
1284+ goto closem;
1285+ }
1286+
1287+ if (lflag)
1288+ pr = start_pr(file1, file2);
1289+
1290+ (void)capsicum;
1291+
1292+ switch (files_differ(f1, f2, flags)) {
1293+ case 0:
1294+ goto closem;
1295+ case 1:
1296+ break;
1297+ default:
1298+ rval = D_ERROR;
1299+ status |= 2;
1300+ goto closem;
1301+ }
1302+
1303+ if (diff_format == D_BRIEF && ignore_pats == NULL &&
1304+ (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|D_SKIPBLANKLINES|D_STRIPCR)) == 0)
1305+ {
1306+ rval = D_DIFFER;
1307+ status |= 1;
1308+ goto closem;
1309+ }
1310+ if ((flags & D_FORCEASCII) != 0) {
1311+ prepare(0, f1, stb1.st_size, flags);
1312+ prepare(1, f2, stb2.st_size, flags);
1313+ } else if (!asciifile(f1) || !asciifile(f2) ||
1314+ !prepare(0, f1, stb1.st_size, flags) ||
1315+ !prepare(1, f2, stb2.st_size, flags)) {
1316+ rval = D_BINARY;
1317+ status |= 1;
1318+ goto closem;
1319+ }
1320+ if (len[0] > INT_MAX - 2)
1321+ errno = EFBIG, err(1, "%s", file1);
1322+ if (len[1] > INT_MAX - 2)
1323+ errno = EFBIG, err(1, "%s", file2);
1324+
1325+ prune();
1326+ sort(sfile[0], slen[0]);
1327+ sort(sfile[1], slen[1]);
1328+
1329+ member = (int *)file[1];
1330+ equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1331+ member = ereallocarray(member, slen[1] + 2, sizeof(*member));
1332+
1333+ class = (int *)file[0];
1334+ unsort(sfile[0], slen[0], class);
1335+ class = ereallocarray(class, slen[0] + 2, sizeof(*class));
1336+
1337+ klist = ecalloc(slen[0] + 2, sizeof(*klist));
1338+ clen = 0;
1339+ clistlen = 100;
1340+ clist = ecalloc(clistlen, sizeof(*clist));
1341+ i = stone(class, slen[0], member, klist, flags);
1342+ free(member);
1343+ free(class);
1344+
1345+ J = ereallocarray(J, len[0] + 2, sizeof(*J));
1346+ unravel(klist[i]);
1347+ free(clist);
1348+ free(klist);
1349+
1350+ ixold = ereallocarray(ixold, len[0] + 2, sizeof(*ixold));
1351+ ixnew = ereallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
1352+ check(f1, f2, flags);
1353+ output(file1, f1, file2, f2, flags);
1354+
1355+closem:
1356+ if (pr != NULL)
1357+ stop_pr(pr);
1358+ if (anychange) {
1359+ status |= 1;
1360+ if (rval == D_SAME)
1361+ rval = D_DIFFER;
1362+ }
1363+ if (f1 != NULL && f1 != stdin)
1364+ fclose(f1);
1365+ if (f2 != NULL && f2 != stdin)
1366+ fclose(f2);
1367+
1368+ return rval;
1369+}
1370+
1371+static int
1372+files_differ(FILE *f1, FILE *f2, int flags)
1373+{
1374+ char buf1[BUFSIZ], buf2[BUFSIZ];
1375+ size_t i, j;
1376+
1377+ if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
1378+ (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
1379+ return 1;
1380+
1381+ if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
1382+ return 0;
1383+
1384+ for (;;) {
1385+ i = fread(buf1, 1, sizeof(buf1), f1);
1386+ j = fread(buf2, 1, sizeof(buf2), f2);
1387+ if ((!i && ferror(f1)) || (!j && ferror(f2)))
1388+ return -1;
1389+ if (i != j)
1390+ return 1;
1391+ if (i == 0)
1392+ return 0;
1393+ if (memcmp(buf1, buf2, i) != 0)
1394+ return 1;
1395+ }
1396+}
1397+
1398+static FILE *
1399+opentemp(const char *f)
1400+{
1401+ char buf[BUFSIZ], tempfile[PATH_MAX];
1402+ ssize_t nread;
1403+ int ifd, ofd;
1404+
1405+ if (strcmp(f, "-") == 0)
1406+ ifd = STDIN_FILENO;
1407+ else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
1408+ return NULL;
1409+
1410+ strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
1411+ if ((ofd = mkstemp(tempfile)) == -1) {
1412+ close(ifd);
1413+ return NULL;
1414+ }
1415+ unlink(tempfile);
1416+ while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
1417+ if (write(ofd, buf, nread) != nread) {
1418+ close(ifd);
1419+ close(ofd);
1420+ return NULL;
1421+ }
1422+ }
1423+ close(ifd);
1424+ lseek(ofd, (off_t)0, SEEK_SET);
1425+ return fdopen(ofd, "r");
1426+}
1427+
1428+static int
1429+prepare(int i, FILE *fd, size_t filesize, int flags)
1430+{
1431+ struct Line *p;
1432+ unsigned h;
1433+ size_t sz, j = 0;
1434+ enum Readhash r;
1435+
1436+ rewind(fd);
1437+ sz = filesize / 25;
1438+ if (sz < 100)
1439+ sz = 100;
1440+
1441+ p = ecalloc(sz + 3, sizeof(*p));
1442+ while ((r = readhash(fd, flags, &h)) != RH_EOF) {
1443+ if (r == RH_BINARY)
1444+ return 0;
1445+ if (j == SIZE_MAX)
1446+ break;
1447+ if (j == sz) {
1448+ sz = sz * 3 / 2;
1449+ p = ereallocarray(p, sz + 3, sizeof(*p));
1450+ }
1451+ p[++j].value = h;
1452+ }
1453+ len[i] = j;
1454+ file[i] = p;
1455+ return 1;
1456+}
1457+
1458+static void
1459+prune(void)
1460+{
1461+ size_t i, j;
1462+
1463+ for (pref = 0; pref < len[0] && pref < len[1] &&
1464+ file[0][pref + 1].value == file[1][pref + 1].value;
1465+ pref++)
1466+ ;
1467+ for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
1468+ file[0][len[0] - suff].value == file[1][len[1] - suff].value;
1469+ suff++)
1470+ ;
1471+ for (j = 0; j < 2; j++) {
1472+ sfile[j] = file[j] + pref;
1473+ slen[j] = len[j] - pref - suff;
1474+ for (i = 0; i <= slen[j]; i++)
1475+ sfile[j][i].serial = i;
1476+ }
1477+}
1478+
1479+static void
1480+equiv(struct Line *a, int n, struct Line *b, int m, int *c)
1481+{
1482+ int i, j;
1483+
1484+ i = j = 1;
1485+ while (i <= n && j <= m) {
1486+ if (a[i].value < b[j].value)
1487+ a[i++].value = 0;
1488+ else if (a[i].value == b[j].value)
1489+ a[i++].value = j;
1490+ else
1491+ j++;
1492+ }
1493+ while (i <= n)
1494+ a[i++].value = 0;
1495+ b[m + 1].value = 0;
1496+ j = 0;
1497+ while (++j <= m) {
1498+ c[j] = -b[j].serial;
1499+ while (b[j + 1].value == b[j].value) {
1500+ j++;
1501+ c[j] = b[j].serial;
1502+ }
1503+ }
1504+ c[j] = -1;
1505+}
1506+
1507+static int
1508+stone(int *a, int n, int *b, int *c, int flags)
1509+{
1510+ int i, k, y, j, l;
1511+ int oldc, tc, oldl, sq;
1512+ unsigned numtries, bound;
1513+
1514+ if (flags & D_MINIMAL)
1515+ bound = UINT_MAX;
1516+ else {
1517+ sq = sqrt(n);
1518+ bound = MAX(256, sq);
1519+ }
1520+
1521+ k = 0;
1522+ c[0] = newcand(0, 0, 0);
1523+ for (i = 1; i <= n; i++) {
1524+ j = a[i];
1525+ if (j == 0)
1526+ continue;
1527+ y = -b[j];
1528+ oldl = 0;
1529+ oldc = c[0];
1530+ numtries = 0;
1531+ do {
1532+ if (y <= clist[oldc].y)
1533+ continue;
1534+ l = search(c, k, y);
1535+ if (l != oldl + 1)
1536+ oldc = c[l - 1];
1537+ if (l <= k) {
1538+ if (clist[c[l]].y <= y)
1539+ continue;
1540+ tc = c[l];
1541+ c[l] = newcand(i, y, oldc);
1542+ oldc = tc;
1543+ oldl = l;
1544+ numtries++;
1545+ } else {
1546+ c[l] = newcand(i, y, oldc);
1547+ k++;
1548+ break;
1549+ }
1550+ } while ((y = b[++j]) > 0 && numtries < bound);
1551+ }
1552+ return k;
1553+}
1554+
1555+static int
1556+newcand(int x, int y, int pred)
1557+{
1558+ struct Cand *q;
1559+
1560+ if (clen == clistlen) {
1561+ clistlen = clistlen * 11 / 10;
1562+ clist = ereallocarray(clist, clistlen, sizeof(*clist));
1563+ }
1564+ q = clist + clen;
1565+ q->x = x;
1566+ q->y = y;
1567+ q->pred = pred;
1568+ return clen++;
1569+}
1570+
1571+static int
1572+search(int *c, int k, int y)
1573+{
1574+ int i, j, l, t;
1575+
1576+ if (clist[c[k]].y < y)
1577+ return k + 1;
1578+ i = 0;
1579+ j = k + 1;
1580+ for (;;) {
1581+ l = (i + j) / 2;
1582+ if (l <= i)
1583+ break;
1584+ t = clist[c[l]].y;
1585+ if (t > y)
1586+ j = l;
1587+ else if (t < y)
1588+ i = l;
1589+ else
1590+ return l;
1591+ }
1592+ return l + 1;
1593+}
1594+
1595+static void
1596+unravel(int p)
1597+{
1598+ struct Cand *q;
1599+ size_t i;
1600+
1601+ for (i = 0; i <= len[0]; i++)
1602+ J[i] = i <= pref ? i :
1603+ i > len[0] - suff ? i + len[1] - len[0] : 0;
1604+ for (q = clist + p; q->y != 0; q = clist + q->pred)
1605+ J[q->x + pref] = q->y + pref;
1606+}
1607+
1608+static void
1609+check(FILE *f1, FILE *f2, int flags)
1610+{
1611+ int i, j, c, d;
1612+ long ctold, ctnew;
1613+
1614+ rewind(f1);
1615+ rewind(f2);
1616+ j = 1;
1617+ ixold[0] = ixnew[0] = 0;
1618+ ctold = ctnew = 0;
1619+ for (i = 1; i <= (int)len[0]; i++) {
1620+ if (J[i] == 0) {
1621+ ixold[i] = ctold += skipline(f1);
1622+ continue;
1623+ }
1624+ while (j < J[i]) {
1625+ ixnew[j] = ctnew += skipline(f2);
1626+ j++;
1627+ }
1628+ if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) {
1629+ for (;;) {
1630+ c = getc(f1);
1631+ d = getc(f2);
1632+ if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) {
1633+ if (c == EOF && isspace(d)) {
1634+ ctnew++;
1635+ break;
1636+ } else if (isspace(c) && d == EOF) {
1637+ ctold++;
1638+ break;
1639+ }
1640+ }
1641+ ctold++;
1642+ ctnew++;
1643+ if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
1644+ if (c == '\r') {
1645+ if ((c = getc(f1)) == '\n')
1646+ ctold++;
1647+ else
1648+ ungetc(c, f1);
1649+ }
1650+ if (d == '\r') {
1651+ if ((d = getc(f2)) == '\n')
1652+ ctnew++;
1653+ else
1654+ ungetc(d, f2);
1655+ }
1656+ break;
1657+ }
1658+ if ((flags & D_FOLDBLANKS) && isspace(c) && isspace(d)) {
1659+ do {
1660+ if (c == '\n')
1661+ break;
1662+ ctold++;
1663+ } while (isspace(c = getc(f1)));
1664+ do {
1665+ if (d == '\n')
1666+ break;
1667+ ctnew++;
1668+ } while (isspace(d = getc(f2)));
1669+ } else if (flags & D_IGNOREBLANKS) {
1670+ while (isspace(c) && c != '\n') {
1671+ c = getc(f1);
1672+ ctold++;
1673+ }
1674+ while (isspace(d) && d != '\n') {
1675+ d = getc(f2);
1676+ ctnew++;
1677+ }
1678+ }
1679+ if (chrtran(c) != chrtran(d)) {
1680+ J[i] = 0;
1681+ if (c != '\n' && c != EOF)
1682+ ctold += skipline(f1);
1683+ if (d != '\n' && c != EOF)
1684+ ctnew += skipline(f2);
1685+ break;
1686+ }
1687+ if (c == '\n' || c == EOF)
1688+ break;
1689+ }
1690+ } else {
1691+ for (;;) {
1692+ ctold++;
1693+ ctnew++;
1694+ if ((c = getc(f1)) != (d = getc(f2))) {
1695+ J[i] = 0;
1696+ if (c != '\n' && c != EOF)
1697+ ctold += skipline(f1);
1698+ if (d != '\n' && c != EOF)
1699+ ctnew += skipline(f2);
1700+ break;
1701+ }
1702+ if (c == '\n' || c == EOF)
1703+ break;
1704+ }
1705+ }
1706+ ixold[i] = ctold;
1707+ ixnew[j] = ctnew;
1708+ j++;
1709+ }
1710+ for (; j <= (int)len[1]; j++) {
1711+ ixnew[j] = ctnew += skipline(f2);
1712+ }
1713+}
1714+
1715+static void
1716+sort(struct Line *a, int n)
1717+{
1718+ struct Line *ai, *aim, w;
1719+ int j, m = 0, k;
1720+
1721+ if (n == 0)
1722+ return;
1723+ for (j = 1; j <= n; j *= 2)
1724+ m = 2 * j - 1;
1725+ for (m /= 2; m != 0; m /= 2) {
1726+ k = n - m;
1727+ for (j = 1; j <= k; j++) {
1728+ for (ai = &a[j]; ai > a; ai -= m) {
1729+ aim = &ai[m];
1730+ if (aim < ai)
1731+ break;
1732+ if (aim->value > ai[0].value ||
1733+ (aim->value == ai[0].value && aim->serial > ai[0].serial))
1734+ break;
1735+ w.value = ai[0].value;
1736+ ai[0].value = aim->value;
1737+ aim->value = w.value;
1738+ w.serial = ai[0].serial;
1739+ ai[0].serial = aim->serial;
1740+ aim->serial = w.serial;
1741+ }
1742+ }
1743+ }
1744+}
1745+
1746+static void
1747+unsort(struct Line *f, int l, int *b)
1748+{
1749+ int *a, i;
1750+
1751+ a = ecalloc(l + 1, sizeof(*a));
1752+ for (i = 1; i <= l; i++)
1753+ a[f[i].serial] = f[i].value;
1754+ for (i = 1; i <= l; i++)
1755+ b[i] = a[i];
1756+ free(a);
1757+}
1758+
1759+static int
1760+skipline(FILE *f)
1761+{
1762+ int i, c;
1763+
1764+ for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
1765+ continue;
1766+ return i;
1767+}
1768+
1769+static void
1770+output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
1771+{
1772+ int i, j, m, i0, i1, j0, j1, nc;
1773+
1774+ rewind(f1);
1775+ rewind(f2);
1776+ m = len[0];
1777+ J[0] = 0;
1778+ J[m + 1] = len[1] + 1;
1779+ if (diff_format != D_EDIT) {
1780+ for (i0 = 1; i0 <= m; i0 = i1 + 1) {
1781+ while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
1782+ if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
1783+ nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
1784+ print_space(nc, hw - nc + lpad + 1 + rpad, flags);
1785+ fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
1786+ printf("\n");
1787+ }
1788+ i0++;
1789+ }
1790+ j0 = J[i0 - 1] + 1;
1791+ i1 = i0 - 1;
1792+ while (i1 < m && J[i1 + 1] == 0)
1793+ i1++;
1794+ j1 = J[i1 + 1] - 1;
1795+ J[i1] = j1;
1796+
1797+ if (diff_format == D_SIDEBYSIDE) {
1798+ for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++)
1799+ change(file1, f1, file2, f2, i, i, j, j, &flags);
1800+ while (i <= i1) {
1801+ change(file1, f1, file2, f2, i, i, j + 1, j, &flags);
1802+ i++;
1803+ }
1804+ while (j <= j1) {
1805+ change(file1, f1, file2, f2, i + 1, i, j, j, &flags);
1806+ j++;
1807+ }
1808+ } else
1809+ change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
1810+ }
1811+ } else {
1812+ for (i0 = m; i0 >= 1; i0 = i1 - 1) {
1813+ while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
1814+ i0--;
1815+ j0 = J[i0 + 1] - 1;
1816+ i1 = i0 + 1;
1817+ while (i1 > 1 && J[i1 - 1] == 0)
1818+ i1--;
1819+ j1 = J[i1 - 1] + 1;
1820+ J[i1] = j1;
1821+ change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
1822+ }
1823+ }
1824+ if (m == 0)
1825+ change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
1826+ if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
1827+ for (;;) {
1828+#define c i0
1829+ if ((c = getc(f1)) == EOF)
1830+ return;
1831+ printf("%c", c);
1832+ }
1833+#undef c
1834+ }
1835+ if (anychange != 0) {
1836+ if (diff_format == D_CONTEXT)
1837+ dump_context_vec(f1, f2, flags);
1838+ else if (diff_format == D_UNIFIED)
1839+ dump_unified_vec(f1, f2, flags);
1840+ }
1841+}
1842+
1843+static void
1844+range(int a, int b, const char *separator)
1845+{
1846+ printf("%d", a > b ? b : a);
1847+ if (a < b)
1848+ printf("%s%d", separator, b);
1849+}
1850+
1851+static void
1852+uni_range(int a, int b)
1853+{
1854+ if (a < b)
1855+ printf("%d,%d", a, b - a + 1);
1856+ else if (a == b)
1857+ printf("%d", b);
1858+ else
1859+ printf("%d,0", b);
1860+}
1861+
1862+static char *
1863+preadline(int fd, size_t rlen, off_t off)
1864+{
1865+ char *line;
1866+ ssize_t nr;
1867+
1868+ line = emalloc(rlen + 1);
1869+ if ((nr = pread(fd, line, rlen, off)) == -1)
1870+ err(2, "preadline");
1871+ if (nr > 0 && line[nr-1] == '\n')
1872+ nr--;
1873+ line[nr] = '\0';
1874+ return line;
1875+}
1876+
1877+static int
1878+ignoreline_pattern(char *line)
1879+{
1880+ int ret;
1881+
1882+ ret = regexec(&ignore_re, line, 0, NULL, 0);
1883+ return ret == 0;
1884+}
1885+
1886+static int
1887+ignoreline(char *line, int skip_blanks)
1888+{
1889+ if (skip_blanks && *line == '\0')
1890+ return 1;
1891+ if (ignore_pats != NULL && ignoreline_pattern(line))
1892+ return 1;
1893+ return 0;
1894+}
1895+
1896+static void
1897+change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
1898+ int *pflags)
1899+{
1900+ static size_t max_context = 64;
1901+ long curpos;
1902+ int i, nc;
1903+ const char *walk;
1904+ int skip_blanks, ignore;
1905+
1906+ skip_blanks = (*pflags & D_SKIPBLANKLINES);
1907+restart:
1908+ if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) && a > b && c > d)
1909+ return;
1910+ if (ignore_pats != NULL || skip_blanks) {
1911+ char *line;
1912+ if (a <= b) {
1913+ for (i = a; i <= b; i++) {
1914+ line = preadline(fileno(f1), ixold[i] - ixold[i - 1], ixold[i - 1]);
1915+ ignore = ignoreline(line, skip_blanks);
1916+ free(line);
1917+ if (!ignore)
1918+ goto proceed;
1919+ }
1920+ }
1921+ if (a > b || c <= d) {
1922+ for (i = c; i <= d; i++) {
1923+ line = preadline(fileno(f2), ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1924+ ignore = ignoreline(line, skip_blanks);
1925+ free(line);
1926+ if (!ignore)
1927+ goto proceed;
1928+ }
1929+ }
1930+ return;
1931+ }
1932+proceed:
1933+ if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1934+ printf("%s %s %s\n", diffargs, file1, file2);
1935+ *pflags &= ~D_HEADER;
1936+ }
1937+ if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1938+ if (context_vec_start == NULL || context_vec_ptr == context_vec_end - 1) {
1939+ ptrdiff_t offset = -1;
1940+
1941+ if (context_vec_start != NULL)
1942+ offset = context_vec_ptr - context_vec_start;
1943+ max_context <<= 1;
1944+ context_vec_start = ereallocarray(context_vec_start, max_context, sizeof(*context_vec_start));
1945+ context_vec_end = context_vec_start + max_context;
1946+ context_vec_ptr = context_vec_start + offset;
1947+ }
1948+ if (anychange == 0) {
1949+ print_header(file1, file2);
1950+ anychange = 1;
1951+ } else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
1952+ c > context_vec_ptr->d + (2 * diff_context) + 1) {
1953+ if (diff_format == D_CONTEXT)
1954+ dump_context_vec(f1, f2, *pflags);
1955+ else
1956+ dump_unified_vec(f1, f2, *pflags);
1957+ }
1958+ context_vec_ptr++;
1959+ context_vec_ptr->a = a;
1960+ context_vec_ptr->b = b;
1961+ context_vec_ptr->c = c;
1962+ context_vec_ptr->d = d;
1963+ return;
1964+ }
1965+ if (anychange == 0)
1966+ anychange = 1;
1967+ switch (diff_format) {
1968+ case D_BRIEF:
1969+ return;
1970+ case D_NORMAL:
1971+ case D_EDIT:
1972+ range(a, b, ",");
1973+ printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1974+ if (diff_format == D_NORMAL)
1975+ range(c, d, ",");
1976+ printf("\n");
1977+ break;
1978+ case D_REVERSE:
1979+ printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1980+ range(a, b, " ");
1981+ printf("\n");
1982+ break;
1983+ case D_NREVERSE:
1984+ if (a > b)
1985+ printf("a%d %d\n", b, d - c + 1);
1986+ else {
1987+ printf("d%d %d\n", a, b - a + 1);
1988+ if (!(c > d))
1989+ printf("a%d %d\n", b, d - c + 1);
1990+ }
1991+ break;
1992+ }
1993+ if (diff_format == D_GFORMAT) {
1994+ curpos = ftell(f1);
1995+ nc = ixold[a > b ? b : a - 1] - curpos;
1996+ for (i = 0; i < nc; i++)
1997+ printf("%c", getc(f1));
1998+ for (walk = group_format; *walk != '\0'; walk++) {
1999+ if (*walk == '%') {
2000+ walk++;
2001+ switch (*walk) {
2002+ case '<':
2003+ fetch(ixold, a, b, f1, '<', 1, *pflags);
2004+ break;
2005+ case '>':
2006+ fetch(ixnew, c, d, f2, '>', 0, *pflags);
2007+ break;
2008+ default:
2009+ printf("%%%c", *walk);
2010+ break;
2011+ }
2012+ continue;
2013+ }
2014+ printf("%c", *walk);
2015+ }
2016+ }
2017+ if (diff_format == D_SIDEBYSIDE) {
2018+ if (color && a > b)
2019+ printf("\033[%sm", add_code);
2020+ else if (color && c > d)
2021+ printf("\033[%sm", del_code);
2022+ if (a > b) {
2023+ print_space(0, hw + lpad, *pflags);
2024+ } else {
2025+ nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
2026+ print_space(nc, hw - nc + lpad, *pflags);
2027+ }
2028+ if (color && a > b)
2029+ printf("\033[%sm", add_code);
2030+ else if (color && c > d)
2031+ printf("\033[%sm", del_code);
2032+ printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
2033+ if (color && c > d)
2034+ printf("\033[m");
2035+ print_space(hw + lpad + 1, rpad, *pflags);
2036+ fetch(ixnew, c, d, f2, '\0', 0, *pflags);
2037+ printf("\n");
2038+ }
2039+ if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
2040+ fetch(ixold, a, b, f1, '<', 1, *pflags);
2041+ if (a <= b && c <= d && diff_format == D_NORMAL)
2042+ printf("---\n");
2043+ }
2044+ if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
2045+ fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
2046+ if (edoffset != 0 && diff_format == D_EDIT) {
2047+ printf(".\n");
2048+ printf("%ds/.//\n", a + edoffset - 1);
2049+ b = a + edoffset - 1;
2050+ a = b + 1;
2051+ c += edoffset;
2052+ goto restart;
2053+ }
2054+ if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
2055+ printf(".\n");
2056+ if (inifdef) {
2057+ printf("#endif /* %s */\n", ifdefname);
2058+ inifdef = 0;
2059+ }
2060+}
2061+
2062+static int
2063+fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
2064+{
2065+ int i, j, c, lastc, col, nc, newcol;
2066+
2067+ edoffset = 0;
2068+ nc = 0;
2069+ col = 0;
2070+ if ((diff_format == D_IFDEF) && oldfile) {
2071+ long curpos = ftell(lb);
2072+ nc = f[a > b ? b : a - 1] - curpos;
2073+ for (i = 0; i < nc; i++)
2074+ printf("%c", getc(lb));
2075+ }
2076+ if (a > b)
2077+ return 0;
2078+ if (diff_format == D_IFDEF) {
2079+ if (inifdef) {
2080+ printf("#else /* %s%s */\n",
2081+ oldfile == 1 ? "!" : "", ifdefname);
2082+ } else {
2083+ if (oldfile)
2084+ printf("#ifndef %s\n", ifdefname);
2085+ else
2086+ printf("#ifdef %s\n", ifdefname);
2087+ }
2088+ inifdef = 1 + oldfile;
2089+ }
2090+ for (i = a; i <= b; i++) {
2091+ fseek(lb, f[i - 1], SEEK_SET);
2092+ nc = f[i] - f[i - 1];
2093+ if (diff_format == D_SIDEBYSIDE && hw < nc)
2094+ nc = hw;
2095+ if (diff_format != D_IFDEF && diff_format != D_GFORMAT && ch != '\0') {
2096+ if (color && (ch == '>' || ch == '+'))
2097+ printf("\033[%sm", add_code);
2098+ else if (color && (ch == '<' || ch == '-'))
2099+ printf("\033[%sm", del_code);
2100+ printf("%c", ch);
2101+ if (Tflag && (diff_format == D_NORMAL ||
2102+ diff_format == D_CONTEXT ||
2103+ diff_format == D_UNIFIED))
2104+ printf("\t");
2105+ else if (diff_format != D_UNIFIED)
2106+ printf(" ");
2107+ }
2108+ col = j = 0;
2109+ lastc = '\0';
2110+ while (j < nc && (hw == 0 || col < hw)) {
2111+ c = getc(lb);
2112+ if (flags & D_STRIPCR && c == '\r') {
2113+ if ((c = getc(lb)) == '\n')
2114+ j++;
2115+ else {
2116+ ungetc(c, lb);
2117+ c = '\r';
2118+ }
2119+ }
2120+ if (c == EOF) {
2121+ if (diff_format == D_EDIT ||
2122+ diff_format == D_REVERSE ||
2123+ diff_format == D_NREVERSE)
2124+ warnx("No newline at end of file");
2125+ else
2126+ printf("\n\\ No newline at end of file\n");
2127+ return col;
2128+ }
2129+ if (c == '\t') {
2130+ newcol = roundup(col + 1, tabsize);
2131+ if ((flags & D_EXPANDTABS) == 0) {
2132+ if (hw > 0 && newcol >= hw)
2133+ return col;
2134+ printf("\t");
2135+ } else {
2136+ if (hw > 0 && newcol > hw)
2137+ newcol = hw;
2138+ printf("%*s", newcol - col, "");
2139+ }
2140+ col = newcol;
2141+ } else {
2142+ if (diff_format == D_EDIT && j == 1 && c == '\n' && lastc == '.') {
2143+ printf(".\n");
2144+ edoffset = i - a + 1;
2145+ return edoffset;
2146+ }
2147+ if (diff_format != D_SIDEBYSIDE || c != '\n') {
2148+ if (color && c == '\n')
2149+ printf("\033[m%c", c);
2150+ else
2151+ printf("%c", c);
2152+ col++;
2153+ }
2154+ }
2155+ j++;
2156+ lastc = c;
2157+ }
2158+ }
2159+ if (color && diff_format == D_SIDEBYSIDE)
2160+ printf("\033[m");
2161+ return col;
2162+}
2163+
2164+static enum Readhash
2165+readhash(FILE *f, int flags, unsigned *hash)
2166+{
2167+ int i, t, space;
2168+ unsigned sum;
2169+
2170+ sum = 1;
2171+ space = 0;
2172+ for (i = 0;;) {
2173+ switch (t = getc(f)) {
2174+ case '\0':
2175+ if ((flags & D_FORCEASCII) == 0)
2176+ return RH_BINARY;
2177+ goto hashchar;
2178+ case '\r':
2179+ if (flags & D_STRIPCR) {
2180+ t = getc(f);
2181+ if (t == '\n')
2182+ break;
2183+ ungetc(t, f);
2184+ }
2185+ /* FALLTHROUGH */
2186+ case '\t':
2187+ case '\v':
2188+ case '\f':
2189+ case ' ':
2190+ if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) {
2191+ space++;
2192+ continue;
2193+ }
2194+ /* FALLTHROUGH */
2195+ default:
2196+ hashchar:
2197+ if (space && (flags & D_IGNOREBLANKS) == 0) {
2198+ i++;
2199+ space = 0;
2200+ }
2201+ sum = sum * 127 + chrtran(t);
2202+ i++;
2203+ continue;
2204+ case EOF:
2205+ if (i == 0)
2206+ return RH_EOF;
2207+ /* FALLTHROUGH */
2208+ case '\n':
2209+ break;
2210+ }
2211+ break;
2212+ }
2213+ *hash = sum;
2214+ return RH_OK;
2215+}
2216+
2217+static int
2218+asciifile(FILE *f)
2219+{
2220+ unsigned char buf[BUFSIZ];
2221+ size_t cnt;
2222+
2223+ if (f == NULL)
2224+ return 1;
2225+
2226+ rewind(f);
2227+ cnt = fread(buf, 1, sizeof(buf), f);
2228+ return memchr(buf, '\0', cnt) == NULL;
2229+}
2230+
2231+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0)
2232+
2233+static char *
2234+match_function(const long *f, int pos, FILE *fp)
2235+{
2236+ char buf[FUNCTION_CONTEXT_SIZE];
2237+ size_t nc;
2238+ int last = lastline;
2239+ const char *state = NULL;
2240+
2241+ lastline = pos;
2242+ for (; pos > last; pos--) {
2243+ fseek(fp, f[pos - 1], SEEK_SET);
2244+ nc = f[pos] - f[pos - 1];
2245+ if (nc >= sizeof(buf))
2246+ nc = sizeof(buf) - 1;
2247+ nc = fread(buf, 1, nc, fp);
2248+ if (nc == 0)
2249+ continue;
2250+ buf[nc] = '\0';
2251+ buf[strcspn(buf, "\n")] = '\0';
2252+ if (most_recent_pat != NULL) {
2253+ int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
2254+ if (ret != 0)
2255+ continue;
2256+ strlcpy(lastbuf, buf, sizeof(lastbuf));
2257+ lastmatchline = pos;
2258+ return lastbuf;
2259+ } else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$' || buf[0] == '-' || buf[0] == '+') {
2260+ if (begins_with(buf, "private:")) {
2261+ if (!state)
2262+ state = " (private)";
2263+ } else if (begins_with(buf, "protected:")) {
2264+ if (!state)
2265+ state = " (protected)";
2266+ } else if (begins_with(buf, "public:")) {
2267+ if (!state)
2268+ state = " (public)";
2269+ } else {
2270+ strlcpy(lastbuf, buf, sizeof(lastbuf));
2271+ if (state)
2272+ strlcat(lastbuf, state, sizeof(lastbuf));
2273+ lastmatchline = pos;
2274+ return lastbuf;
2275+ }
2276+ }
2277+ }
2278+ return lastmatchline > 0 ? lastbuf : NULL;
2279+}
2280+
2281+static void
2282+dump_context_vec(FILE *f1, FILE *f2, int flags)
2283+{
2284+ struct ContextVec *cvp = context_vec_start;
2285+ int lowa, upb, lowc, upd, do_output;
2286+ int a, b, c, d;
2287+ char ch, *f;
2288+
2289+ if (context_vec_start > context_vec_ptr)
2290+ return;
2291+
2292+ b = d = 0;
2293+ lowa = MAX(1, cvp->a - diff_context);
2294+ upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2295+ lowc = MAX(1, cvp->c - diff_context);
2296+ upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2297+
2298+ printf("***************");
2299+ if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2300+ f = match_function(ixold, cvp->a - 1, f1);
2301+ if (f != NULL)
2302+ printf(" %s", f);
2303+ }
2304+ printf("\n*** ");
2305+ range(lowa, upb, ",");
2306+ printf(" ****\n");
2307+
2308+ do_output = 0;
2309+ for (; cvp <= context_vec_ptr; cvp++)
2310+ if (cvp->a <= cvp->b) {
2311+ cvp = context_vec_start;
2312+ do_output++;
2313+ break;
2314+ }
2315+ if (do_output) {
2316+ while (cvp <= context_vec_ptr) {
2317+ a = cvp->a;
2318+ b = cvp->b;
2319+ c = cvp->c;
2320+ d = cvp->d;
2321+
2322+ if (a <= b && c <= d)
2323+ ch = 'c';
2324+ else
2325+ ch = (a <= b) ? 'd' : 'a';
2326+
2327+ if (ch == 'a')
2328+ fetch(ixold, lowa, b, f1, ' ', 0, flags);
2329+ else {
2330+ fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2331+ fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-', 0, flags);
2332+ }
2333+ lowa = b + 1;
2334+ cvp++;
2335+ }
2336+ fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
2337+ }
2338+
2339+ printf("--- ");
2340+ range(lowc, upd, ",");
2341+ printf(" ----\n");
2342+
2343+ do_output = 0;
2344+ for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
2345+ if (cvp->c <= cvp->d) {
2346+ cvp = context_vec_start;
2347+ do_output++;
2348+ break;
2349+ }
2350+ if (do_output) {
2351+ while (cvp <= context_vec_ptr) {
2352+ a = cvp->a;
2353+ b = cvp->b;
2354+ c = cvp->c;
2355+ d = cvp->d;
2356+
2357+ if (a <= b && c <= d)
2358+ ch = 'c';
2359+ else
2360+ ch = (a <= b) ? 'd' : 'a';
2361+
2362+ if (ch == 'd')
2363+ fetch(ixnew, lowc, d, f2, ' ', 0, flags);
2364+ else {
2365+ fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2366+ fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+', 0, flags);
2367+ }
2368+ lowc = d + 1;
2369+ cvp++;
2370+ }
2371+ fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2372+ }
2373+ context_vec_ptr = context_vec_start - 1;
2374+}
2375+
2376+static void
2377+dump_unified_vec(FILE *f1, FILE *f2, int flags)
2378+{
2379+ struct ContextVec *cvp = context_vec_start;
2380+ int lowa, upb, lowc, upd;
2381+ int a, b, c, d;
2382+ char ch, *f;
2383+
2384+ if (context_vec_start > context_vec_ptr)
2385+ return;
2386+
2387+ b = d = 0;
2388+ lowa = MAX(1, cvp->a - diff_context);
2389+ upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2390+ lowc = MAX(1, cvp->c - diff_context);
2391+ upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2392+
2393+ printf("@@ -");
2394+ uni_range(lowa, upb);
2395+ printf(" +");
2396+ uni_range(lowc, upd);
2397+ printf(" @@");
2398+ if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2399+ f = match_function(ixold, cvp->a - 1, f1);
2400+ if (f != NULL)
2401+ printf(" %s", f);
2402+ }
2403+ printf("\n");
2404+
2405+ for (; cvp <= context_vec_ptr; cvp++) {
2406+ a = cvp->a;
2407+ b = cvp->b;
2408+ c = cvp->c;
2409+ d = cvp->d;
2410+
2411+ if (a <= b && c <= d)
2412+ ch = 'c';
2413+ else
2414+ ch = (a <= b) ? 'd' : 'a';
2415+
2416+ switch (ch) {
2417+ case 'c':
2418+ fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2419+ fetch(ixold, a, b, f1, '-', 0, flags);
2420+ fetch(ixnew, c, d, f2, '+', 0, flags);
2421+ break;
2422+ case 'd':
2423+ fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2424+ fetch(ixold, a, b, f1, '-', 0, flags);
2425+ break;
2426+ case 'a':
2427+ fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2428+ fetch(ixnew, c, d, f2, '+', 0, flags);
2429+ break;
2430+ }
2431+ lowa = b + 1;
2432+ lowc = d + 1;
2433+ }
2434+ fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2435+ context_vec_ptr = context_vec_start - 1;
2436+}
2437+
2438+static void
2439+print_header(const char *file1, const char *file2)
2440+{
2441+ const char *time_format;
2442+ char buf[256];
2443+ struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
2444+ int nsec1 = stb1.st_mtim.tv_nsec;
2445+ int nsec2 = stb2.st_mtim.tv_nsec;
2446+
2447+ time_format = "%Y-%m-%d %H:%M:%S";
2448+ if (cflag)
2449+ time_format = "%c";
2450+ tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
2451+ tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
2452+ if (label[0] != NULL)
2453+ printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---", label[0]);
2454+ else {
2455+ strftime(buf, sizeof(buf), time_format, tm_ptr1);
2456+ printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---", file1, buf);
2457+ if (!cflag) {
2458+ strftime(buf, sizeof(buf), "%z", tm_ptr1);
2459+ printf(".%.9d %s", nsec1, buf);
2460+ }
2461+ printf("\n");
2462+ }
2463+ if (label[1] != NULL)
2464+ printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++", label[1]);
2465+ else {
2466+ strftime(buf, sizeof(buf), time_format, tm_ptr2);
2467+ printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++", file2, buf);
2468+ if (!cflag) {
2469+ strftime(buf, sizeof(buf), "%z", tm_ptr2);
2470+ printf(".%.9d %s", nsec2, buf);
2471+ }
2472+ printf("\n");
2473+ }
2474+}
2475+
2476+static void
2477+print_space(int nc, int n, int flags)
2478+{
2479+ int col, newcol, tabstop;
2480+
2481+ col = nc;
2482+ newcol = nc + n;
2483+ if ((flags & D_EXPANDTABS) == 0) {
2484+ while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
2485+ printf("\t");
2486+ col = tabstop;
2487+ }
2488+ }
2489+ printf("%*s", newcol - col, "");
2490+}
+0,
-908
1@@ -1,908 +0,0 @@
2-/* $OpenBSD: diff.c,v 1.67 2019/06/28 13:35:00 deraadt Exp $ */
3-
4-/*
5- * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
6- *
7- * Permission to use, copy, modify, and distribute this software for any
8- * purpose with or without fee is hereby granted, provided that the above
9- * copyright notice and this permission notice appear in all copies.
10- *
11- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18- *
19- * Sponsored in part by the Defense Advanced Research Projects
20- * Agency (DARPA) and Air Force Research Laboratory, Air Force
21- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22- */
23-
24-#include <sys/cdefs.h>
25-#include <sys/stat.h>
26-
27-#include <ctype.h>
28-#include <err.h>
29-#include <errno.h>
30-#include <getopt.h>
31-#include <limits.h>
32-#include <stdio.h>
33-#include <stdlib.h>
34-#include <string.h>
35-#include <unistd.h>
36-
37-#include "util.h"
38-
39-#include "diff.h"
40-#include "xmalloc.h"
41-
42-#ifndef __dead2
43-#define __dead2 __attribute__((__noreturn__))
44-#endif
45-
46-static const char diff_version[] = "FreeBSD diff 20240307";
47-bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
48-bool ignore_file_case, suppress_common, color, noderef;
49-static bool help = false;
50-int diff_format, diff_context, diff_algorithm, status;
51-bool diff_algorithm_set;
52-int tabsize = 8, width = 130;
53-static int colorflag = COLORFLAG_NEVER;
54-char *start, *ifdefname, *diffargs, *label[2];
55-char *ignore_pats, *most_recent_pat;
56-char *group_format = NULL;
57-const char *add_code, *del_code;
58-struct stat stb1, stb2;
59-struct excludes *excludes_list;
60-regex_t ignore_re, most_recent_re;
61-
62-static struct algorithm {
63- const char *name;
64- int id;
65-} algorithms[] = {
66- {"stone", D_DIFFSTONE},
67- {"myers", D_DIFFMYERS},
68- {"patience", D_DIFFPATIENCE},
69- {NULL, D_DIFFNONE}
70-};
71-
72-#define OPTIONS "0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
73-enum {
74- OPT_TSIZE = CHAR_MAX + 1,
75- OPT_STRIPCR,
76- OPT_IGN_FN_CASE,
77- OPT_NO_IGN_FN_CASE,
78- OPT_NORMAL,
79- OPT_HELP,
80- OPT_HORIZON_LINES,
81- OPT_CHANGED_GROUP_FORMAT,
82- OPT_SUPPRESS_COMMON,
83- OPT_COLOR,
84- OPT_NO_DEREFERENCE,
85- OPT_VERSION,
86-};
87-
88-static struct option longopts[] = {
89- { "algorithm", required_argument, 0, 'A' },
90- { "text", no_argument, 0, 'a' },
91- { "ignore-space-change", no_argument, 0, 'b' },
92- { "context", optional_argument, 0, 'C' },
93- { "ifdef", required_argument, 0, 'D' },
94- { "minimal", no_argument, 0, 'd' },
95- { "ed", no_argument, 0, 'e' },
96- { "forward-ed", no_argument, 0, 'f' },
97- { "show-function-line", required_argument, 0, 'F' },
98- { "speed-large-files", no_argument, NULL, 'H' },
99- { "ignore-blank-lines", no_argument, 0, 'B' },
100- { "ignore-matching-lines", required_argument, 0, 'I' },
101- { "ignore-case", no_argument, 0, 'i' },
102- { "paginate", no_argument, NULL, 'l' },
103- { "label", required_argument, 0, 'L' },
104- { "new-file", no_argument, 0, 'N' },
105- { "rcs", no_argument, 0, 'n' },
106- { "unidirectional-new-file", no_argument, 0, 'P' },
107- { "show-c-function", no_argument, 0, 'p' },
108- { "brief", no_argument, 0, 'q' },
109- { "recursive", no_argument, 0, 'r' },
110- { "report-identical-files", no_argument, 0, 's' },
111- { "starting-file", required_argument, 0, 'S' },
112- { "expand-tabs", no_argument, 0, 't' },
113- { "initial-tab", no_argument, 0, 'T' },
114- { "unified", optional_argument, 0, 'U' },
115- { "ignore-all-space", no_argument, 0, 'w' },
116- { "width", required_argument, 0, 'W' },
117- { "exclude", required_argument, 0, 'x' },
118- { "exclude-from", required_argument, 0, 'X' },
119- { "side-by-side", no_argument, NULL, 'y' },
120- { "ignore-file-name-case", no_argument, NULL, OPT_IGN_FN_CASE },
121- { "help", no_argument, NULL, OPT_HELP},
122- { "horizon-lines", required_argument, NULL, OPT_HORIZON_LINES },
123- { "no-dereference", no_argument, NULL, OPT_NO_DEREFERENCE},
124- { "no-ignore-file-name-case", no_argument, NULL, OPT_NO_IGN_FN_CASE },
125- { "normal", no_argument, NULL, OPT_NORMAL },
126- { "strip-trailing-cr", no_argument, NULL, OPT_STRIPCR },
127- { "tabsize", required_argument, NULL, OPT_TSIZE },
128- { "changed-group-format", required_argument, NULL, OPT_CHANGED_GROUP_FORMAT},
129- { "suppress-common-lines", no_argument, NULL, OPT_SUPPRESS_COMMON },
130- { "color", optional_argument, NULL, OPT_COLOR },
131- { "version", no_argument, NULL, OPT_VERSION},
132- { NULL, 0, 0, '\0'}
133-};
134-
135-static void checked_regcomp(char const *, regex_t *);
136-static void usage(void) __dead2;
137-static void conflicting_format(void) __dead2;
138-static void push_excludes(char *);
139-static void push_ignore_pats(char *);
140-static void read_excludes_file(char *file);
141-static void set_argstr(char **, char **);
142-static char *splice(char *, char *);
143-static bool do_color(void);
144-
145-/* ?man diff: differential file and directory comparator
146-arguments: file1 file2
147-synopsis: [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--rcs] [--show-c-function] [--starting-file] [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [-I pattern] [-F pattern] [-L label] file1 file2
148-synopsis: [-aBbdilpTtw] [-A algo] [-I pattern] [-F pattern] [-L label] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -C number file1 file2
149-synopsis: [-aBbdiltw] [-A algo] [-I pattern] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -D string file1 file2
150-synopsis: [-aBbdilpTtw] [-A algo] [-I pattern] [-F pattern] [-L label] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -U number file1 file2
151-synopsis: [-aBbdilNPprsTtw] [-c|-e|-f|-n|-q|-u] [-A algo] [--brief] [--color=when] [--changed-group-format GFMT] [--context] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--new-file] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--recursive] [--report-identical-files] [--show-c-function] [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [--unidirectional-new-file] [--unified] [-I pattern] [-F pattern] [-L label] [-S name] [-X file] [-x pattern] dir1 dir2
152-synopsis: [-aBbditwW] [--color=when] [--expand-tabs] [--ignore-all-space] [--ignore-blank-lines] [--ignore-case] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--strip-trailing-cr] [--suppress-common-lines] [--tabsize number] [--text] -y file1 file2
153-synopsis: diff [--help]
154-synopsis: diff [--version]
155-The diff utility compares the contents of file1 and file2 and writes to the standard output the list of changes necessary to convert one file into the other. No output is produced if the files are identical.
156-## OUTPUT OPTIONS
157-### -C number, --context number
158-Like c but produces a diff with number lines of context.
159-### -c
160-Produces a diff with 3 lines of context.
161-
162-With c the output format is modified slightly: the output begins with identification of the files involved and their creation dates and then each change is separated by a line with fifteen *'s. The lines removed from file1 are marked with '- '; those added to file2 are marked '+ '. Lines which are changed from one file to the other are marked in both files with '! '. Changes which lie within 3 lines of each other are grouped together on output.
163-### -D string, --ifdef string
164-Creates a merged version of file1 and file2 on the standard output, with C preprocessor controls included so that a compilation of the result without defining string is equivalent to compiling file1, while defining string will yield file2.
165-### -e, --ed
166-Produces output in a form suitable as input for the editor utility, ed(1), which can then be used to convert file1 into file2.
167-
168-Extra commands are added to the output when comparing directories with e, so that the result is a sh(1) script for converting text files which are common to the two directories from their state in dir1 to their state in dir2. Note that when comparing directories with e, the resulting file may no longer be interpreted as an ed(1) script. Output is added to indicate which file each set of ed(1) commands applies to. These hunks can be manually extracted to produce an ed(1) script, which can also be applied with patch(1).
169-### -f, --forward-ed
170-Identical output to that of the e flag, but in reverse order. It cannot be digested by ed(1).
171-### --help
172-This option prints a summary to stdout and exits with status 0.
173-### -n
174-Produces a script similar to that of e, but in the opposite order and with a count of changed lines on each insert or delete command. This is the form used by rcsdiff.
175-### -q, --brief
176-Just print a line when the files differ. Does not output a list of changes.
177-### -U number, --unified number
178-Like u but produces a diff with number lines of context.
179-### -u
180-Produces a unified diff with 3 lines of context.
181-
182-A unified diff is similar to the context diff produced by the c option. However, unlike with c, all lines to be changed (added and/or removed) are present in a single section.
183-### --version
184-This option prints a version string to stdout and exits with status 0.
185-### -y, --side-by-side
186-Output in two columns with a marker between them.
187-
188-The marker can be one of the following:
189-
190-### space
191-Corresponding lines are identical.
192-### '|'
193-Corresponding lines are different.
194-### '<'
195-Files differ and only the first file contains the line.
196-### '>'
197-Files differ and only the second file contains the line.
198-## COMPARISON OPTIONS
199-### -A algo, --algorithm algo
200-Configure the algorithm used when comparing files. diff supports 3 algorithms:
201-
202-### myers
203-The Myers diff algorithm finds the shortest edit which transforms one input into the other. It generally runs in O(N+D²) time, requiring O(N) space, where N is the sum of the lengths of the inputs and D is the length of the difference between them, with a theoretical O(N·D) worst case. If it encounters worst-case input, the implementation used by diff falls back to a less optimal but faster algorithm.
204-### patience
205-The Patience variant of the Myers algorithm attempts to create more aesthetically pleasing diff output by logically grouping lines.
206-### stone
207-The Stone algorithm (commonly known as Hunt-McIlroy or Hunt-Szymanski) looks for the longest common subsequence between compared files. Stone encounters worst case performance when there are long common subsequences. In large files this can lead to a significant performance impact. The Stone algorithm is maintained for compatibility.
208-
209-The diff utility defaults to the Myers algorithm, but will fall back to the Stone algorithm if the input or output options are not supported by the Myers implementation.
210-### -a, --text
211-Treat all files as ASCII text.
212-
213-Normally diff will simply print "Binary files ... differ" if files contain binary characters. Use of this option forces diff to produce a diff.
214-### -B, --ignore-blank-lines
215-Causes chunks that include only blank lines to be ignored.
216-### -b, --ignore-space-change
217-Causes trailing blanks (spaces and tabs) to be ignored, and other strings of blanks to compare equal.
218-### --color when
219-Color the additions green, and removals red, or the value in the DIFFCOLORS environment variable.
220-
221-The possible values of when are "never", "always" and "auto". auto will use color if the output is a tty and the COLORTERM environment variable is set to a non-empty string.
222-### -d, --minimal
223-Try very hard to produce a diff as small as possible. This may consume a lot of processing power and memory when processing large files with many changes.
224-### -F pattern, --show-function-line pattern
225-Like p, but display the last line that matches provided pattern.
226-### -I pattern, --ignore-matching-lines pattern
227-Ignores changes, insertions, and deletions whose lines match the extended regular expression pattern. Multiple I patterns may be specified. All lines in the change must match some pattern for the change to be ignored. See re_format(7) for more information on regular expression patterns.
228-### -i, --ignore-case
229-Ignores the case of letters. E.g., "A" will compare equal to "a".
230-### -l, --paginate
231-Pass the output through pr(1) to paginate it.
232-### -L label, --label label
233-Print label instead of the first (and second, if this option is specified twice) file name and time in the context or unified diff header.
234-### -p, --show-c-function
235-With unified and context diffs, show with each change the first 40 characters of the last line before the context beginning with a letter, an underscore or a dollar sign. For C and Objective-C source code following standard layout conventions, this will show the prototype of the function the change applies to.
236-### -T, --initial-tab
237-Print a tab rather than a space before the rest of the line for the normal, context or unified output formats. This makes the alignment of tabs in the line consistent.
238-### -t, --expand-tabs
239-Will expand tabs in output lines.
240-
241-Normal or c output adds character(s) to the front of each line which may screw up the indentation of the original source lines and make the output listing difficult to interpret. This option will preserve the original source's indentation.
242-### -w, --ignore-all-space
243-Is similar to b --ignore-space-change but causes whitespace (blanks and tabs) to be totally ignored. E.g., "if ( a == b )" will compare equal to "if(a==b)".
244-### -W number, --width number
245-Output at most number columns when using side by side format. The default value is 130. Note that unless t was specified, diff will always align the second column to a tab stop, so values of --width smaller than approximately five times the value of --tabsize may yield surprising results.
246-### --changed-group-format GFMT
247-Format input groups in the provided.
248-
249-the format is a string with special keywords:
250-
251-### %<
252-lines from FILE1
253-### %>
254-lines from FILE2
255-### --ignore-file-name-case
256-ignore case when comparing file names
257-### --no-dereference
258-do not follow symbolic links
259-### --no-ignore-file-name-case
260-do not ignore case when comparing file names (default)
261-### --normal
262-default diff output
263-### --speed-large-files
264-stub option for compatibility with GNU diff
265-### --strip-trailing-cr
266-strip carriage return on input files
267-### --suppress-common-lines
268-Do not output common lines when using the side by side format
269-### --tabsize number
270-Number of spaces representing a tab (default 8)
271-## DIRECTORY COMPARISON OPTIONS
272-### -N, --new-file
273-If a file is found in only one directory, act as if it was found in the other directory too but was of zero size.
274-### -P, --unidirectional-new-file
275-If a file is found only in dir2, act as if it was found in dir1 too but was of zero size.
276-### -r, --recursive
277-Causes application of diff recursively to common subdirectories encountered.
278-### -S name, --starting-file name
279-Re-starts a directory diff in the middle, beginning with file name.
280-### -s, --report-identical-files
281-Causes diff to report files which are the same, which are otherwise not mentioned.
282-### -X file, --exclude-from file
283-Exclude files and subdirectories from comparison whose basenames match lines in file. Multiple X options may be specified.
284-### -x pattern, --exclude pattern
285-Exclude files and subdirectories from comparison whose basenames match pattern. Patterns are matched using shell-style globbing via fnmatch(3). Multiple x options may be specified.
286-
287-If both arguments are directories, diff sorts the contents of the directories by name, and then runs the regular file diff algorithm, producing a change list, on text files which are different. Binary files which differ, common subdirectories, and files which appear in only one directory are described as such. In directory mode only regular files and directories are compared. If a non-regular file such as a device special file or FIFO is encountered, a diagnostic message is printed.
288-
289-If only one of file1 and file2 is a directory, diff is applied to the non-directory file and the file contained in the directory file with a filename that is the same as the last component of the non-directory file.
290-
291-If either file1 or file2 is -, the standard input is used in its place.
292-## OUTPUT STYLE
293-The default (without e, c, or n -rcs options) output contains lines of these forms, where XX, YY, ZZ, QQ are line numbers respective of file order.
294-
295-### XXaYY
296-At (the end of) line XX of file1, append the contents of line YY of file2 to make them equal.
297-### XXaYY,ZZ
298-Same as above, but append the range of lines, YY through ZZ of file2 to line XX of file1.
299-### XXdYY
300-At line XX delete the line. The value YY tells to which line the change would bring file1 in line with file2.
301-### XX,YYdZZ
302-Delete the range of lines XX through YY in file1.
303-### XXcYY
304-Change the line XX in file1 to the line YY in file2.
305-### XX,YYcZZ
306-Replace the range of specified lines with the line ZZ.
307-### XX,YYcZZ,QQ
308-Replace the range XX,YY from file1 with the range ZZ,QQ from file2.
309-
310-These lines resemble ed(1) subcommands to convert file1 into file2. The line numbers before the action letters pertain to file1; those after pertain to file2. Thus, by exchanging a for d and reading the line in reverse order, one can also determine how to convert file2 into file1. As in ed(1), identical pairs (where num1 = num2) are abbreviated as a single number.
311-## ENVIRONMENT
312-### DIFFCOLORS
313-The value of this variable is the form add:rm, where add is the ASCII escape sequence for additions and rm is the ASCII escape sequence for deletions. If this is unset, diff uses green for additions and red for removals.
314-## FILES
315-### /tmp/diff.XXXXXXXX
316-Temporary file used when comparing a device or the standard input. Note that the temporary file is unlinked as soon as it is created so it will not show up in a directory listing.
317-## EXIT STATUS
318-### 0
319-No differences were found.
320-### 1
321-Differences were found.
322-### >1
323-An error occurred.
324-
325-The --help and --version options exit with a status of 0.
326-## EXAMPLES
327-### Compare old_dir and new_dir recursively generating an unified diff and treating files found only in one of those directories as new files
328-$ diff -ruN /path/to/old_dir /path/to/new_dir
329-### Same as above but excluding files matching the expressions "*.h" and "*.c"
330-$ diff -ruN -x '*.h' -x '*.c' /path/to/old_dir /path/to/new_dir
331-### Show a single line indicating if the files differ
332-$ diff -q /boot/loader.conf /boot/defaults/loader.conf
333-Files /boot/loader.conf and /boot/defaults/loader.conf differ
334-### Assuming a file named example.txt with the following contents
335-FreeBSD is an operating system
336-Linux is a kernel
337-OpenBSD is an operating system
338-### Compare stdin with example.txt excluding from the comparison those lines containing either "Linux" or "Open"
339-$ echo "FreeBSD is an operating system" | diff -q -I 'Linux|Open' example.txt -
340-## SEE ALSO
341-cmp(1), comm(1), diff3(1), ed(1), patch(1), pr(1), sdiff(1)
342-## STANDARDS
343-The diff utility is compliant with the POSIX.1-2008 specification.
344-
345-The flags A a D d I i L l N n P p q S s T t w X x y are extensions to that specification.
346-## HISTORY
347-A diff command appeared in AT&T Version 6 UNIX.
348-
349-The diff implementation used in FreeBSD was GNU diff until FreeBSD 11.4. This was replaced in FreeBSD 12.0 by a BSD-licensed implementation written by Todd Miller. Some GNUisms were lost in the process.
350-
351-libdiff was imported from the Game of Trees version control system and default algorithm was changed to Myers for FreeBSD 15 .
352-*/
353-int
354-main(int argc, char **argv)
355-{
356- const char *errstr = NULL;
357- char *ep, **oargv;
358- long l;
359- int ch, dflags, lastch, gotstdin, prevoptind, newarg;
360-
361- oargv = argv;
362- gotstdin = 0;
363- dflags = 0;
364- lastch = '\0';
365- prevoptind = 1;
366- newarg = 1;
367- diff_context = 3;
368- diff_format = D_UNSET;
369- diff_algorithm = D_DIFFMYERS;
370- diff_algorithm_set = false;
371-#define FORMAT_MISMATCHED(type) \
372- (diff_format != D_UNSET && diff_format != (type))
373- while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
374- switch (ch) {
375- case '0': case '1': case '2': case '3': case '4':
376- case '5': case '6': case '7': case '8': case '9':
377- if (newarg)
378- usage(); /* disallow -[0-9]+ */
379- else if (lastch == 'c' || lastch == 'u')
380- diff_context = 0;
381- else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
382- usage();
383- diff_context = (diff_context * 10) + (ch - '0');
384- break;
385- case 'A':
386- diff_algorithm = D_DIFFNONE;
387- for (struct algorithm *a = algorithms; a->name;a++) {
388- if(strcasecmp(optarg, a->name) == 0) {
389- diff_algorithm = a->id;
390- diff_algorithm_set = true;
391- break;
392- }
393- }
394-
395- if (diff_algorithm == D_DIFFNONE) {
396- printf("unknown algorithm: %s\n", optarg);
397- usage();
398- }
399- break;
400- case 'a':
401- dflags |= D_FORCEASCII;
402- break;
403- case 'b':
404- dflags |= D_FOLDBLANKS;
405- break;
406- case 'C':
407- case 'c':
408- if (FORMAT_MISMATCHED(D_CONTEXT))
409- conflicting_format();
410- cflag = true;
411- diff_format = D_CONTEXT;
412- if (optarg != NULL) {
413- l = strtol(optarg, &ep, 10);
414- if (*ep != '\0' || l < 0 || l >= INT_MAX)
415- usage();
416- diff_context = (int)l;
417- }
418- break;
419- case 'd':
420- dflags |= D_MINIMAL;
421- break;
422- case 'D':
423- if (FORMAT_MISMATCHED(D_IFDEF))
424- conflicting_format();
425- diff_format = D_IFDEF;
426- ifdefname = optarg;
427- break;
428- case 'e':
429- if (FORMAT_MISMATCHED(D_EDIT))
430- conflicting_format();
431- diff_format = D_EDIT;
432- break;
433- case 'f':
434- if (FORMAT_MISMATCHED(D_REVERSE))
435- conflicting_format();
436- diff_format = D_REVERSE;
437- break;
438- case 'H':
439- /* ignore but needed for compatibility with GNU diff */
440- break;
441- case 'h':
442- /* silently ignore for backwards compatibility */
443- break;
444- case 'B':
445- dflags |= D_SKIPBLANKLINES;
446- break;
447- case 'F':
448- if (dflags & D_PROTOTYPE)
449- conflicting_format();
450- dflags |= D_MATCHLAST;
451- most_recent_pat = xstrdup(optarg);
452- break;
453- case 'I':
454- push_ignore_pats(optarg);
455- break;
456- case 'i':
457- dflags |= D_IGNORECASE;
458- break;
459- case 'L':
460- if (label[0] == NULL)
461- label[0] = optarg;
462- else if (label[1] == NULL)
463- label[1] = optarg;
464- else
465- usage();
466- break;
467- case 'l':
468- lflag = true;
469- break;
470- case 'N':
471- Nflag = true;
472- break;
473- case 'n':
474- if (FORMAT_MISMATCHED(D_NREVERSE))
475- conflicting_format();
476- diff_format = D_NREVERSE;
477- break;
478- case 'p':
479- if (dflags & D_MATCHLAST)
480- conflicting_format();
481- dflags |= D_PROTOTYPE;
482- break;
483- case 'P':
484- Pflag = true;
485- break;
486- case 'r':
487- rflag = true;
488- break;
489- case 'q':
490- if (FORMAT_MISMATCHED(D_BRIEF))
491- conflicting_format();
492- diff_format = D_BRIEF;
493- break;
494- case 'S':
495- start = optarg;
496- break;
497- case 's':
498- sflag = true;
499- break;
500- case 'T':
501- Tflag = true;
502- break;
503- case 't':
504- dflags |= D_EXPANDTABS;
505- break;
506- case 'U':
507- case 'u':
508- if (FORMAT_MISMATCHED(D_UNIFIED))
509- conflicting_format();
510- diff_format = D_UNIFIED;
511- if (optarg != NULL) {
512- l = strtol(optarg, &ep, 10);
513- if (*ep != '\0' || l < 0 || l >= INT_MAX)
514- usage();
515- diff_context = (int)l;
516- }
517- break;
518- case 'w':
519- dflags |= D_IGNOREBLANKS;
520- break;
521- case 'W':
522- width = (int) strtonum(optarg, 1, INT_MAX, &errstr);
523- if (errstr) {
524- warnx("Invalid argument for width");
525- usage();
526- }
527- break;
528- case 'X':
529- read_excludes_file(optarg);
530- break;
531- case 'x':
532- push_excludes(optarg);
533- break;
534- case 'y':
535- if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
536- conflicting_format();
537- diff_format = D_SIDEBYSIDE;
538- break;
539- case OPT_CHANGED_GROUP_FORMAT:
540- if (FORMAT_MISMATCHED(D_GFORMAT))
541- conflicting_format();
542- diff_format = D_GFORMAT;
543- group_format = optarg;
544- break;
545- case OPT_HELP:
546- help = true;
547- usage();
548- break;
549- case OPT_HORIZON_LINES:
550- break; /* XXX TODO for compatibility with GNU diff3 */
551- case OPT_IGN_FN_CASE:
552- ignore_file_case = true;
553- break;
554- case OPT_NO_IGN_FN_CASE:
555- ignore_file_case = false;
556- break;
557- case OPT_NORMAL:
558- if (FORMAT_MISMATCHED(D_NORMAL))
559- conflicting_format();
560- diff_format = D_NORMAL;
561- break;
562- case OPT_TSIZE:
563- tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr);
564- if (errstr) {
565- warnx("Invalid argument for tabsize");
566- usage();
567- }
568- break;
569- case OPT_STRIPCR:
570- dflags |= D_STRIPCR;
571- break;
572- case OPT_SUPPRESS_COMMON:
573- suppress_common = 1;
574- break;
575- case OPT_COLOR:
576- if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
577- colorflag = COLORFLAG_AUTO;
578- else if (strncmp(optarg, "always", 6) == 0)
579- colorflag = COLORFLAG_ALWAYS;
580- else if (strncmp(optarg, "never", 5) == 0)
581- colorflag = COLORFLAG_NEVER;
582- else
583- errx(2, "unsupported --color value '%s' (must be always, auto, or never)",
584- optarg);
585- break;
586- case OPT_NO_DEREFERENCE:
587- noderef = true;
588- break;
589- case OPT_VERSION:
590- printf("%s\n", diff_version);
591- exit(0);
592- default:
593- usage();
594- break;
595- }
596- lastch = ch;
597- newarg = optind != prevoptind;
598- prevoptind = optind;
599- }
600- if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
601- diff_format = D_CONTEXT;
602- if (diff_format == D_UNSET)
603- diff_format = D_NORMAL;
604- argc -= optind;
605- argv += optind;
606-
607- if (do_color()) {
608- char *p;
609- const char *env;
610-
611- color = true;
612- add_code = "32";
613- del_code = "31";
614- env = getenv("DIFFCOLORS");
615- if (env != NULL && *env != '\0' && (p = strdup(env))) {
616- add_code = p;
617- strsep(&p, ":");
618- if (p != NULL)
619- del_code = p;
620- }
621- }
622-
623-#ifdef __OpenBSD__
624- if (pledge("stdio rpath tmppath", NULL) == -1)
625- err(2, "pledge");
626-#endif
627-
628- /*
629- * Do sanity checks, fill in stb1 and stb2 and call the appropriate
630- * driver routine. Both drivers use the contents of stb1 and stb2.
631- */
632- if (argc != 2)
633- usage();
634- checked_regcomp(ignore_pats, &ignore_re);
635- checked_regcomp(most_recent_pat, &most_recent_re);
636- if (strcmp(argv[0], "-") == 0) {
637- fstat(STDIN_FILENO, &stb1);
638- gotstdin = 1;
639- } else if (stat(argv[0], &stb1) != 0) {
640- if (!Nflag || errno != ENOENT)
641- err(2, "%s", argv[0]);
642- dflags |= D_EMPTY1;
643- memset(&stb1, 0, sizeof(struct stat));
644- }
645-
646- if (strcmp(argv[1], "-") == 0) {
647- fstat(STDIN_FILENO, &stb2);
648- gotstdin = 1;
649- } else if (stat(argv[1], &stb2) != 0) {
650- if (!Nflag || errno != ENOENT)
651- err(2, "%s", argv[1]);
652- dflags |= D_EMPTY2;
653- memset(&stb2, 0, sizeof(stb2));
654- stb2.st_mode = stb1.st_mode;
655- }
656-
657- if (dflags & D_EMPTY1 && dflags & D_EMPTY2){
658- warn("%s", argv[0]);
659- warn("%s", argv[1]);
660- exit(2);
661- }
662-
663- if (stb1.st_mode == 0)
664- stb1.st_mode = stb2.st_mode;
665-
666- if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
667- errx(2, "can't compare - to a directory");
668- set_argstr(oargv, argv);
669- if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
670- if (diff_format == D_IFDEF)
671- errx(2, "-D option not supported with directories");
672- diffdir(argv[0], argv[1], dflags);
673- } else {
674- if (S_ISDIR(stb1.st_mode)) {
675- argv[0] = splice(argv[0], argv[1]);
676- if (stat(argv[0], &stb1) == -1)
677- err(2, "%s", argv[0]);
678- }
679- if (S_ISDIR(stb2.st_mode)) {
680- argv[1] = splice(argv[1], argv[0]);
681- if (stat(argv[1], &stb2) == -1)
682- err(2, "%s", argv[1]);
683- }
684- print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0],
685- argv[1], "");
686- }
687- if (fflush(stdout) != 0)
688- err(2, "stdout");
689- exit(status);
690-}
691-
692-static void
693-checked_regcomp(char const *pattern, regex_t *comp)
694-{
695- char buf[BUFSIZ];
696- int error;
697-
698- if (pattern == NULL)
699- return;
700-
701- error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
702- if (error != 0) {
703- regerror(error, comp, buf, sizeof(buf));
704- if (*pattern != '\0')
705- errx(2, "%s: %s", pattern, buf);
706- else
707- errx(2, "%s", buf);
708- }
709-}
710-
711-static void
712-set_argstr(char **av, char **ave)
713-{
714- size_t argsize;
715- char **ap;
716-
717- argsize = strlen("diff") + 1;
718- for (ap = av + 1; ap < ave; ap++) {
719- if (strcmp(*ap, "--") != 0)
720- argsize += 1 + strlen(*ap);
721- }
722- diffargs = xmalloc(argsize);
723- strlcpy(diffargs, "diff", argsize);
724- for (ap = av + 1; ap < ave; ap++) {
725- if (strcmp(*ap, "--") != 0) {
726- strlcat(diffargs, " ", argsize);
727- strlcat(diffargs, *ap, argsize);
728- }
729- }
730-}
731-
732-/*
733- * Read in an excludes file and push each line.
734- */
735-static void
736-read_excludes_file(char *file)
737-{
738- FILE *fp;
739- char *pattern = NULL;
740- size_t blen = 0;
741- ssize_t len;
742-
743- if (strcmp(file, "-") == 0)
744- fp = stdin;
745- else if ((fp = fopen(file, "r")) == NULL)
746- err(2, "%s", file);
747- while ((len = getline(&pattern, &blen, fp)) >= 0) {
748- if ((len > 0) && (pattern[len - 1] == '\n'))
749- pattern[len - 1] = '\0';
750- push_excludes(pattern);
751- /* we allocate a new string per line */
752- pattern = NULL;
753- blen = 0;
754- }
755- free(pattern);
756- if (strcmp(file, "-") != 0)
757- fclose(fp);
758-}
759-
760-/*
761- * Push a pattern onto the excludes list.
762- */
763-static void
764-push_excludes(char *pattern)
765-{
766- struct excludes *entry;
767-
768- entry = xmalloc(sizeof(*entry));
769- entry->pattern = pattern;
770- entry->next = excludes_list;
771- excludes_list = entry;
772-}
773-
774-static void
775-push_ignore_pats(char *pattern)
776-{
777- size_t len;
778-
779- if (ignore_pats == NULL)
780- ignore_pats = xstrdup(pattern);
781- else {
782- /* old + "|" + new + NUL */
783- len = strlen(ignore_pats) + strlen(pattern) + 2;
784- ignore_pats = xreallocarray(ignore_pats, 1, len);
785- strlcat(ignore_pats, "|", len);
786- strlcat(ignore_pats, pattern, len);
787- }
788-}
789-
790-void
791-print_status(int val, char *path1, char *path2, const char *entry)
792-{
793- if (label[0] != NULL)
794- path1 = label[0];
795- if (label[1] != NULL)
796- path2 = label[1];
797-
798- switch (val) {
799- case D_BINARY:
800- printf("Binary files %s%s and %s%s differ\n",
801- path1, entry, path2, entry);
802- break;
803- case D_DIFFER:
804- if (diff_format == D_BRIEF)
805- printf("Files %s%s and %s%s differ\n",
806- path1, entry, path2, entry);
807- break;
808- case D_SAME:
809- if (sflag)
810- printf("Files %s%s and %s%s are identical\n",
811- path1, entry, path2, entry);
812- break;
813- case D_MISMATCH1:
814- printf("File %s%s is a directory while file %s%s is a regular file\n",
815- path1, entry, path2, entry);
816- break;
817- case D_MISMATCH2:
818- printf("File %s%s is a regular file while file %s%s is a directory\n",
819- path1, entry, path2, entry);
820- break;
821- case D_SKIPPED1:
822- printf("File %s%s is not a regular file or directory and was skipped\n",
823- path1, entry);
824- break;
825- case D_SKIPPED2:
826- printf("File %s%s is not a regular file or directory and was skipped\n",
827- path2, entry);
828- break;
829- case D_ERROR:
830- break;
831- }
832-}
833-
834-static void
835-usage(void)
836-{
837- (void)fprintf(help ? stdout : stderr,
838- "usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
839- " [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
840- " [-I pattern] [-F pattern] [-L label] file1 file2\n"
841- " diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
842- " [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
843- " [-F pattern] -C number file1 file2\n"
844- " diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
845- " [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
846- " diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
847- " [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
848- " [-F pattern] -U number file1 file2\n"
849- " diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
850- " [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
851- " [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2\n"
852- " diff [-aBbditwW] [--expand-tabs] [--ignore-all-space]\n"
853- " [--ignore-blank-lines] [--ignore-case] [--minimal]\n"
854- " [--no-ignore-file-name-case] [--strip-trailing-cr]\n"
855- " [--suppress-common-lines] [--tabsize] [--text] [--width]\n"
856- " -y | --side-by-side file1 file2\n"
857- " diff [--help] [--version]\n");
858-
859- if (help)
860- exit(0);
861- else
862- exit(2);
863-}
864-
865-static void
866-conflicting_format(void)
867-{
868-
869- fprintf(stderr, "error: conflicting output format options.\n");
870- usage();
871-}
872-
873-static bool
874-do_color(void)
875-{
876- const char *p, *p2;
877-
878- switch (colorflag) {
879- case COLORFLAG_AUTO:
880- p = getenv("CLICOLOR");
881- p2 = getenv("COLORTERM");
882- if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0'))
883- return isatty(STDOUT_FILENO);
884- break;
885- case COLORFLAG_ALWAYS:
886- return (true);
887- case COLORFLAG_NEVER:
888- return (false);
889- }
890-
891- return (false);
892-}
893-
894-static char *
895-splice(char *dir, char *path)
896-{
897- char *tail, *buf;
898- size_t dirlen;
899-
900- dirlen = strlen(dir);
901- while (dirlen != 0 && dir[dirlen - 1] == '/')
902- dirlen--;
903- if ((tail = strrchr(path, '/')) == NULL)
904- tail = path;
905- else
906- tail++;
907- xasprintf(&buf, "%.*s/%s", (int)dirlen, dir, tail);
908- return (buf);
909-}
+0,
-137
1@@ -1,137 +0,0 @@
2-/* $OpenBSD: diff.h,v 1.34 2020/11/01 18:16:08 jcs Exp $ */
3-
4-/*-
5- * Copyright (c) 1991, 1993
6- * The Regents of the University of California. All rights reserved.
7- *
8- * Redistribution and use in source and binary forms, with or without
9- * modification, are permitted provided that the following conditions
10- * are met:
11- * 1. Redistributions of source code must retain the above copyright
12- * notice, this list of conditions and the following disclaimer.
13- * 2. Redistributions in binary form must reproduce the above copyright
14- * notice, this list of conditions and the following disclaimer in the
15- * documentation and/or other materials provided with the distribution.
16- * 3. Neither the name of the University nor the names of its contributors
17- * may be used to endorse or promote products derived from this software
18- * without specific prior written permission.
19- *
20- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30- * SUCH DAMAGE.
31- */
32-
33-#include <sys/types.h>
34-#include <sys/stat.h>
35-
36-#include <stdbool.h>
37-#include <regex.h>
38-
39-/*
40- * Output format options
41- */
42-#define D_NORMAL 0 /* Normal output */
43-#define D_EDIT -1 /* Editor script out */
44-#define D_REVERSE 1 /* Reverse editor script */
45-#define D_CONTEXT 2 /* Diff with context */
46-#define D_UNIFIED 3 /* Unified context diff */
47-#define D_IFDEF 4 /* Diff with merged #ifdef's */
48-#define D_NREVERSE 5 /* Reverse ed script with numbered
49- lines and no trailing . */
50-#define D_BRIEF 6 /* Say if the files differ */
51-#define D_GFORMAT 7 /* Diff with defined changed group format */
52-#define D_SIDEBYSIDE 8 /* Side by side */
53-
54-#define D_UNSET -2
55-
56-/*
57- * Algorithms
58- */
59-
60-#define D_DIFFNONE 0
61-#define D_DIFFSTONE 1 /* Stone or 'old diff' algorithm */
62-#define D_DIFFMYERS 2 /* Myers diff algorithm */
63-#define D_DIFFPATIENCE 3 /* Patience diff algorithm */
64-
65-/*
66- * Output flags
67- */
68-#define D_HEADER 0x001 /* Print a header/footer between files */
69-#define D_EMPTY1 0x002 /* Treat first file as empty (/dev/null) */
70-#define D_EMPTY2 0x004 /* Treat second file as empty (/dev/null) */
71-
72-/*
73- * Command line flags
74- */
75-#define D_FORCEASCII 0x008 /* Treat file as ascii regardless of content */
76-#define D_FOLDBLANKS 0x010 /* Treat all white space as equal */
77-#define D_MINIMAL 0x020 /* Make diff as small as possible */
78-#define D_IGNORECASE 0x040 /* Case-insensitive matching */
79-#define D_PROTOTYPE 0x080 /* Display C function prototype */
80-#define D_EXPANDTABS 0x100 /* Expand tabs to spaces */
81-#define D_IGNOREBLANKS 0x200 /* Ignore white space changes */
82-#define D_STRIPCR 0x400 /* Strip trailing cr */
83-#define D_SKIPBLANKLINES 0x800 /* Skip blank lines */
84-#define D_MATCHLAST 0x1000 /* Display last line matching provided regex */
85-
86-/* Features supported by new algorithms */
87-#define D_NEWALGO_FLAGS (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS)
88-
89-/*
90- * Status values for print_status() and diffreg() return values
91- */
92-#define D_SAME 0 /* Files are the same */
93-#define D_DIFFER 1 /* Files are different */
94-#define D_BINARY 2 /* Binary files are different */
95-#define D_MISMATCH1 3 /* path1 was a dir, path2 a file */
96-#define D_MISMATCH2 4 /* path1 was a file, path2 a dir */
97-#define D_SKIPPED1 5 /* path1 was a special file */
98-#define D_SKIPPED2 6 /* path2 was a special file */
99-#define D_ERROR 7 /* A file access error occurred */
100-
101-/*
102- * Color options
103- */
104-#define COLORFLAG_NEVER 0
105-#define COLORFLAG_AUTO 1
106-#define COLORFLAG_ALWAYS 2
107-
108-struct excludes {
109- char *pattern;
110- struct excludes *next;
111-};
112-
113-extern bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
114-extern bool ignore_file_case, suppress_common, color, noderef, algorithm_set;
115-extern int diff_format, diff_context, diff_algorithm, status;
116-extern bool diff_algorithm_set;
117-extern int tabsize, width;
118-extern char *start, *ifdefname, *diffargs, *label[2];
119-extern char *ignore_pats, *most_recent_pat;
120-extern char *group_format;
121-extern const char *add_code, *del_code;
122-extern struct stat stb1, stb2;
123-extern struct excludes *excludes_list;
124-extern regex_t ignore_re, most_recent_re;
125-
126-int diffreg(char *, char *, int, int);
127-int diffreg_new(char *, char *, int, int);
128-bool can_libdiff(int);
129-void diffdir(char *, char *, int);
130-void print_status(int, char *, char *, const char *);
131-
132-#ifndef MIN
133-#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
134-#endif
135-
136-#ifndef MAX
137-#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
138-#endif
+0,
-377
1@@ -1,377 +0,0 @@
2-/* $OpenBSD: diffdir.c,v 1.45 2015/10/05 20:15:00 millert Exp $ */
3-
4-/*
5- * Copyright (c) 2003, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
6- *
7- * Permission to use, copy, modify, and distribute this software for any
8- * purpose with or without fee is hereby granted, provided that the above
9- * copyright notice and this permission notice appear in all copies.
10- *
11- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18- *
19- * Sponsored in part by the Defense Advanced Research Projects
20- * Agency (DARPA) and Air Force Research Laboratory, Air Force
21- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22- */
23-
24-#include <sys/cdefs.h>
25-#include <sys/stat.h>
26-
27-#include <dirent.h>
28-#include <err.h>
29-#include <errno.h>
30-#include <fcntl.h>
31-#include <fnmatch.h>
32-#include <limits.h>
33-#include <stdio.h>
34-#include <stdlib.h>
35-#include <string.h>
36-#include <unistd.h>
37-
38-#include "util.h"
39-
40-#include "diff.h"
41-
42-static int selectfile(const struct dirent *);
43-static void diffit(struct dirent *, char *, size_t, struct dirent *,
44- char *, size_t, int);
45-static void print_only(const char *, size_t, const char *);
46-
47-#define d_status d_type /* we need to store status for -l */
48-
49-#if 0
50-struct inode {
51- dev_t dev;
52- ino_t ino;
53- RB_ENTRY(inode) entry;
54-};
55-
56-static int
57-inodecmp(struct inode *a, struct inode *b)
58-{
59- return (a->dev < b->dev ? -1 : a->dev > b->dev ? 1 :
60- a->ino < b->ino ? -1 : a->ino > b->ino ? 1 : 0);
61-}
62-
63-RB_HEAD(inodetree, inode);
64-static struct inodetree v1 = RB_INITIALIZER(&v1);
65-static struct inodetree v2 = RB_INITIALIZER(&v2);
66-RB_GENERATE_STATIC(inodetree, inode, entry, inodecmp);
67-
68-static int
69-vscandir(struct inodetree *tree, const char *path, struct dirent ***dirp,
70- int (*selectf)(const struct dirent *),
71- int (*comparf)(const struct dirent **, const struct dirent **))
72-{
73- struct stat sb;
74- struct inode *ino = NULL;
75- int fd = -1, ret, serrno;
76-
77- if ((fd = open(path, O_DIRECTORY | O_RDONLY)) < 0 ||
78- (ino = calloc(1, sizeof(*ino))) == NULL ||
79- fstat(fd, &sb) != 0)
80- goto fail;
81- ino->dev = sb.st_dev;
82- ino->ino = sb.st_ino;
83- if (RB_FIND(inodetree, tree, ino)) {
84- free(ino);
85- close(fd);
86- warnx("%s: Directory loop detected", path);
87- *dirp = NULL;
88- return (0);
89- }
90- if ((ret = fdscandir(fd, dirp, selectf, comparf)) < 0)
91- goto fail;
92- RB_INSERT(inodetree, tree, ino);
93- close(fd);
94- return (ret);
95-fail:
96- serrno = errno;
97- if (ino != NULL)
98- free(ino);
99- if (fd >= 0)
100- close(fd);
101- errno = serrno;
102- return (-1);
103-}
104-#endif
105-
106-/*
107- * Diff directory traversal. Will be called recursively if -r was specified.
108- */
109-void
110-diffdir(char *p1, char *p2, int flags)
111-{
112- struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
113- struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
114- size_t dirlen1, dirlen2;
115- char path1[PATH_MAX], path2[PATH_MAX];
116- int pos;
117-
118- edp1 = edp2 = NULL;
119-
120- dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
121- if (dirlen1 >= sizeof(path1) - 1) {
122- errno = ENAMETOOLONG;
123- warn("%s", p1);
124- status |= 2;
125- return;
126- }
127- if (path1[dirlen1 - 1] != '/') {
128- path1[dirlen1++] = '/';
129- path1[dirlen1] = '\0';
130- }
131- dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
132- if (dirlen2 >= sizeof(path2) - 1) {
133- errno = ENAMETOOLONG;
134- warn("%s", p2);
135- status |= 2;
136- return;
137- }
138- if (path2[dirlen2 - 1] != '/') {
139- path2[dirlen2++] = '/';
140- path2[dirlen2] = '\0';
141- }
142-
143- /*
144- * Get a list of entries in each directory, skipping "excluded" files
145- * and sorting alphabetically.
146- */
147- pos = scandir(path1, &dirp1, selectfile, alphasort);
148- if (pos == -1) {
149- if (errno == ENOENT && (Nflag || Pflag)) {
150- pos = 0;
151- } else {
152- warn("%s", path1);
153- goto closem;
154- }
155- }
156- dp1 = dirp1;
157- edp1 = dirp1 + pos;
158-
159- pos = scandir(path2, &dirp2, selectfile, alphasort);
160- if (pos == -1) {
161- if (errno == ENOENT && Nflag) {
162- pos = 0;
163- } else {
164- warn("%s", path2);
165- goto closem;
166- }
167- }
168- dp2 = dirp2;
169- edp2 = dirp2 + pos;
170-
171- /*
172- * If we were given a starting point, find it.
173- */
174- if (start != NULL) {
175- while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
176- dp1++;
177- while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
178- dp2++;
179- }
180-
181- /*
182- * Iterate through the two directory lists, diffing as we go.
183- */
184- while (dp1 != edp1 || dp2 != edp2) {
185- dent1 = dp1 != edp1 ? *dp1 : NULL;
186- dent2 = dp2 != edp2 ? *dp2 : NULL;
187-
188- pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
189- ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name) :
190- strcmp(dent1->d_name, dent2->d_name) ;
191- if (pos == 0) {
192- /* file exists in both dirs, diff it */
193- diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
194- dp1++;
195- dp2++;
196- } else if (pos < 0) {
197- /* file only in first dir, only diff if -N */
198- if (Nflag) {
199- diffit(dent1, path1, dirlen1, dent2, path2,
200- dirlen2, flags);
201- } else {
202- print_only(path1, dirlen1, dent1->d_name);
203- status |= 1;
204- }
205- dp1++;
206- } else {
207- /* file only in second dir, only diff if -N or -P */
208- if (Nflag || Pflag)
209- diffit(dent2, path1, dirlen1, dent1, path2,
210- dirlen2, flags);
211- else {
212- print_only(path2, dirlen2, dent2->d_name);
213- status |= 1;
214- }
215- dp2++;
216- }
217- }
218-
219-closem:
220- if (dirp1 != NULL) {
221- for (dp1 = dirp1; dp1 < edp1; dp1++)
222- free(*dp1);
223- free(dirp1);
224- }
225- if (dirp2 != NULL) {
226- for (dp2 = dirp2; dp2 < edp2; dp2++)
227- free(*dp2);
228- free(dirp2);
229- }
230-}
231-
232-/*
233- * Do the actual diff by calling either diffreg() or diffdir().
234- */
235-static void
236-diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
237- char *path2, size_t plen2, int flags)
238-{
239- flags |= D_HEADER;
240- strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
241-
242- /*
243- * If we are ignoring file case, use dent2s name here if both names are
244- * the same apart from case.
245- */
246- if (ignore_file_case && strcasecmp(dp->d_name, dp2->d_name) == 0)
247- strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
248- else
249- strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
250-
251- if (noderef) {
252- if (lstat(path1, &stb1) != 0) {
253- if (!(Nflag || Pflag) || errno != ENOENT) {
254- warn("%s", path1);
255- return;
256- }
257- flags |= D_EMPTY1;
258- memset(&stb1, 0, sizeof(stb1));
259- }
260-
261- if (lstat(path2, &stb2) != 0) {
262- if (!Nflag || errno != ENOENT) {
263- warn("%s", path2);
264- return;
265- }
266- flags |= D_EMPTY2;
267- memset(&stb2, 0, sizeof(stb2));
268- stb2.st_mode = stb1.st_mode;
269- }
270- if (stb1.st_mode == 0)
271- stb1.st_mode = stb2.st_mode;
272- if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
273- if (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
274- char buf1[PATH_MAX];
275- char buf2[PATH_MAX];
276- ssize_t len1 = 0;
277- ssize_t len2 = 0;
278-
279- len1 = readlink(path1, buf1, sizeof(buf1));
280- len2 = readlink(path2, buf2, sizeof(buf2));
281-
282- if (len1 < 0 || len2 < 0) {
283- perror("reading links");
284- return;
285- }
286- buf1[len1] = '\0';
287- buf2[len2] = '\0';
288-
289- if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
290- printf("Symbolic links %s and %s differ\n",
291- path1, path2);
292- status |= 1;
293- }
294-
295- return;
296- }
297-
298- printf("File %s is a %s while file %s is a %s\n",
299- path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
300- (S_ISDIR(stb1.st_mode) ? "directory" :
301- (S_ISREG(stb1.st_mode) ? "file" : "error")),
302- path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
303- (S_ISDIR(stb2.st_mode) ? "directory" :
304- (S_ISREG(stb2.st_mode) ? "file" : "error")));
305- status |= 1;
306- return;
307- }
308- } else {
309- if (stat(path1, &stb1) != 0) {
310- if (!(Nflag || Pflag) || errno != ENOENT) {
311- warn("%s", path1);
312- return;
313- }
314- flags |= D_EMPTY1;
315- memset(&stb1, 0, sizeof(stb1));
316- }
317-
318- if (stat(path2, &stb2) != 0) {
319- if (!Nflag || errno != ENOENT) {
320- warn("%s", path2);
321- return;
322- }
323- flags |= D_EMPTY2;
324- memset(&stb2, 0, sizeof(stb2));
325- stb2.st_mode = stb1.st_mode;
326- }
327- if (stb1.st_mode == 0)
328- stb1.st_mode = stb2.st_mode;
329- }
330- if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
331- if (rflag)
332- diffdir(path1, path2, flags);
333- else
334- printf("Common subdirectories: %s and %s\n",
335- path1, path2);
336- return;
337- }
338- if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
339- dp->d_status = D_SKIPPED1;
340- else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
341- dp->d_status = D_SKIPPED2;
342- else
343- dp->d_status = diffreg(path1, path2, flags, 0);
344- print_status(dp->d_status, path1, path2, "");
345-}
346-
347-/*
348- * Returns 1 if the directory entry should be included in the
349- * diff, else 0. Checks the excludes list.
350- */
351-static int
352-selectfile(const struct dirent *dp)
353-{
354- struct excludes *excl;
355-
356- if (dp->d_fileno == 0)
357- return (0);
358-
359- /* always skip "." and ".." */
360- if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
361- (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
362- return (0);
363-
364- /* check excludes list */
365- for (excl = excludes_list; excl != NULL; excl = excl->next)
366- if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
367- return (0);
368-
369- return (1);
370-}
371-
372-void
373-print_only(const char *path, size_t dirlen, const char *entry)
374-{
375- if (dirlen > 1)
376- dirlen--;
377- printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
378-}
+0,
-1697
1@@ -1,1697 +0,0 @@
2-/* $OpenBSD: diffreg.c,v 1.93 2019/06/28 13:35:00 deraadt Exp $ */
3-
4-/*-
5- * SPDX-License-Identifier: BSD-4-Clause
6- *
7- * Copyright (C) Caldera International Inc. 2001-2002.
8- * All rights reserved.
9- *
10- * Redistribution and use in source and binary forms, with or without
11- * modification, are permitted provided that the following conditions
12- * are met:
13- * 1. Redistributions of source code and documentation must retain the above
14- * copyright notice, this list of conditions and the following disclaimer.
15- * 2. Redistributions in binary form must reproduce the above copyright
16- * notice, this list of conditions and the following disclaimer in the
17- * documentation and/or other materials provided with the distribution.
18- * 3. All advertising materials mentioning features or use of this software
19- * must display the following acknowledgement:
20- * This product includes software developed or owned by Caldera
21- * International, Inc.
22- * 4. Neither the name of Caldera International, Inc. nor the names of other
23- * contributors may be used to endorse or promote products derived from
24- * this software without specific prior written permission.
25- *
26- * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
27- * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
28- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30- * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
31- * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37- * POSSIBILITY OF SUCH DAMAGE.
38- */
39-/*-
40- * Copyright (c) 1991, 1993
41- * The Regents of the University of California. All rights reserved.
42- *
43- * Redistribution and use in source and binary forms, with or without
44- * modification, are permitted provided that the following conditions
45- * are met:
46- * 1. Redistributions of source code must retain the above copyright
47- * notice, this list of conditions and the following disclaimer.
48- * 2. Redistributions in binary form must reproduce the above copyright
49- * notice, this list of conditions and the following disclaimer in the
50- * documentation and/or other materials provided with the distribution.
51- * 3. Neither the name of the University nor the names of its contributors
52- * may be used to endorse or promote products derived from this software
53- * without specific prior written permission.
54- *
55- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65- * SUCH DAMAGE.
66- */
67-
68-#include <sys/stat.h>
69-#include <sys/param.h>
70-
71-#include <ctype.h>
72-#include <err.h>
73-#include <errno.h>
74-#include <fcntl.h>
75-#include <limits.h>
76-#include <math.h>
77-#include <paths.h>
78-#include <regex.h>
79-#include <stdbool.h>
80-#include <stddef.h>
81-#include <stdint.h>
82-#include <stdio.h>
83-#include <stdlib.h>
84-#include <string.h>
85-#include <time.h>
86-#include <unistd.h>
87-#include <limits.h>
88-
89-#include "util.h"
90-
91-#include "pr.h"
92-#include "diff.h"
93-#include "xmalloc.h"
94-
95-#ifndef roundup
96-#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
97-#endif
98-
99-/*
100- * diff - compare two files.
101- */
102-
103-/*
104- * Uses an algorithm due to Harold Stone, which finds a pair of longest
105- * identical subsequences in the two files.
106- *
107- * The major goal is to generate the match vector J. J[i] is the index of
108- * the line in file1 corresponding to line i file0. J[i] = 0 if there is no
109- * such line in file1.
110- *
111- * Lines are hashed so as to work in core. All potential matches are
112- * located by sorting the lines of each file on the hash (called
113- * ``value''). In particular, this collects the equivalence classes in
114- * file1 together. Subroutine equiv replaces the value of each line in
115- * file0 by the index of the first element of its matching equivalence in
116- * (the reordered) file1. To save space equiv squeezes file1 into a single
117- * array member in which the equivalence classes are simply concatenated,
118- * except that their first members are flagged by changing sign.
119- *
120- * Next the indices that point into member are unsorted into array class
121- * according to the original order of file0.
122- *
123- * The cleverness lies in routine stone. This marches through the lines of
124- * file0, developing a vector klist of "k-candidates". At step i
125- * a k-candidate is a matched pair of lines x,y (x in file0 y in file1)
126- * such that there is a common subsequence of length k between the first
127- * i lines of file0 and the first y lines of file1, but there is no such
128- * subsequence for any smaller y. x is the earliest possible mate to y that
129- * occurs in such a subsequence.
130- *
131- * Whenever any of the members of the equivalence class of lines in file1
132- * matable to a line in file0 has serial number less than the y of some
133- * k-candidate, that k-candidate with the smallest such y is replaced. The
134- * new k-candidate is chained (via pred) to the current k-1 candidate so
135- * that the actual subsequence can be recovered. When a member has serial
136- * number greater that the y of all k-candidates, the klist is extended. At
137- * the end, the longest subsequence is pulled out and placed in the array J
138- * by unravel.
139- *
140- * With J in hand, the matches there recorded are check'ed against reality
141- * to assure that no spurious matches have crept in due to hashing. If they
142- * have, they are broken, and "jackpot" is recorded -- a harmless matter
143- * except that a true match for a spuriously mated line may now be
144- * unnecessarily reported as a change.
145- *
146- * Much of the complexity of the program comes simply from trying to
147- * minimize core utilization and maximize the range of doable problems by
148- * dynamically allocating what is needed and reusing what is not. The core
149- * requirements for problems larger than somewhat are (in words)
150- * 2*length(file0) + length(file1) + 3*(number of k-candidates installed),
151- * typically about 6n words for files of length n.
152- */
153-
154-struct cand {
155- int x;
156- int y;
157- int pred;
158-};
159-
160-static struct line {
161- int serial;
162- int value;
163-} *file[2];
164-
165-/*
166- * The following struct is used to record change information when
167- * doing a "context" or "unified" diff. (see routine "change" to
168- * understand the highly mnemonic field names)
169- */
170-struct context_vec {
171- int a; /* start line in old file */
172- int b; /* end line in old file */
173- int c; /* start line in new file */
174- int d; /* end line in new file */
175-};
176-
177-enum readhash { RH_BINARY, RH_OK, RH_EOF };
178-
179-static FILE *opentemp(const char *);
180-static void output(char *, FILE *, char *, FILE *, int);
181-static void check(FILE *, FILE *, int);
182-static void range(int, int, const char *);
183-static void uni_range(int, int);
184-static void dump_context_vec(FILE *, FILE *, int);
185-static void dump_unified_vec(FILE *, FILE *, int);
186-static bool prepare(int, FILE *, size_t, int);
187-static void prune(void);
188-static void equiv(struct line *, int, struct line *, int, int *);
189-static void unravel(int);
190-static void unsort(struct line *, int, int *);
191-static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
192-static void sort(struct line *, int);
193-static void print_header(const char *, const char *);
194-static void print_space(int, int, int);
195-static bool ignoreline_pattern(char *);
196-static bool ignoreline(char *, bool);
197-static int asciifile(FILE *);
198-static int fetch(long *, int, int, FILE *, int, int, int);
199-static int newcand(int, int, int);
200-static int search(int *, int, int);
201-static int skipline(FILE *);
202-static int stone(int *, int, int *, int *, int);
203-static enum readhash readhash(FILE *, int, unsigned *);
204-static int files_differ(FILE *, FILE *, int);
205-static char *match_function(const long *, int, FILE *);
206-static char *preadline(int, size_t, off_t);
207-
208-static int *J; /* will be overlaid on class */
209-static int *class; /* will be overlaid on file[0] */
210-static int *klist; /* will be overlaid on file[0] after class */
211-static int *member; /* will be overlaid on file[1] */
212-static int clen;
213-static int inifdef; /* whether or not we are in a #ifdef block */
214-static size_t len[2]; /* lengths of files in lines */
215-static size_t pref, suff; /* lengths of prefix and suffix */
216-static size_t slen[2]; /* lengths of files minus pref / suff */
217-static int anychange;
218-static int hw, lpad,rpad; /* half width and padding */
219-static int edoffset;
220-static long *ixnew; /* will be overlaid on file[1] */
221-static long *ixold; /* will be overlaid on klist */
222-static struct cand *clist; /* merely a free storage pot for candidates */
223-static int clistlen; /* the length of clist */
224-static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */
225-static int (*chrtran)(int); /* translation table for case-folding */
226-static struct context_vec *context_vec_start;
227-static struct context_vec *context_vec_end;
228-static struct context_vec *context_vec_ptr;
229-
230-#define FUNCTION_CONTEXT_SIZE 55
231-static char lastbuf[FUNCTION_CONTEXT_SIZE];
232-static int lastline;
233-static int lastmatchline;
234-
235-static int
236-clow2low(int c)
237-{
238-
239- return (c);
240-}
241-
242-static int
243-cup2low(int c)
244-{
245-
246- return (tolower(c));
247-}
248-
249-int
250-diffreg(char *file1, char *file2, int flags, int capsicum)
251-{
252- FILE *f1, *f2;
253- int i, rval;
254- struct pr *pr = NULL;
255-
256- f1 = f2 = NULL;
257- rval = D_SAME;
258- anychange = 0;
259- lastline = 0;
260- lastmatchline = 0;
261-
262- /*
263- * In side-by-side mode, we need to print the left column, a
264- * change marker surrounded by padding, and the right column.
265- *
266- * If expanding tabs, we don't care about alignment, so we simply
267- * subtract 3 from the width and divide by two.
268- *
269- * If not expanding tabs, we need to ensure that the right column
270- * is aligned to a tab stop. We start with the same formula, then
271- * decrement until we reach a size that lets us tab-align the
272- * right column. We then adjust the width down if necessary for
273- * the padding calculation to work.
274- *
275- * Left padding is half the space left over, rounded down; right
276- * padding is whatever is needed to match the width.
277- */
278- if (diff_format == D_SIDEBYSIDE) {
279- if (flags & D_EXPANDTABS) {
280- if (width > 3) {
281- hw = (width - 3) / 2;
282- } else {
283- /* not enough space */
284- hw = 0;
285- }
286- } else if (width <= 3 || width <= tabsize) {
287- /* not enough space */
288- hw = 0;
289- } else {
290- hw = (width - 3) / 2;
291- while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
292- hw--;
293- if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
294- width = roundup(hw + 3, tabsize) + hw;
295- }
296- lpad = (width - hw * 2 - 1) / 2;
297- rpad = (width - hw * 2 - 1) - lpad;
298- }
299-
300- if (flags & D_IGNORECASE)
301- chrtran = cup2low;
302- else
303- chrtran = clow2low;
304- if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
305- return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
306- if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
307- goto closem;
308-
309- if (flags & D_EMPTY1)
310- f1 = fopen(_PATH_DEVNULL, "r");
311- else {
312- if (!S_ISREG(stb1.st_mode)) {
313- if ((f1 = opentemp(file1)) == NULL ||
314- fstat(fileno(f1), &stb1) == -1) {
315- warn("%s", file1);
316- rval = D_ERROR;
317- status |= 2;
318- goto closem;
319- }
320- } else if (strcmp(file1, "-") == 0)
321- f1 = stdin;
322- else
323- f1 = fopen(file1, "r");
324- }
325- if (f1 == NULL) {
326- warn("%s", file1);
327- rval = D_ERROR;
328- status |= 2;
329- goto closem;
330- }
331-
332- if (flags & D_EMPTY2)
333- f2 = fopen(_PATH_DEVNULL, "r");
334- else {
335- if (!S_ISREG(stb2.st_mode)) {
336- if ((f2 = opentemp(file2)) == NULL ||
337- fstat(fileno(f2), &stb2) == -1) {
338- warn("%s", file2);
339- rval = D_ERROR;
340- status |= 2;
341- goto closem;
342- }
343- } else if (strcmp(file2, "-") == 0)
344- f2 = stdin;
345- else
346- f2 = fopen(file2, "r");
347- }
348- if (f2 == NULL) {
349- warn("%s", file2);
350- rval = D_ERROR;
351- status |= 2;
352- goto closem;
353- }
354-
355- if (lflag)
356- pr = start_pr(file1, file2);
357-
358- (void)capsicum;
359-
360- switch (files_differ(f1, f2, flags)) {
361- case 0:
362- goto closem;
363- case 1:
364- break;
365- default:
366- /* error */
367- rval = D_ERROR;
368- status |= 2;
369- goto closem;
370- }
371-
372- if (diff_format == D_BRIEF && ignore_pats == NULL &&
373- (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|
374- D_SKIPBLANKLINES|D_STRIPCR)) == 0)
375- {
376- rval = D_DIFFER;
377- status |= 1;
378- goto closem;
379- }
380- if ((flags & D_FORCEASCII) != 0) {
381- (void)prepare(0, f1, stb1.st_size, flags);
382- (void)prepare(1, f2, stb2.st_size, flags);
383- } else if (!asciifile(f1) || !asciifile(f2) ||
384- !prepare(0, f1, stb1.st_size, flags) ||
385- !prepare(1, f2, stb2.st_size, flags)) {
386- rval = D_BINARY;
387- status |= 1;
388- goto closem;
389- }
390- if (len[0] > INT_MAX - 2)
391- errno = EFBIG, err(1, "%s", file1);
392- if (len[1] > INT_MAX - 2)
393- errno = EFBIG, err(1, "%s", file2);
394-
395- prune();
396- sort(sfile[0], slen[0]);
397- sort(sfile[1], slen[1]);
398-
399- member = (int *)file[1];
400- equiv(sfile[0], slen[0], sfile[1], slen[1], member);
401- member = xreallocarray(member, slen[1] + 2, sizeof(*member));
402-
403- class = (int *)file[0];
404- unsort(sfile[0], slen[0], class);
405- class = xreallocarray(class, slen[0] + 2, sizeof(*class));
406-
407- klist = xcalloc(slen[0] + 2, sizeof(*klist));
408- clen = 0;
409- clistlen = 100;
410- clist = xcalloc(clistlen, sizeof(*clist));
411- i = stone(class, slen[0], member, klist, flags);
412- free(member);
413- free(class);
414-
415- J = xreallocarray(J, len[0] + 2, sizeof(*J));
416- unravel(klist[i]);
417- free(clist);
418- free(klist);
419-
420- ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
421- ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
422- check(f1, f2, flags);
423- output(file1, f1, file2, f2, flags);
424-
425-closem:
426- if (pr != NULL)
427- stop_pr(pr);
428- if (anychange) {
429- status |= 1;
430- if (rval == D_SAME)
431- rval = D_DIFFER;
432- }
433- if (f1 != NULL)
434- fclose(f1);
435- if (f2 != NULL)
436- fclose(f2);
437-
438- return (rval);
439-}
440-
441-/*
442- * Check to see if the given files differ.
443- * Returns 0 if they are the same, 1 if different, and -1 on error.
444- * XXX - could use code from cmp(1) [faster]
445- */
446-static int
447-files_differ(FILE *f1, FILE *f2, int flags)
448-{
449- char buf1[BUFSIZ], buf2[BUFSIZ];
450- size_t i, j;
451-
452- if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
453- (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
454- return (1);
455-
456- if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
457- return (0);
458-
459- for (;;) {
460- i = fread(buf1, 1, sizeof(buf1), f1);
461- j = fread(buf2, 1, sizeof(buf2), f2);
462- if ((!i && ferror(f1)) || (!j && ferror(f2)))
463- return (-1);
464- if (i != j)
465- return (1);
466- if (i == 0)
467- return (0);
468- if (memcmp(buf1, buf2, i) != 0)
469- return (1);
470- }
471-}
472-
473-static FILE *
474-opentemp(const char *f)
475-{
476- char buf[BUFSIZ], tempfile[PATH_MAX];
477- ssize_t nread;
478- int ifd, ofd;
479-
480- if (strcmp(f, "-") == 0)
481- ifd = STDIN_FILENO;
482- else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
483- return (NULL);
484-
485- (void)strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
486-
487- if ((ofd = mkstemp(tempfile)) == -1) {
488- close(ifd);
489- return (NULL);
490- }
491- unlink(tempfile);
492- while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
493- if (write(ofd, buf, nread) != nread) {
494- close(ifd);
495- close(ofd);
496- return (NULL);
497- }
498- }
499- close(ifd);
500- lseek(ofd, (off_t)0, SEEK_SET);
501- return (fdopen(ofd, "r"));
502-}
503-
504-static bool
505-prepare(int i, FILE *fd, size_t filesize, int flags)
506-{
507- struct line *p;
508- unsigned h;
509- size_t sz, j = 0;
510- enum readhash r;
511-
512- rewind(fd);
513-
514- sz = MIN(filesize, SIZE_MAX) / 25;
515- if (sz < 100)
516- sz = 100;
517-
518- p = xcalloc(sz + 3, sizeof(*p));
519- while ((r = readhash(fd, flags, &h)) != RH_EOF) {
520- if (r == RH_BINARY)
521- return (false);
522- if (j == SIZE_MAX)
523- break;
524- if (j == sz) {
525- sz = sz * 3 / 2;
526- p = xreallocarray(p, sz + 3, sizeof(*p));
527- }
528- p[++j].value = h;
529- }
530-
531- len[i] = j;
532- file[i] = p;
533-
534- return (true);
535-}
536-
537-static void
538-prune(void)
539-{
540- size_t i, j;
541-
542- for (pref = 0; pref < len[0] && pref < len[1] &&
543- file[0][pref + 1].value == file[1][pref + 1].value;
544- pref++)
545- ;
546- for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
547- file[0][len[0] - suff].value == file[1][len[1] - suff].value;
548- suff++)
549- ;
550- for (j = 0; j < 2; j++) {
551- sfile[j] = file[j] + pref;
552- slen[j] = len[j] - pref - suff;
553- for (i = 0; i <= slen[j]; i++)
554- sfile[j][i].serial = i;
555- }
556-}
557-
558-static void
559-equiv(struct line *a, int n, struct line *b, int m, int *c)
560-{
561- int i, j;
562-
563- i = j = 1;
564- while (i <= n && j <= m) {
565- if (a[i].value < b[j].value)
566- a[i++].value = 0;
567- else if (a[i].value == b[j].value)
568- a[i++].value = j;
569- else
570- j++;
571- }
572- while (i <= n)
573- a[i++].value = 0;
574- b[m + 1].value = 0;
575- j = 0;
576- while (++j <= m) {
577- c[j] = -b[j].serial;
578- while (b[j + 1].value == b[j].value) {
579- j++;
580- c[j] = b[j].serial;
581- }
582- }
583- c[j] = -1;
584-}
585-
586-static int
587-stone(int *a, int n, int *b, int *c, int flags)
588-{
589- int i, k, y, j, l;
590- int oldc, tc, oldl, sq;
591- unsigned numtries, bound;
592-
593- if (flags & D_MINIMAL)
594- bound = UINT_MAX;
595- else {
596- sq = sqrt(n);
597- bound = MAX(256, sq);
598- }
599-
600- k = 0;
601- c[0] = newcand(0, 0, 0);
602- for (i = 1; i <= n; i++) {
603- j = a[i];
604- if (j == 0)
605- continue;
606- y = -b[j];
607- oldl = 0;
608- oldc = c[0];
609- numtries = 0;
610- do {
611- if (y <= clist[oldc].y)
612- continue;
613- l = search(c, k, y);
614- if (l != oldl + 1)
615- oldc = c[l - 1];
616- if (l <= k) {
617- if (clist[c[l]].y <= y)
618- continue;
619- tc = c[l];
620- c[l] = newcand(i, y, oldc);
621- oldc = tc;
622- oldl = l;
623- numtries++;
624- } else {
625- c[l] = newcand(i, y, oldc);
626- k++;
627- break;
628- }
629- } while ((y = b[++j]) > 0 && numtries < bound);
630- }
631- return (k);
632-}
633-
634-static int
635-newcand(int x, int y, int pred)
636-{
637- struct cand *q;
638-
639- if (clen == clistlen) {
640- clistlen = clistlen * 11 / 10;
641- clist = xreallocarray(clist, clistlen, sizeof(*clist));
642- }
643- q = clist + clen;
644- q->x = x;
645- q->y = y;
646- q->pred = pred;
647- return (clen++);
648-}
649-
650-static int
651-search(int *c, int k, int y)
652-{
653- int i, j, l, t;
654-
655- if (clist[c[k]].y < y) /* quick look for typical case */
656- return (k + 1);
657- i = 0;
658- j = k + 1;
659- for (;;) {
660- l = (i + j) / 2;
661- if (l <= i)
662- break;
663- t = clist[c[l]].y;
664- if (t > y)
665- j = l;
666- else if (t < y)
667- i = l;
668- else
669- return (l);
670- }
671- return (l + 1);
672-}
673-
674-static void
675-unravel(int p)
676-{
677- struct cand *q;
678- size_t i;
679-
680- for (i = 0; i <= len[0]; i++)
681- J[i] = i <= pref ? i :
682- i > len[0] - suff ? i + len[1] - len[0] : 0;
683- for (q = clist + p; q->y != 0; q = clist + q->pred)
684- J[q->x + pref] = q->y + pref;
685-}
686-
687-/*
688- * Check does double duty:
689- * 1. ferret out any fortuitous correspondences due to confounding by
690- * hashing (which result in "jackpot")
691- * 2. collect random access indexes to the two files
692- */
693-static void
694-check(FILE *f1, FILE *f2, int flags)
695-{
696- int i, j, /* jackpot, */ c, d;
697- long ctold, ctnew;
698-
699- rewind(f1);
700- rewind(f2);
701- j = 1;
702- ixold[0] = ixnew[0] = 0;
703- /* jackpot = 0; */
704- ctold = ctnew = 0;
705- for (i = 1; i <= (int)len[0]; i++) {
706- if (J[i] == 0) {
707- ixold[i] = ctold += skipline(f1);
708- continue;
709- }
710- while (j < J[i]) {
711- ixnew[j] = ctnew += skipline(f2);
712- j++;
713- }
714- if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) {
715- for (;;) {
716- c = getc(f1);
717- d = getc(f2);
718- /*
719- * GNU diff ignores a missing newline
720- * in one file for -b or -w.
721- */
722- if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) {
723- if (c == EOF && isspace(d)) {
724- ctnew++;
725- break;
726- } else if (isspace(c) && d == EOF) {
727- ctold++;
728- break;
729- }
730- }
731- ctold++;
732- ctnew++;
733- if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
734- if (c == '\r') {
735- if ((c = getc(f1)) == '\n') {
736- ctold++;
737- } else {
738- ungetc(c, f1);
739- }
740- }
741- if (d == '\r') {
742- if ((d = getc(f2)) == '\n') {
743- ctnew++;
744- } else {
745- ungetc(d, f2);
746- }
747- }
748- break;
749- }
750- if ((flags & D_FOLDBLANKS) && isspace(c) &&
751- isspace(d)) {
752- do {
753- if (c == '\n')
754- break;
755- ctold++;
756- } while (isspace(c = getc(f1)));
757- do {
758- if (d == '\n')
759- break;
760- ctnew++;
761- } while (isspace(d = getc(f2)));
762- } else if (flags & D_IGNOREBLANKS) {
763- while (isspace(c) && c != '\n') {
764- c = getc(f1);
765- ctold++;
766- }
767- while (isspace(d) && d != '\n') {
768- d = getc(f2);
769- ctnew++;
770- }
771- }
772- if (chrtran(c) != chrtran(d)) {
773- /* jackpot++; */
774- J[i] = 0;
775- if (c != '\n' && c != EOF)
776- ctold += skipline(f1);
777- if (d != '\n' && c != EOF)
778- ctnew += skipline(f2);
779- break;
780- }
781- if (c == '\n' || c == EOF)
782- break;
783- }
784- } else {
785- for (;;) {
786- ctold++;
787- ctnew++;
788- if ((c = getc(f1)) != (d = getc(f2))) {
789- /* jackpot++; */
790- J[i] = 0;
791- if (c != '\n' && c != EOF)
792- ctold += skipline(f1);
793- if (d != '\n' && c != EOF)
794- ctnew += skipline(f2);
795- break;
796- }
797- if (c == '\n' || c == EOF)
798- break;
799- }
800- }
801- ixold[i] = ctold;
802- ixnew[j] = ctnew;
803- j++;
804- }
805- for (; j <= (int)len[1]; j++) {
806- ixnew[j] = ctnew += skipline(f2);
807- }
808- /*
809- * if (jackpot)
810- * fprintf(stderr, "jackpot\n");
811- */
812-}
813-
814-/* shellsort CACM #201 */
815-static void
816-sort(struct line *a, int n)
817-{
818- struct line *ai, *aim, w;
819- int j, m = 0, k;
820-
821- if (n == 0)
822- return;
823- for (j = 1; j <= n; j *= 2)
824- m = 2 * j - 1;
825- for (m /= 2; m != 0; m /= 2) {
826- k = n - m;
827- for (j = 1; j <= k; j++) {
828- for (ai = &a[j]; ai > a; ai -= m) {
829- aim = &ai[m];
830- if (aim < ai)
831- break; /* wraparound */
832- if (aim->value > ai[0].value ||
833- (aim->value == ai[0].value &&
834- aim->serial > ai[0].serial))
835- break;
836- w.value = ai[0].value;
837- ai[0].value = aim->value;
838- aim->value = w.value;
839- w.serial = ai[0].serial;
840- ai[0].serial = aim->serial;
841- aim->serial = w.serial;
842- }
843- }
844- }
845-}
846-
847-static void
848-unsort(struct line *f, int l, int *b)
849-{
850- int *a, i;
851-
852- a = xcalloc(l + 1, sizeof(*a));
853- for (i = 1; i <= l; i++)
854- a[f[i].serial] = f[i].value;
855- for (i = 1; i <= l; i++)
856- b[i] = a[i];
857- free(a);
858-}
859-
860-static int
861-skipline(FILE *f)
862-{
863- int i, c;
864-
865- for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
866- continue;
867- return (i);
868-}
869-
870-static void
871-output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
872-{
873- int i, j, m, i0, i1, j0, j1, nc;
874-
875- rewind(f1);
876- rewind(f2);
877- m = len[0];
878- J[0] = 0;
879- J[m + 1] = len[1] + 1;
880- if (diff_format != D_EDIT) {
881- for (i0 = 1; i0 <= m; i0 = i1 + 1) {
882- while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
883- if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
884- nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
885- print_space(nc, hw - nc + lpad + 1 + rpad, flags);
886- fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
887- printf("\n");
888- }
889- i0++;
890- }
891- j0 = J[i0 - 1] + 1;
892- i1 = i0 - 1;
893- while (i1 < m && J[i1 + 1] == 0)
894- i1++;
895- j1 = J[i1 + 1] - 1;
896- J[i1] = j1;
897-
898- /*
899- * When using side-by-side, lines from both of the files are
900- * printed. The algorithm used by diff(1) identifies the ranges
901- * in which two files differ.
902- * See the change() function below.
903- * The for loop below consumes the shorter range, whereas one of
904- * the while loops deals with the longer one.
905- */
906- if (diff_format == D_SIDEBYSIDE) {
907- for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++)
908- change(file1, f1, file2, f2, i, i, j, j, &flags);
909-
910- while (i <= i1) {
911- change(file1, f1, file2, f2, i, i, j + 1, j, &flags);
912- i++;
913- }
914-
915- while (j <= j1) {
916- change(file1, f1, file2, f2, i + 1, i, j, j, &flags);
917- j++;
918- }
919- } else
920- change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
921- }
922- } else {
923- for (i0 = m; i0 >= 1; i0 = i1 - 1) {
924- while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
925- i0--;
926- j0 = J[i0 + 1] - 1;
927- i1 = i0 + 1;
928- while (i1 > 1 && J[i1 - 1] == 0)
929- i1--;
930- j1 = J[i1 - 1] + 1;
931- J[i1] = j1;
932- change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
933- }
934- }
935- if (m == 0)
936- change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
937- if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
938- for (;;) {
939-#define c i0
940- if ((c = getc(f1)) == EOF)
941- return;
942- printf("%c", c);
943- }
944-#undef c
945- }
946- if (anychange != 0) {
947- if (diff_format == D_CONTEXT)
948- dump_context_vec(f1, f2, flags);
949- else if (diff_format == D_UNIFIED)
950- dump_unified_vec(f1, f2, flags);
951- }
952-}
953-
954-static void
955-range(int a, int b, const char *separator)
956-{
957- printf("%d", a > b ? b : a);
958- if (a < b)
959- printf("%s%d", separator, b);
960-}
961-
962-static void
963-uni_range(int a, int b)
964-{
965- if (a < b)
966- printf("%d,%d", a, b - a + 1);
967- else if (a == b)
968- printf("%d", b);
969- else
970- printf("%d,0", b);
971-}
972-
973-static char *
974-preadline(int fd, size_t rlen, off_t off)
975-{
976- char *line;
977- ssize_t nr;
978-
979- line = xmalloc(rlen + 1);
980- if ((nr = pread(fd, line, rlen, off)) == -1)
981- err(2, "preadline");
982- if (nr > 0 && line[nr-1] == '\n')
983- nr--;
984- line[nr] = '\0';
985- return (line);
986-}
987-
988-static bool
989-ignoreline_pattern(char *line)
990-{
991- int ret;
992-
993- ret = regexec(&ignore_re, line, 0, NULL, 0);
994- return (ret == 0); /* if it matched, it should be ignored. */
995-}
996-
997-static bool
998-ignoreline(char *line, bool skip_blanks)
999-{
1000-
1001- if (skip_blanks && *line == '\0')
1002- return (true);
1003- if (ignore_pats != NULL && ignoreline_pattern(line))
1004- return (true);
1005- return (false);
1006-}
1007-
1008-/*
1009- * Indicate that there is a difference between lines a and b of the from file
1010- * to get to lines c to d of the to file. If a is greater then b then there
1011- * are no lines in the from file involved and this means that there were
1012- * lines appended (beginning at b). If c is greater than d then there are
1013- * lines missing from the to file.
1014- */
1015-static void
1016-change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
1017- int *pflags)
1018-{
1019- static size_t max_context = 64;
1020- long curpos;
1021- int i, nc;
1022- const char *walk;
1023- bool skip_blanks, ignore;
1024-
1025- skip_blanks = (*pflags & D_SKIPBLANKLINES);
1026-restart:
1027- if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) &&
1028- a > b && c > d)
1029- return;
1030- if (ignore_pats != NULL || skip_blanks) {
1031- char *line;
1032- /*
1033- * All lines in the change, insert, or delete must match an ignore
1034- * pattern for the change to be ignored.
1035- */
1036- if (a <= b) { /* Changes and deletes. */
1037- for (i = a; i <= b; i++) {
1038- line = preadline(fileno(f1),
1039- ixold[i] - ixold[i - 1], ixold[i - 1]);
1040- ignore = ignoreline(line, skip_blanks);
1041- free(line);
1042- if (!ignore)
1043- goto proceed;
1044- }
1045- }
1046- if (a > b || c <= d) { /* Changes and inserts. */
1047- for (i = c; i <= d; i++) {
1048- line = preadline(fileno(f2),
1049- ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1050- ignore = ignoreline(line, skip_blanks);
1051- free(line);
1052- if (!ignore)
1053- goto proceed;
1054- }
1055- }
1056- return;
1057- }
1058-proceed:
1059- if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1060- printf("%s %s %s\n", diffargs, file1, file2);
1061- *pflags &= ~D_HEADER;
1062- }
1063- if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1064- /*
1065- * Allocate change records as needed.
1066- */
1067- if (context_vec_start == NULL ||
1068- context_vec_ptr == context_vec_end - 1) {
1069- ptrdiff_t offset = -1;
1070-
1071- if (context_vec_start != NULL)
1072- offset = context_vec_ptr - context_vec_start;
1073- max_context <<= 1;
1074- context_vec_start = xreallocarray(context_vec_start,
1075- max_context, sizeof(*context_vec_start));
1076- context_vec_end = context_vec_start + max_context;
1077- context_vec_ptr = context_vec_start + offset;
1078- }
1079- if (anychange == 0) {
1080- /*
1081- * Print the context/unidiff header first time through.
1082- */
1083- print_header(file1, file2);
1084- anychange = 1;
1085- } else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
1086- c > context_vec_ptr->d + (2 * diff_context) + 1) {
1087- /*
1088- * If this change is more than 'diff_context' lines from the
1089- * previous change, dump the record and reset it.
1090- */
1091- if (diff_format == D_CONTEXT)
1092- dump_context_vec(f1, f2, *pflags);
1093- else
1094- dump_unified_vec(f1, f2, *pflags);
1095- }
1096- context_vec_ptr++;
1097- context_vec_ptr->a = a;
1098- context_vec_ptr->b = b;
1099- context_vec_ptr->c = c;
1100- context_vec_ptr->d = d;
1101- return;
1102- }
1103- if (anychange == 0)
1104- anychange = 1;
1105- switch (diff_format) {
1106- case D_BRIEF:
1107- return;
1108- case D_NORMAL:
1109- case D_EDIT:
1110- range(a, b, ",");
1111- printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1112- if (diff_format == D_NORMAL)
1113- range(c, d, ",");
1114- printf("\n");
1115- break;
1116- case D_REVERSE:
1117- printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1118- range(a, b, " ");
1119- printf("\n");
1120- break;
1121- case D_NREVERSE:
1122- if (a > b)
1123- printf("a%d %d\n", b, d - c + 1);
1124- else {
1125- printf("d%d %d\n", a, b - a + 1);
1126- if (!(c > d))
1127- /* add changed lines */
1128- printf("a%d %d\n", b, d - c + 1);
1129- }
1130- break;
1131- }
1132- if (diff_format == D_GFORMAT) {
1133- curpos = ftell(f1);
1134- /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1135- nc = ixold[a > b ? b : a - 1] - curpos;
1136- for (i = 0; i < nc; i++)
1137- printf("%c", getc(f1));
1138- for (walk = group_format; *walk != '\0'; walk++) {
1139- if (*walk == '%') {
1140- walk++;
1141- switch (*walk) {
1142- case '<':
1143- fetch(ixold, a, b, f1, '<', 1, *pflags);
1144- break;
1145- case '>':
1146- fetch(ixnew, c, d, f2, '>', 0, *pflags);
1147- break;
1148- default:
1149- printf("%%%c", *walk);
1150- break;
1151- }
1152- continue;
1153- }
1154- printf("%c", *walk);
1155- }
1156- }
1157- if (diff_format == D_SIDEBYSIDE) {
1158- if (color && a > b)
1159- printf("\033[%sm", add_code);
1160- else if (color && c > d)
1161- printf("\033[%sm", del_code);
1162- if (a > b) {
1163- print_space(0, hw + lpad, *pflags);
1164- } else {
1165- nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
1166- print_space(nc, hw - nc + lpad, *pflags);
1167- }
1168- if (color && a > b)
1169- printf("\033[%sm", add_code);
1170- else if (color && c > d)
1171- printf("\033[%sm", del_code);
1172- printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
1173- if (color && c > d)
1174- printf("\033[m");
1175- print_space(hw + lpad + 1, rpad, *pflags);
1176- fetch(ixnew, c, d, f2, '\0', 0, *pflags);
1177- printf("\n");
1178- }
1179- if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1180- fetch(ixold, a, b, f1, '<', 1, *pflags);
1181- if (a <= b && c <= d && diff_format == D_NORMAL)
1182- printf("---\n");
1183- }
1184- if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
1185- fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
1186- if (edoffset != 0 && diff_format == D_EDIT) {
1187- /*
1188- * A non-zero edoffset value for D_EDIT indicates that the last line
1189- * printed was a bare dot (".") that has been escaped as ".." to
1190- * prevent ed(1) from misinterpreting it. We have to add a
1191- * substitute command to change this back and restart where we left
1192- * off.
1193- */
1194- printf(".\n");
1195- printf("%ds/.//\n", a + edoffset - 1);
1196- b = a + edoffset - 1;
1197- a = b + 1;
1198- c += edoffset;
1199- goto restart;
1200- }
1201- if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
1202- printf(".\n");
1203- if (inifdef) {
1204- printf("#endif /* %s */\n", ifdefname);
1205- inifdef = 0;
1206- }
1207-}
1208-
1209-static int
1210-fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
1211-{
1212- int i, j, c, lastc, col, nc, newcol;
1213-
1214- edoffset = 0;
1215- nc = 0;
1216- col = 0;
1217- /*
1218- * When doing #ifdef's, copy down to current line
1219- * if this is the first file, so that stuff makes it to output.
1220- */
1221- if ((diff_format == D_IFDEF) && oldfile) {
1222- long curpos = ftell(lb);
1223- /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1224- nc = f[a > b ? b : a - 1] - curpos;
1225- for (i = 0; i < nc; i++)
1226- printf("%c", getc(lb));
1227- }
1228- if (a > b)
1229- return (0);
1230- if (diff_format == D_IFDEF) {
1231- if (inifdef) {
1232- printf("#else /* %s%s */\n",
1233- oldfile == 1 ? "!" : "", ifdefname);
1234- } else {
1235- if (oldfile)
1236- printf("#ifndef %s\n", ifdefname);
1237- else
1238- printf("#ifdef %s\n", ifdefname);
1239- }
1240- inifdef = 1 + oldfile;
1241- }
1242- for (i = a; i <= b; i++) {
1243- fseek(lb, f[i - 1], SEEK_SET);
1244- nc = f[i] - f[i - 1];
1245- if (diff_format == D_SIDEBYSIDE && hw < nc)
1246- nc = hw;
1247- if (diff_format != D_IFDEF && diff_format != D_GFORMAT &&
1248- ch != '\0') {
1249- if (color && (ch == '>' || ch == '+'))
1250- printf("\033[%sm", add_code);
1251- else if (color && (ch == '<' || ch == '-'))
1252- printf("\033[%sm", del_code);
1253- printf("%c", ch);
1254- if (Tflag && (diff_format == D_NORMAL ||
1255- diff_format == D_CONTEXT ||
1256- diff_format == D_UNIFIED))
1257- printf("\t");
1258- else if (diff_format != D_UNIFIED)
1259- printf(" ");
1260- }
1261- col = j = 0;
1262- lastc = '\0';
1263- while (j < nc && (hw == 0 || col < hw)) {
1264- c = getc(lb);
1265- if (flags & D_STRIPCR && c == '\r') {
1266- if ((c = getc(lb)) == '\n')
1267- j++;
1268- else {
1269- ungetc(c, lb);
1270- c = '\r';
1271- }
1272- }
1273- if (c == EOF) {
1274- if (diff_format == D_EDIT ||
1275- diff_format == D_REVERSE ||
1276- diff_format == D_NREVERSE)
1277- warnx("No newline at end of file");
1278- else
1279- printf("\n\\ No newline at end of file\n");
1280- return (col);
1281- }
1282- /*
1283- * when using --side-by-side, col needs to be increased
1284- * in any case to keep the columns aligned
1285- */
1286- if (c == '\t') {
1287- /*
1288- * Calculate where the tab would bring us.
1289- * If it would take us to the end of the
1290- * column, either clip it (if expanding
1291- * tabs) or return right away (if not).
1292- */
1293- newcol = roundup(col + 1, tabsize);
1294- if ((flags & D_EXPANDTABS) == 0) {
1295- if (hw > 0 && newcol >= hw)
1296- return (col);
1297- printf("\t");
1298- } else {
1299- if (hw > 0 && newcol > hw)
1300- newcol = hw;
1301- printf("%*s", newcol - col, "");
1302- }
1303- col = newcol;
1304- } else {
1305- if (diff_format == D_EDIT && j == 1 && c == '\n' &&
1306- lastc == '.') {
1307- /*
1308- * Don't print a bare "." line since that will confuse
1309- * ed(1). Print ".." instead and set the, global variable
1310- * edoffset to an offset from which to restart. The
1311- * caller must check the value of edoffset
1312- */
1313- printf(".\n");
1314- edoffset = i - a + 1;
1315- return (edoffset);
1316- }
1317- /* when side-by-side, do not print a newline */
1318- if (diff_format != D_SIDEBYSIDE || c != '\n') {
1319- if (color && c == '\n')
1320- printf("\033[m%c", c);
1321- else
1322- printf("%c", c);
1323- col++;
1324- }
1325- }
1326-
1327- j++;
1328- lastc = c;
1329- }
1330- }
1331- if (color && diff_format == D_SIDEBYSIDE)
1332- printf("\033[m");
1333- return (col);
1334-}
1335-
1336-/*
1337- * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
1338- */
1339-static enum readhash
1340-readhash(FILE *f, int flags, unsigned *hash)
1341-{
1342- int i, t, space;
1343- unsigned sum;
1344-
1345- sum = 1;
1346- space = 0;
1347- for (i = 0;;) {
1348- switch (t = getc(f)) {
1349- case '\0':
1350- if ((flags & D_FORCEASCII) == 0)
1351- return (RH_BINARY);
1352- goto hashchar;
1353- case '\r':
1354- if (flags & D_STRIPCR) {
1355- t = getc(f);
1356- if (t == '\n')
1357- break;
1358- ungetc(t, f);
1359- }
1360- /* FALLTHROUGH */
1361- case '\t':
1362- case '\v':
1363- case '\f':
1364- case ' ':
1365- if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) {
1366- space++;
1367- continue;
1368- }
1369- /* FALLTHROUGH */
1370- default:
1371- hashchar:
1372- if (space && (flags & D_IGNOREBLANKS) == 0) {
1373- i++;
1374- space = 0;
1375- }
1376- sum = sum * 127 + chrtran(t);
1377- i++;
1378- continue;
1379- case EOF:
1380- if (i == 0)
1381- return (RH_EOF);
1382- /* FALLTHROUGH */
1383- case '\n':
1384- break;
1385- }
1386- break;
1387- }
1388- *hash = sum;
1389- return (RH_OK);
1390-}
1391-
1392-static int
1393-asciifile(FILE *f)
1394-{
1395- unsigned char buf[BUFSIZ];
1396- size_t cnt;
1397-
1398- if (f == NULL)
1399- return (1);
1400-
1401- rewind(f);
1402- cnt = fread(buf, 1, sizeof(buf), f);
1403- return (memchr(buf, '\0', cnt) == NULL);
1404-}
1405-
1406-#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0)
1407-
1408-static char *
1409-match_function(const long *f, int pos, FILE *fp)
1410-{
1411- char buf[FUNCTION_CONTEXT_SIZE];
1412- size_t nc;
1413- int last = lastline;
1414- const char *state = NULL;
1415-
1416- lastline = pos;
1417- for (; pos > last; pos--) {
1418- fseek(fp, f[pos - 1], SEEK_SET);
1419- nc = f[pos] - f[pos - 1];
1420- if (nc >= sizeof(buf))
1421- nc = sizeof(buf) - 1;
1422- nc = fread(buf, 1, nc, fp);
1423- if (nc == 0)
1424- continue;
1425- buf[nc] = '\0';
1426- buf[strcspn(buf, "\n")] = '\0';
1427- if (most_recent_pat != NULL) {
1428- int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
1429-
1430- if (ret != 0)
1431- continue;
1432- strlcpy(lastbuf, buf, sizeof(lastbuf));
1433- lastmatchline = pos;
1434- return (lastbuf);
1435- } else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$'
1436- || buf[0] == '-' || buf[0] == '+') {
1437- if (begins_with(buf, "private:")) {
1438- if (!state)
1439- state = " (private)";
1440- } else if (begins_with(buf, "protected:")) {
1441- if (!state)
1442- state = " (protected)";
1443- } else if (begins_with(buf, "public:")) {
1444- if (!state)
1445- state = " (public)";
1446- } else {
1447- strlcpy(lastbuf, buf, sizeof(lastbuf));
1448- if (state)
1449- strlcat(lastbuf, state, sizeof(lastbuf));
1450- lastmatchline = pos;
1451- return (lastbuf);
1452- }
1453- }
1454- }
1455- return (lastmatchline > 0 ? lastbuf : NULL);
1456-}
1457-
1458-/* dump accumulated "context" diff changes */
1459-static void
1460-dump_context_vec(FILE *f1, FILE *f2, int flags)
1461-{
1462- struct context_vec *cvp = context_vec_start;
1463- int lowa, upb, lowc, upd, do_output;
1464- int a, b, c, d;
1465- char ch, *f;
1466-
1467- if (context_vec_start > context_vec_ptr)
1468- return;
1469-
1470- b = d = 0; /* gcc */
1471- lowa = MAX(1, cvp->a - diff_context);
1472- upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
1473- lowc = MAX(1, cvp->c - diff_context);
1474- upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
1475-
1476- printf("***************");
1477- if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
1478- f = match_function(ixold, cvp->a - 1, f1);
1479- if (f != NULL)
1480- printf(" %s", f);
1481- }
1482- printf("\n*** ");
1483- range(lowa, upb, ",");
1484- printf(" ****\n");
1485-
1486- /*
1487- * Output changes to the "old" file. The first loop suppresses
1488- * output if there were no changes to the "old" file (we'll see
1489- * the "old" lines as context in the "new" list).
1490- */
1491- do_output = 0;
1492- for (; cvp <= context_vec_ptr; cvp++)
1493- if (cvp->a <= cvp->b) {
1494- cvp = context_vec_start;
1495- do_output++;
1496- break;
1497- }
1498- if (do_output) {
1499- while (cvp <= context_vec_ptr) {
1500- a = cvp->a;
1501- b = cvp->b;
1502- c = cvp->c;
1503- d = cvp->d;
1504-
1505- if (a <= b && c <= d)
1506- ch = 'c';
1507- else
1508- ch = (a <= b) ? 'd' : 'a';
1509-
1510- if (ch == 'a')
1511- fetch(ixold, lowa, b, f1, ' ', 0, flags);
1512- else {
1513- fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1514- fetch(ixold, a, b, f1,
1515- ch == 'c' ? '!' : '-', 0, flags);
1516- }
1517- lowa = b + 1;
1518- cvp++;
1519- }
1520- fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
1521- }
1522- /* output changes to the "new" file */
1523- printf("--- ");
1524- range(lowc, upd, ",");
1525- printf(" ----\n");
1526-
1527- do_output = 0;
1528- for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1529- if (cvp->c <= cvp->d) {
1530- cvp = context_vec_start;
1531- do_output++;
1532- break;
1533- }
1534- if (do_output) {
1535- while (cvp <= context_vec_ptr) {
1536- a = cvp->a;
1537- b = cvp->b;
1538- c = cvp->c;
1539- d = cvp->d;
1540-
1541- if (a <= b && c <= d)
1542- ch = 'c';
1543- else
1544- ch = (a <= b) ? 'd' : 'a';
1545-
1546- if (ch == 'd')
1547- fetch(ixnew, lowc, d, f2, ' ', 0, flags);
1548- else {
1549- fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1550- fetch(ixnew, c, d, f2,
1551- ch == 'c' ? '!' : '+', 0, flags);
1552- }
1553- lowc = d + 1;
1554- cvp++;
1555- }
1556- fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1557- }
1558- context_vec_ptr = context_vec_start - 1;
1559-}
1560-
1561-/* dump accumulated "unified" diff changes */
1562-static void
1563-dump_unified_vec(FILE *f1, FILE *f2, int flags)
1564-{
1565- struct context_vec *cvp = context_vec_start;
1566- int lowa, upb, lowc, upd;
1567- int a, b, c, d;
1568- char ch, *f;
1569-
1570- if (context_vec_start > context_vec_ptr)
1571- return;
1572-
1573- b = d = 0; /* gcc */
1574- lowa = MAX(1, cvp->a - diff_context);
1575- upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
1576- lowc = MAX(1, cvp->c - diff_context);
1577- upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
1578-
1579- printf("@@ -");
1580- uni_range(lowa, upb);
1581- printf(" +");
1582- uni_range(lowc, upd);
1583- printf(" @@");
1584- if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
1585- f = match_function(ixold, cvp->a - 1, f1);
1586- if (f != NULL)
1587- printf(" %s", f);
1588- }
1589- printf("\n");
1590-
1591- /*
1592- * Output changes in "unified" diff format--the old and new lines
1593- * are printed together.
1594- */
1595- for (; cvp <= context_vec_ptr; cvp++) {
1596- a = cvp->a;
1597- b = cvp->b;
1598- c = cvp->c;
1599- d = cvp->d;
1600-
1601- /*
1602- * c: both new and old changes
1603- * d: only changes in the old file
1604- * a: only changes in the new file
1605- */
1606- if (a <= b && c <= d)
1607- ch = 'c';
1608- else
1609- ch = (a <= b) ? 'd' : 'a';
1610-
1611- switch (ch) {
1612- case 'c':
1613- fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1614- fetch(ixold, a, b, f1, '-', 0, flags);
1615- fetch(ixnew, c, d, f2, '+', 0, flags);
1616- break;
1617- case 'd':
1618- fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1619- fetch(ixold, a, b, f1, '-', 0, flags);
1620- break;
1621- case 'a':
1622- fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1623- fetch(ixnew, c, d, f2, '+', 0, flags);
1624- break;
1625- }
1626- lowa = b + 1;
1627- lowc = d + 1;
1628- }
1629- fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1630-
1631- context_vec_ptr = context_vec_start - 1;
1632-}
1633-
1634-static void
1635-print_header(const char *file1, const char *file2)
1636-{
1637- const char *time_format;
1638- char buf[256];
1639- struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
1640- int nsec1 = stb1.st_mtim.tv_nsec;
1641- int nsec2 = stb2.st_mtim.tv_nsec;
1642-
1643- time_format = "%Y-%m-%d %H:%M:%S";
1644-
1645- if (cflag)
1646- time_format = "%c";
1647- tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
1648- tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
1649- if (label[0] != NULL)
1650- printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---",
1651- label[0]);
1652- else {
1653- strftime(buf, sizeof(buf), time_format, tm_ptr1);
1654- printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---",
1655- file1, buf);
1656- if (!cflag) {
1657- strftime(buf, sizeof(buf), "%z", tm_ptr1);
1658- printf(".%.9d %s", nsec1, buf);
1659- }
1660- printf("\n");
1661- }
1662- if (label[1] != NULL)
1663- printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++",
1664- label[1]);
1665- else {
1666- strftime(buf, sizeof(buf), time_format, tm_ptr2);
1667- printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++",
1668- file2, buf);
1669- if (!cflag) {
1670- strftime(buf, sizeof(buf), "%z", tm_ptr2);
1671- printf(".%.9d %s", nsec2, buf);
1672- }
1673- printf("\n");
1674- }
1675-}
1676-
1677-/*
1678- * Prints n number of space characters either by using tab
1679- * or single space characters.
1680- * nc is the preceding number of characters
1681- */
1682-static void
1683-print_space(int nc, int n, int flags)
1684-{
1685- int col, newcol, tabstop;
1686-
1687- col = nc;
1688- newcol = nc + n;
1689- /* first, use tabs if allowed */
1690- if ((flags & D_EXPANDTABS) == 0) {
1691- while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
1692- printf("\t");
1693- col = tabstop;
1694- }
1695- }
1696- /* finish with spaces */
1697- printf("%*s", newcol - col, "");
1698-}
+0,
-155
1@@ -1,155 +0,0 @@
2-/*-
3- * Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
4- * All rights reserved.
5- *
6- * Redistribution and use in source and binary forms, with or without
7- * modification, are permitted provided that the following conditions
8- * are met:
9- * 1. Redistributions of source code must retain the above copyright
10- * notice, this list of conditions and the following disclaimer
11- * in this position and unchanged.
12- * 2. Redistributions in binary form must reproduce the above copyright
13- * notice, this list of conditions and the following disclaimer in the
14- * documentation and/or other materials provided with the distribution.
15- *
16- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26- */
27-
28-#include <sys/cdefs.h>
29-#include <sys/wait.h>
30-
31-#include <err.h>
32-#include <paths.h>
33-#include <signal.h>
34-#include <stdio.h>
35-#include <stdlib.h>
36-#include <unistd.h>
37-#include <fcntl.h>
38-#include <errno.h>
39-#include <poll.h>
40-
41-#include "util.h"
42-
43-#include "pr.h"
44-#include "diff.h"
45-#include "xmalloc.h"
46-
47-#define _PATH_PR "/usr/bin/pr"
48-
49-static int sigpipe[2] = {-1, -1};
50-static struct pollfd poll_fd;
51-
52-static void
53-handle_sig(int signo)
54-{
55- write(sigpipe[1], &signo, sizeof(signo));
56-}
57-
58-struct pr *
59-start_pr(char *file1, char *file2)
60-{
61- int pfd[2];
62- pid_t pid;
63- char *header;
64- struct pr *pr;
65-
66- pr = xcalloc(1, sizeof(*pr));
67-
68- xasprintf(&header, "%s %s %s", diffargs, file1, file2);
69- signal(SIGPIPE, SIG_IGN);
70- fflush(stdout);
71- rewind(stdout);
72- if (pipe(pfd) == -1)
73- err(2, "pipe");
74- if (sigpipe[0] < 0) {
75- if (pipe(sigpipe) == -1)
76- err(2, "pipe");
77- if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC) == -1)
78- err(2, "fcntl");
79- if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC) == -1)
80- err(2, "fcntl");
81- if (signal(SIGCHLD, handle_sig) == SIG_ERR)
82- err(2, "signal");
83- poll_fd.fd = sigpipe[0];
84- poll_fd.events = POLLIN;
85- }
86- poll_fd.revents = 0;
87- switch ((pid = fork())) {
88- case -1:
89- status |= 2;
90- free(header);
91- err(2, "No more processes");
92- case 0:
93- /* child */
94- if (pfd[0] != STDIN_FILENO) {
95- dup2(pfd[0], STDIN_FILENO);
96- close(pfd[0]);
97- }
98- close(pfd[1]);
99- execl(_PATH_PR, _PATH_PR, "-h", header, (char *)0);
100- _exit(127);
101- default:
102-
103- /* parent */
104- if (pfd[1] != STDOUT_FILENO) {
105- pr->ostdout = dup(STDOUT_FILENO);
106- dup2(pfd[1], STDOUT_FILENO);
107- close(pfd[1]);
108- }
109- close(pfd[0]);
110- rewind(stdout);
111- free(header);
112- pr->cpid = pid;
113- }
114- return (pr);
115-}
116-
117-/* close the pipe to pr and restore stdout */
118-void
119-stop_pr(struct pr *pr)
120-{
121- int wstatus;
122- int done = 0;
123-
124- if (pr == NULL)
125- return;
126-
127- fflush(stdout);
128- if (pr->ostdout != STDOUT_FILENO) {
129- close(STDOUT_FILENO);
130- dup2(pr->ostdout, STDOUT_FILENO);
131- close(pr->ostdout);
132- }
133- while (!done) {
134- pid_t wpid;
135- int npe = poll(&poll_fd, 1, -1);
136- if (npe == -1) {
137- if (errno == EINTR) continue;
138- err(2, "poll");
139- }
140- if (poll_fd.revents != POLLIN)
141- continue;
142- if (read(poll_fd.fd, &npe, sizeof(npe)) < 0)
143- err(2, "read");
144- while ((wpid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
145- if (wpid != pr->cpid) continue;
146- if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
147- errx(2, "pr exited abnormally");
148- else if (WIFSIGNALED(wstatus))
149- errx(2, "pr killed by signal %d",
150- WTERMSIG(wstatus));
151- done = 1;
152- break;
153- }
154- }
155- free(pr);
156-}
+0,
-33
1@@ -1,33 +0,0 @@
2-/*-
3- * Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
4- * All rights reserved.
5- *
6- * Redistribution and use in source and binary forms, with or without
7- * modification, are permitted provided that the following conditions
8- * are met:
9- * 1. Redistributions of source code must retain the above copyright
10- * notice, this list of conditions and the following disclaimer
11- * in this position and unchanged.
12- * 2. Redistributions in binary form must reproduce the above copyright
13- * notice, this list of conditions and the following disclaimer in the
14- * documentation and/or other materials provided with the distribution.
15- *
16- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19- * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26- */
27-
28-struct pr {
29- int ostdout;
30- pid_t cpid;
31-};
32-
33-struct pr *start_pr(char *file1, char *file2);
34-void stop_pr(struct pr *);
+0,
-96
1@@ -1,96 +0,0 @@
2-/* $OpenBSD: xmalloc.c,v 1.10 2019/06/28 05:44:09 deraadt Exp $ */
3-/*
4- * Author: Tatu Ylonen <ylo@cs.hut.fi>
5- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6- * All rights reserved
7- * Versions of malloc and friends that check their results, and never return
8- * failure (they call fatal if they encounter an error).
9- *
10- * As far as I am concerned, the code I have written for this software
11- * can be used freely for any purpose. Any derived versions of this
12- * software must be clearly marked as such, and if the derived work is
13- * incompatible with the protocol description in the RFC file, it must be
14- * called by a name other than "ssh" or "Secure Shell".
15- */
16-
17-#include <err.h>
18-#include <stdarg.h>
19-#include <stdint.h>
20-#include <stdio.h>
21-#include <stdlib.h>
22-#include <string.h>
23-
24-#include "util.h"
25-
26-#include "xmalloc.h"
27-
28-void *
29-xmalloc(size_t size)
30-{
31- void *ptr;
32-
33- if (size == 0)
34- errx(2, "xmalloc: zero size");
35- ptr = malloc(size);
36- if (ptr == NULL)
37- err(2, "xmalloc: allocating %zu bytes", size);
38- return ptr;
39-}
40-
41-void *
42-xcalloc(size_t nmemb, size_t size)
43-{
44- void *ptr;
45-
46- ptr = calloc(nmemb, size);
47- if (ptr == NULL)
48- err(2, "xcalloc: allocating %zu * %zu bytes", nmemb, size);
49- return ptr;
50-}
51-
52-void *
53-xreallocarray(void *ptr, size_t nmemb, size_t size)
54-{
55- void *new_ptr;
56-
57- new_ptr = reallocarray(ptr, nmemb, size);
58- if (new_ptr == NULL)
59- err(2, "xreallocarray: allocating %zu * %zu bytes",
60- nmemb, size);
61- return new_ptr;
62-}
63-
64-char *
65-xstrdup(const char *str)
66-{
67- char *cp;
68-
69- if ((cp = strdup(str)) == NULL)
70- err(2, "xstrdup");
71- return cp;
72-}
73-
74-int
75-xasprintf(char **ret, const char *fmt, ...)
76-{
77- va_list ap;
78- va_list ap2;
79- int i;
80- size_t n;
81-
82- va_start(ap, fmt);
83- va_copy(ap2, ap);
84- i = vsnprintf(NULL, 0, fmt, ap);
85- if (i < 0)
86- err(2, "xasprintf");
87- n = (size_t)i + 1;
88- *ret = xmalloc(n);
89- i = vsnprintf(*ret, n, fmt, ap2);
90- va_end(ap2);
91- va_end(ap);
92-
93- if (i == -1)
94- err(2, "xasprintf");
95-
96- return i;
97-}
+0,
-32
1@@ -1,32 +0,0 @@
2-/* $OpenBSD: xmalloc.h,v 1.4 2015/11/12 16:30:30 mmcc Exp $ */
3-
4-/*
5- * Author: Tatu Ylonen <ylo@cs.hut.fi>
6- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7- * All rights reserved
8- * Created: Mon Mar 20 22:09:17 1995 ylo
9- *
10- * Versions of malloc and friends that check their results, and never return
11- * failure (they call fatal if they encounter an error).
12- *
13- * As far as I am concerned, the code I have written for this software
14- * can be used freely for any purpose. Any derived versions of this
15- * software must be clearly marked as such, and if the derived work is
16- * incompatible with the protocol description in the RFC file, it must be
17- * called by a name other than "ssh" or "Secure Shell".
18- */
19-
20-#ifndef XMALLOC_H
21-#define XMALLOC_H
22-
23-#include <stddef.h>
24-
25-void *xmalloc(size_t);
26-void *xcalloc(size_t, size_t);
27-void *xreallocarray(void *, size_t, size_t);
28-char *xstrdup(const char *);
29-int xasprintf(char **, const char *, ...)
30- __attribute__((__format__ (printf, 2, 3)))
31- __attribute__((__nonnull__ (2)));
32-
33-#endif /* XMALLOC_H */
+894,
-0
1@@ -0,0 +1,894 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "util.h"
4+
5+#include <sys/types.h>
6+#include <sys/wait.h>
7+
8+#include <ctype.h>
9+#include <err.h>
10+#include <errno.h>
11+#include <fcntl.h>
12+#include <stdarg.h>
13+#include <getopt.h>
14+#include <inttypes.h>
15+#include <limits.h>
16+#include <poll.h>
17+#include <stdio.h>
18+#include <stdlib.h>
19+#include <string.h>
20+#include <unistd.h>
21+
22+struct Range {
23+ int from;
24+ int to;
25+};
26+
27+struct Diff {
28+#define DIFF_TYPE1 1
29+#define DIFF_TYPE2 2
30+#define DIFF_TYPE3 3
31+ int type;
32+
33+ struct Range old;
34+ struct Range new;
35+};
36+
37+#define EFLAG_NONE 0
38+#define EFLAG_OVERLAP 1
39+#define EFLAG_NOOVERLAP 2
40+#define EFLAG_UNMERGED 3
41+
42+static size_t szchanges;
43+
44+static struct Diff *d13;
45+static struct Diff *d23;
46+static struct Diff *de;
47+static char *overlap;
48+static int overlapcnt;
49+static FILE *fp[3];
50+static int cline[3];
51+static int sigpipe[2];
52+static int last[4];
53+static int Aflag, eflag, iflag, mflag, Tflag;
54+static int oflag;
55+static int strip_cr;
56+static char *f1mark, *f2mark, *f3mark;
57+static const char *oldmark = "<<<<<<<";
58+static const char *orgmark = "|||||||";
59+static const char *newmark = ">>>>>>>";
60+static const char *divider = "=======";
61+
62+static int duplicate(struct Range *, struct Range *);
63+static int edit(struct Diff *, int, int, int);
64+static char *getchange(FILE *);
65+static char *get_line(FILE *, size_t *);
66+static int readin(int, struct Diff **);
67+static int skip(int, int, const char *);
68+static void change(int, struct Range *, int);
69+static void keep(int, struct Range *);
70+static void merge(int, int);
71+static void prange(struct Range *, int);
72+static void repos(int);
73+static void separate(const char *);
74+static void edscript(int);
75+static void Ascript(int);
76+static void mergescript(int);
77+static void increase(void);
78+static void usage(void);
79+static void printrange(FILE *, struct Range *);
80+static void diffexec(const char *, char **, int *);
81+static int strtoi(char *, char **);
82+static void handle_sig(int);
83+static void xasprintf(char **, const char *, ...);
84+
85+static const char diff3_version[] = "FreeBSD diff3 20240925";
86+
87+enum {
88+ DIFFPROG_OPT,
89+ STRIPCR_OPT,
90+ HELP_OPT,
91+ VERSION_OPT
92+};
93+
94+#define DIFF_PATH "/usr/bin/diff"
95+
96+#define OPTIONS "3aAeEiL:mTxX"
97+static struct option longopts[] = {
98+ { "ed", no_argument, NULL, 'e' },
99+ { "show-overlap", no_argument, NULL, 'E' },
100+ { "overlap-only", no_argument, NULL, 'x' },
101+ { "initial-tab", no_argument, NULL, 'T' },
102+ { "text", no_argument, NULL, 'a' },
103+ { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT },
104+ { "show-all", no_argument, NULL, 'A' },
105+ { "easy-only", no_argument, NULL, '3' },
106+ { "merge", no_argument, NULL, 'm' },
107+ { "label", required_argument, NULL, 'L' },
108+ { "diff-program", required_argument, NULL, DIFFPROG_OPT },
109+ { "help", no_argument, NULL, HELP_OPT},
110+ { "version", no_argument, NULL, VERSION_OPT}
111+};
112+
113+static void
114+usage(void)
115+{
116+ fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
117+ "[-L label3] file1 file2 file3\n");
118+}
119+
120+static int
121+strtoi(char *str, char **end)
122+{
123+ intmax_t num;
124+
125+ errno = 0;
126+ num = strtoimax(str, end, 10);
127+ if ((end != NULL && *end == str) ||
128+ num < 0 || num > INT_MAX ||
129+ errno == EINVAL || errno == ERANGE)
130+ err(1, "error in diff output");
131+ return (int)num;
132+}
133+
134+static int
135+readin(int fd, struct Diff **dd)
136+{
137+ int a, b, c, d;
138+ int i;
139+ char kind, *p;
140+ FILE *f;
141+
142+ f = fdopen(fd, "r");
143+ if (f == NULL)
144+ err(2, "fdopen");
145+ for (i = 0; (p = getchange(f)) != NULL; i++) {
146+ if ((size_t)i >= szchanges - 1)
147+ increase();
148+
149+ a = b = strtoi(p, &p);
150+ if (*p == ',')
151+ b = strtoi(p + 1, &p);
152+ kind = *p++;
153+ c = d = strtoi(p, &p);
154+ if (*p == ',')
155+ d = strtoi(p + 1, &p);
156+ if (*p != '\n')
157+ errx(1, "error in diff output");
158+ if (kind == 'a')
159+ a++;
160+ else if (kind == 'c')
161+ /* nothing */ ;
162+ else if (kind == 'd')
163+ c++;
164+ else
165+ errx(1, "error in diff output");
166+ b++;
167+ d++;
168+ if (b < a || d < c)
169+ errx(1, "error in diff output");
170+ (*dd)[i].old.from = a;
171+ (*dd)[i].old.to = b;
172+ (*dd)[i].new.from = c;
173+ (*dd)[i].new.to = d;
174+ if (i > 0) {
175+ if ((*dd)[i].old.from < (*dd)[i - 1].old.to ||
176+ (*dd)[i].new.from < (*dd)[i - 1].new.to)
177+ errx(1, "diff output out of order");
178+ }
179+ }
180+ if (i > 0) {
181+ (*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to;
182+ (*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to;
183+ }
184+ fclose(f);
185+ return i;
186+}
187+
188+static void
189+diffexec(const char *diffprog, char **diffargv, int fd[])
190+{
191+ switch (fork()) {
192+ case 0:
193+ close(fd[0]);
194+ if (dup2(fd[1], STDOUT_FILENO) == -1)
195+ err(2, "child could not duplicate descriptor");
196+ close(fd[1]);
197+ execvp(diffprog, diffargv);
198+ err(2, "could not execute diff: %s", diffprog);
199+ break;
200+ case -1:
201+ err(2, "could not fork");
202+ break;
203+ }
204+ close(fd[1]);
205+}
206+
207+static char *
208+getchange(FILE *b)
209+{
210+ char *line;
211+
212+ while ((line = get_line(b, NULL)) != NULL) {
213+ if (isdigit((unsigned char)line[0]))
214+ return line;
215+ }
216+ return NULL;
217+}
218+
219+static char *
220+get_line(FILE *b, size_t *n)
221+{
222+ ssize_t len;
223+ static char *buf = NULL;
224+ static size_t bufsize = 0;
225+
226+ if ((len = getline(&buf, &bufsize, b)) < 0)
227+ return NULL;
228+
229+ if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
230+ buf[len - 2] = '\n';
231+ buf[len - 1] = '\0';
232+ len--;
233+ }
234+
235+ if (n != NULL)
236+ *n = len;
237+
238+ return buf;
239+}
240+
241+static void
242+merge(int m1, int m2)
243+{
244+ struct Diff *d1, *d2, *d3;
245+ int j, t1, t2;
246+ int dup = 0;
247+
248+ d1 = d13;
249+ d2 = d23;
250+ j = 0;
251+
252+ for (;;) {
253+ t1 = (d1 < d13 + m1);
254+ t2 = (d2 < d23 + m2);
255+ if (!t1 && !t2)
256+ break;
257+
258+ if (!t2 || (t1 && d1->new.to < d2->new.from)) {
259+ if (eflag == EFLAG_NONE) {
260+ separate("1");
261+ change(1, &d1->old, 0);
262+ keep(2, &d1->new);
263+ change(3, &d1->new, 0);
264+ } else if (eflag == EFLAG_OVERLAP) {
265+ j = edit(d2, dup, j, DIFF_TYPE1);
266+ }
267+ d1++;
268+ continue;
269+ }
270+ if (!t1 || (t2 && d2->new.to < d1->new.from)) {
271+ if (eflag == EFLAG_NONE) {
272+ separate("2");
273+ keep(1, &d2->new);
274+ change(3, &d2->new, 0);
275+ change(2, &d2->old, 0);
276+ } else if (Aflag || mflag) {
277+ if (eflag == EFLAG_UNMERGED)
278+ j = edit(d2, dup, j, DIFF_TYPE2);
279+ }
280+ d2++;
281+ continue;
282+ }
283+ if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
284+ d1[1].old.from = d1->old.from;
285+ d1[1].new.from = d1->new.from;
286+ d1++;
287+ continue;
288+ }
289+ if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
290+ d2[1].old.from = d2->old.from;
291+ d2[1].new.from = d2->new.from;
292+ d2++;
293+ continue;
294+ }
295+ if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
296+ dup = duplicate(&d1->old, &d2->old);
297+ if (eflag == EFLAG_NONE) {
298+ separate(dup ? "3" : "");
299+ change(1, &d1->old, dup);
300+ change(2, &d2->old, 0);
301+ d3 = d1->old.to > d1->old.from ? d1 : d2;
302+ change(3, &d3->new, 0);
303+ } else {
304+ j = edit(d1, dup, j, DIFF_TYPE3);
305+ }
306+ dup = 0;
307+ d1++;
308+ d2++;
309+ continue;
310+ }
311+ if (d1->new.from < d2->new.from) {
312+ d2->old.from -= d2->new.from - d1->new.from;
313+ d2->new.from = d1->new.from;
314+ } else if (d2->new.from < d1->new.from) {
315+ d1->old.from -= d1->new.from - d2->new.from;
316+ d1->new.from = d2->new.from;
317+ }
318+ if (d1->new.to > d2->new.to) {
319+ d2->old.to += d1->new.to - d2->new.to;
320+ d2->new.to = d1->new.to;
321+ } else if (d2->new.to > d1->new.to) {
322+ d1->old.to += d2->new.to - d1->new.to;
323+ d1->new.to = d2->new.to;
324+ }
325+ }
326+
327+ if (mflag)
328+ mergescript(j);
329+ else if (Aflag)
330+ Ascript(j);
331+ else if (eflag)
332+ edscript(j);
333+}
334+
335+static void
336+separate(const char *s)
337+{
338+ printf("====%s\n", s);
339+}
340+
341+static void
342+change(int i, struct Range *rold, int dup)
343+{
344+ printf("%d:", i);
345+ last[i] = rold->to;
346+ prange(rold, 0);
347+ if (dup)
348+ return;
349+ i--;
350+ skip(i, rold->from, NULL);
351+ skip(i, rold->to, " ");
352+}
353+
354+static void
355+prange(struct Range *rold, int delete)
356+{
357+ if (rold->to <= rold->from)
358+ printf("%da\n", rold->from - 1);
359+ else {
360+ printf("%d", rold->from);
361+ if (rold->to > rold->from + 1)
362+ printf(",%d", rold->to - 1);
363+ if (delete)
364+ printf("d\n");
365+ else
366+ printf("c\n");
367+ }
368+}
369+
370+static void
371+keep(int i, struct Range *rnew)
372+{
373+ int delta;
374+ struct Range trange;
375+
376+ delta = last[3] - last[i];
377+ trange.from = rnew->from - delta;
378+ trange.to = rnew->to - delta;
379+ change(i, &trange, 1);
380+}
381+
382+static int
383+skip(int i, int from, const char *pr)
384+{
385+ size_t j, n;
386+ char *line;
387+
388+ for (n = 0; cline[i] < from - 1; n += j) {
389+ if ((line = get_line(fp[i], &j)) == NULL)
390+ errx(EXIT_FAILURE, "logic error");
391+ if (pr != NULL)
392+ printf("%s%s", Tflag == 1 ? "\t" : pr, line);
393+ cline[i]++;
394+ }
395+ return (int)n;
396+}
397+
398+static int
399+duplicate(struct Range *r1, struct Range *r2)
400+{
401+ int c, d;
402+ int nchar;
403+ int nline;
404+
405+ if (r1->to-r1->from != r2->to-r2->from)
406+ return 0;
407+ skip(0, r1->from, NULL);
408+ skip(1, r2->from, NULL);
409+ nchar = 0;
410+ for (nline = 0; nline < r1->to - r1->from; nline++) {
411+ do {
412+ c = getc(fp[0]);
413+ d = getc(fp[1]);
414+ if (c == -1 && d == -1)
415+ break;
416+ if (c == -1 || d == -1)
417+ errx(EXIT_FAILURE, "logic error");
418+ nchar++;
419+ if (c != d) {
420+ repos(nchar);
421+ return 0;
422+ }
423+ } while (c != '\n');
424+ }
425+ repos(nchar);
426+ return 1;
427+}
428+
429+static void
430+repos(int nchar)
431+{
432+ int i;
433+
434+ for (i = 0; i < 2; i++)
435+ (void)fseek(fp[i], (long)-nchar, SEEK_CUR);
436+}
437+
438+static int
439+edit(struct Diff *diff, int dup, int j, int difftype)
440+{
441+ if (!(eflag == EFLAG_UNMERGED ||
442+ (!dup && eflag == EFLAG_OVERLAP) ||
443+ (dup && eflag == EFLAG_NOOVERLAP))) {
444+ return j;
445+ }
446+ j++;
447+ overlap[j] = !dup;
448+ if (!dup)
449+ overlapcnt++;
450+
451+ de[j].type = difftype;
452+ de[j].old.from = diff->old.from;
453+ de[j].old.to = diff->old.to;
454+ de[j].new.from = diff->new.from;
455+ de[j].new.to = diff->new.to;
456+ return j;
457+}
458+
459+static void
460+printrange(FILE *p, struct Range *r)
461+{
462+ char *line = NULL;
463+ size_t len = 0;
464+ int i = 1;
465+
466+ if (r->from == r->to)
467+ return;
468+
469+ if (r->from > r->to)
470+ errx(EXIT_FAILURE, "invalid print range");
471+
472+ fseek(p, 0L, SEEK_SET);
473+ while (getline(&line, &len, p) > 0) {
474+ if (i >= r->from)
475+ printf("%s", line);
476+ if (++i > r->to - 1)
477+ break;
478+ }
479+ free(line);
480+}
481+
482+static void
483+edscript(int n)
484+{
485+ int delete;
486+ struct Range *new, *old;
487+
488+ for (; n > 0; n--) {
489+ new = &de[n].new;
490+ old = &de[n].old;
491+
492+ delete = (new->from == new->to);
493+ if (de[n].type == DIFF_TYPE1) {
494+ if (delete)
495+ printf("%dd\n", new->from - 1);
496+ else if (old->from == new->from && old->to == new->to) {
497+ printf("%dc\n", old->from);
498+ printrange(fp[2], old);
499+ printf(".\n");
500+ }
501+ continue;
502+ } else {
503+ if (!oflag || !overlap[n]) {
504+ prange(old, delete);
505+ } else {
506+ printf("%da\n", old->to - 1);
507+ printf("%s\n", divider);
508+ }
509+ printrange(fp[2], new);
510+ if (!oflag || !overlap[n]) {
511+ if (!delete)
512+ printf(".\n");
513+ } else {
514+ printf("%s %s\n.\n", newmark, f3mark);
515+ printf("%da\n%s %s\n.\n", old->from - 1, oldmark, f1mark);
516+ }
517+ }
518+ }
519+ if (iflag)
520+ printf("w\nq\n");
521+
522+ exit(eflag == EFLAG_NONE ? overlapcnt : 0);
523+}
524+
525+static void
526+Ascript(int n)
527+{
528+ int startmark;
529+ int deletenew;
530+ int deleteold;
531+ struct Range *new, *old;
532+
533+ for (; n > 0; n--) {
534+ new = &de[n].new;
535+ old = &de[n].old;
536+ deletenew = (new->from == new->to);
537+ deleteold = (old->from == old->to);
538+
539+ if (de[n].type == DIFF_TYPE2) {
540+ if (!oflag || !overlap[n]) {
541+ prange(old, deletenew);
542+ printrange(fp[2], new);
543+ } else {
544+ startmark = new->to - 1;
545+ printf("%da\n", startmark);
546+ printf("%s %s\n", newmark, f3mark);
547+ printf(".\n");
548+ printf("%da\n", startmark - (new->to - new->from));
549+ printf("%s %s\n", oldmark, f2mark);
550+ if (!deleteold)
551+ printrange(fp[1], old);
552+ printf("%s\n.\n", divider);
553+ }
554+ } else if (de[n].type == DIFF_TYPE3) {
555+ startmark = old->to - 1;
556+ if (!oflag || !overlap[n]) {
557+ prange(old, deletenew);
558+ printrange(fp[2], new);
559+ } else {
560+ printf("%da\n", startmark);
561+ printf("%s %s\n", orgmark, f2mark);
562+ if (deleteold) {
563+ struct Range r;
564+ r.from = old->from-1;
565+ r.to = new->to;
566+ printrange(fp[1], &r);
567+ } else
568+ printrange(fp[1], old);
569+ printf("%s\n", divider);
570+ printrange(fp[2], new);
571+ }
572+ if (!oflag || !overlap[n]) {
573+ if (!deletenew)
574+ printf(".\n");
575+ } else {
576+ printf("%s %s\n.\n", newmark, f3mark);
577+ printf("%da\n%s %s\n.\n", startmark - (old->to - old->from), oldmark, f1mark);
578+ }
579+ }
580+ }
581+ if (iflag)
582+ printf("w\nq\n");
583+
584+ exit(overlapcnt > 0);
585+}
586+
587+static void
588+mergescript(int i)
589+{
590+ struct Range r, *new, *old;
591+ int n;
592+ int delete = 0;
593+
594+ r.from = 1;
595+ r.to = 1;
596+
597+ for (n = 1; n <= i; n++) {
598+ new = &de[n].new;
599+ old = &de[n].old;
600+
601+ delete = (new->from == new->to);
602+ if (de[n].type == DIFF_TYPE1 && delete)
603+ r.to = new->from - 1;
604+ else if (de[n].type == DIFF_TYPE3 && (old->from == old->to)) {
605+ r.from = old->from - 1;
606+ r.to = new->from;
607+ } else
608+ r.to = old->from;
609+
610+ printrange(fp[0], &r);
611+ switch (de[n].type) {
612+ case DIFF_TYPE1:
613+ if (!delete)
614+ printrange(fp[2], new);
615+ break;
616+ case DIFF_TYPE2:
617+ printf("%s %s\n", oldmark, f2mark);
618+ printrange(fp[1], old);
619+ printf("%s\n", divider);
620+ printrange(fp[2], new);
621+ printf("%s %s\n", newmark, f3mark);
622+ break;
623+ case DIFF_TYPE3:
624+ if (!oflag || !overlap[n]) {
625+ printrange(fp[2], new);
626+ } else {
627+ printf("%s %s\n", oldmark, f1mark);
628+ printrange(fp[0], old);
629+ if (eflag != EFLAG_OVERLAP) {
630+ printf("%s %s\n", orgmark, f2mark);
631+ if (old->from == old->to) {
632+ struct Range or;
633+ or.from = old->from - 1;
634+ or.to = new->to;
635+ printrange(fp[1], &or);
636+ } else {
637+ printrange(fp[1], old);
638+ }
639+ }
640+ printf("%s\n", divider);
641+ printrange(fp[2], new);
642+ printf("%s %s\n", newmark, f3mark);
643+ }
644+ break;
645+ default:
646+ printf("Error: Unhandled diff type - exiting\n");
647+ exit(EXIT_FAILURE);
648+ }
649+
650+ if (old->from == old->to)
651+ r.from = new->to;
652+ else
653+ r.from = old->to;
654+ }
655+
656+ new = &de[n-1].new;
657+ old = &de[n-1].old;
658+
659+ if (old->from == new->from && old->to == new->to)
660+ r.from--;
661+ else if (new->from == new->to)
662+ r.from = old->from;
663+
664+ r.to = INT_MAX;
665+ printrange(fp[2], &r);
666+ exit(overlapcnt > 0);
667+}
668+
669+static void
670+increase(void)
671+{
672+ struct Diff *p;
673+ char *q;
674+ size_t newsz, incr;
675+
676+ newsz = szchanges == 0 ? 64 : 2 * szchanges;
677+ incr = newsz - szchanges;
678+
679+ p = ereallocarray(d13, newsz, sizeof(*p));
680+ memset(p + szchanges, 0, incr * sizeof(*p));
681+ d13 = p;
682+
683+ p = ereallocarray(d23, newsz, sizeof(*p));
684+ memset(p + szchanges, 0, incr * sizeof(*p));
685+ d23 = p;
686+
687+ p = ereallocarray(de, newsz, sizeof(*p));
688+ memset(p + szchanges, 0, incr * sizeof(*p));
689+ de = p;
690+
691+ q = ereallocarray(overlap, newsz, 1);
692+ memset(q + szchanges, 0, incr * 1);
693+ overlap = q;
694+ szchanges = newsz;
695+}
696+
697+static void
698+handle_sig(int signo)
699+{
700+ write(sigpipe[1], &signo, sizeof(signo));
701+}
702+
703+static void
704+xasprintf(char **strp, const char *fmt, ...)
705+{
706+ va_list ap;
707+ int r;
708+
709+ va_start(ap, fmt);
710+ r = vasprintf(strp, fmt, ap);
711+ va_end(ap);
712+ if (r == -1)
713+ err(2, "asprintf");
714+}
715+
716+// ?man diff3: 3-way differential file comparison
717+// ?man arguments: file1 file2 file3
718+// ?man synopsis: [-3aAeEiL:mTxX] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
719+int
720+main(int argc, char **argv)
721+{
722+ int ch, nblabels, status, m, n, npe, nleft;
723+ char *labels[] = { NULL, NULL, NULL };
724+ const char *diffprog = DIFF_PATH;
725+ char *file1, *file2, *file3;
726+ char *diffargv[7];
727+ int diffargc = 0;
728+ int fd13[2], fd23[2], signo;
729+ pid_t wpid;
730+ struct pollfd pfd;
731+
732+ nblabels = 0;
733+ eflag = EFLAG_NONE;
734+ oflag = 0;
735+ diffargv[diffargc++] = (char *)diffprog;
736+ while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
737+ // ?man -3: output changes specific only to file3
738+ switch (ch) {
739+ case '3':
740+ eflag = EFLAG_NOOVERLAP;
741+ break;
742+ // ?man -a: treat files as text
743+ case 'a':
744+ diffargv[diffargc++] = "-a";
745+ break;
746+ // ?man -A: output all changes bracketing conflicts
747+ case 'A':
748+ Aflag = 1;
749+ break;
750+ // ?man -e: output ed script
751+ case 'e':
752+ eflag = EFLAG_UNMERGED;
753+ break;
754+ // ?man -E: show overlapping changes
755+ case 'E':
756+ eflag = EFLAG_OVERLAP;
757+ oflag = 1;
758+ break;
759+ // ?man -i: append w and q ed commands
760+ case 'i':
761+ iflag = 1;
762+ break;
763+ // ?man -L:label: define label
764+ case 'L':
765+ oflag = 1;
766+ if (nblabels >= 3)
767+ errx(2, "too many file label options");
768+ labels[nblabels++] = optarg;
769+ break;
770+ // ?man -m: merge output
771+ case 'm':
772+ Aflag = 1;
773+ oflag = 1;
774+ mflag = 1;
775+ break;
776+ // ?man -T: use tab prefix in normal listing
777+ case 'T':
778+ Tflag = 1;
779+ break;
780+ // ?man -x: output script with changes specific to all three
781+ case 'x':
782+ eflag = EFLAG_OVERLAP;
783+ break;
784+ // ?man -X: show overlapping changes
785+ case 'X':
786+ oflag = 1;
787+ eflag = EFLAG_OVERLAP;
788+ break;
789+ case DIFFPROG_OPT:
790+ diffprog = optarg;
791+ break;
792+ case STRIPCR_OPT:
793+ strip_cr = 1;
794+ diffargv[diffargc++] = "--strip-trailing-cr";
795+ break;
796+ case HELP_OPT:
797+ usage();
798+ exit(0);
799+ case VERSION_OPT:
800+ printf("%s\n", diff3_version);
801+ exit(0);
802+ }
803+ }
804+ argc -= optind;
805+ argv += optind;
806+
807+ if (Aflag) {
808+ if (eflag == EFLAG_NONE)
809+ eflag = EFLAG_UNMERGED;
810+ oflag = 1;
811+ }
812+
813+ if (argc != 3) {
814+ usage();
815+ exit(2);
816+ }
817+
818+ file1 = argv[0];
819+ file2 = argv[1];
820+ file3 = argv[2];
821+
822+ if (oflag) {
823+ xasprintf(&f1mark, "%s", labels[0] != NULL ? labels[0] : file1);
824+ xasprintf(&f2mark, "%s", labels[1] != NULL ? labels[1] : file2);
825+ xasprintf(&f3mark, "%s", labels[2] != NULL ? labels[2] : file3);
826+ }
827+ fp[0] = fopen(file1, "r");
828+ if (fp[0] == NULL)
829+ err(2, "Can't open %s", file1);
830+
831+ fp[1] = fopen(file2, "r");
832+ if (fp[1] == NULL)
833+ err(2, "Can't open %s", file2);
834+
835+ fp[2] = fopen(file3, "r");
836+ if (fp[2] == NULL)
837+ err(2, "Can't open %s", file3);
838+
839+ if (pipe(fd13))
840+ err(2, "pipe");
841+ if (pipe(fd23))
842+ err(2, "pipe");
843+ if (pipe(sigpipe))
844+ err(2, "pipe");
845+ if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC))
846+ err(2, "fcntl");
847+ if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC))
848+ err(2, "fcntl");
849+
850+ pfd.fd = sigpipe[0];
851+ pfd.events = POLLIN;
852+ pfd.revents = 0;
853+
854+ if (signal(SIGCHLD, handle_sig) == SIG_ERR)
855+ err(2, "signal");
856+
857+ diffargv[diffargc] = file1;
858+ diffargv[diffargc + 1] = file3;
859+ diffargv[diffargc + 2] = NULL;
860+
861+ nleft = 0;
862+ diffexec(diffprog, diffargv, fd13);
863+ nleft++;
864+
865+ diffargv[diffargc] = file2;
866+ diffexec(diffprog, diffargv, fd23);
867+ nleft++;
868+
869+ increase();
870+ m = readin(fd13[0], &d13);
871+ n = readin(fd23[0], &d23);
872+
873+ while (nleft > 0) {
874+ npe = poll(&pfd, 1, -1);
875+ if (npe == -1) {
876+ if (errno == EINTR)
877+ continue;
878+ err(2, "poll");
879+ }
880+ if (pfd.revents != POLLIN)
881+ continue;
882+ if (read(pfd.fd, &signo, sizeof(signo)) < 0)
883+ err(2, "read");
884+ while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {
885+ if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
886+ errx(2, "diff exited abnormally");
887+ else if (WIFSIGNALED(status))
888+ errx(2, "diff killed by signal %d", WTERMSIG(status));
889+ nleft--;
890+ }
891+ }
892+ merge(m, n);
893+
894+ return EXIT_SUCCESS;
895+}
+3150,
-0
1@@ -0,0 +1,3150 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "util.h"
4+
5+#include <sys/file.h>
6+#include <sys/mman.h>
7+#include <sys/param.h>
8+#include <sys/stat.h>
9+#include <sys/wait.h>
10+
11+#include <ctype.h>
12+#include <dirent.h>
13+#include <err.h>
14+#include <errno.h>
15+#include <fcntl.h>
16+#include <getopt.h>
17+#include <libgen.h>
18+#include <limits.h>
19+#include <paths.h>
20+#include <stdarg.h>
21+#include <stddef.h>
22+#include <stdint.h>
23+#include <stdio.h>
24+#include <stdlib.h>
25+#include <string.h>
26+#include <unistd.h>
27+
28+#define DEBUGGING
29+
30+#define MAXHUNKSIZE 100000
31+#define INITHUNKMAX 125
32+#define INITLINELEN 8192
33+#define BUFFERSIZE 1024
34+#define LINENUM_MAX LONG_MAX
35+
36+#define SCCSPREFIX "s."
37+#define GET "get -e %s"
38+#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
39+
40+#define RCSSUFFIX ",v"
41+#define CHECKOUT "/usr/bin/co"
42+#define RCSDIFF "/usr/bin/rcsdiff"
43+
44+#define ORIGEXT ".orig"
45+#define REJEXT ".rej"
46+
47+#define strNE(s1,s2) (strcmp(s1, s2))
48+#define strEQ(s1,s2) (!strcmp(s1, s2))
49+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
50+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
51+
52+#define OLD_FILE 0
53+#define NEW_FILE 1
54+#define INDEX_FILE 2
55+#define MAX_FILE 3
56+
57+#define CONTEXT_DIFF 1
58+#define NORMAL_DIFF 2
59+#define ED_DIFF 3
60+#define NEW_CONTEXT_DIFF 4
61+#define UNI_DIFF 5
62+
63+#define _PATH_ED "/bin/ed"
64+
65+#define ISDIGIT(c) (isascii((unsigned char)c) && isdigit((unsigned char)c))
66+
67+#ifndef __UNCONST
68+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
69+#endif
70+
71+#ifndef __dead
72+#define __dead __attribute__((__noreturn__))
73+#endif
74+
75+typedef long LINENUM;
76+
77+enum BackupType {
78+ BACKUP_UNDEFINED,
79+ BACKUP_NONE,
80+ BACKUP_SIMPLE,
81+ BACKUP_NUMBERED_EXISTING,
82+ BACKUP_NUMBERED
83+};
84+
85+struct FileName {
86+ char *path;
87+ int exists;
88+};
89+
90+mode_t filemode = 0644;
91+char *buf;
92+size_t bufsz;
93+int using_plan_a = 1;
94+int out_of_mem = 0;
95+
96+#define MAXFILEC 2
97+char *filearg[MAXFILEC];
98+int ok_to_create_file = 0;
99+char *outname = NULL;
100+char *origprae = NULL;
101+char *TMPOUTNAME;
102+char *TMPINNAME;
103+char *TMPREJNAME;
104+char *TMPPATNAME;
105+int toutkeep = 0;
106+int trejkeep = 0;
107+int warn_on_invalid_line;
108+int last_line_missing_eol;
109+
110+#ifdef DEBUGGING
111+int debug = 0;
112+#endif
113+
114+int force = 0;
115+int batch = 0;
116+int verbose = 1;
117+int reverse = 0;
118+int noreverse = 0;
119+int skip_rest_of_patch = 0;
120+int strippath = 957;
121+int canonicalize = 0;
122+int check_only = 0;
123+int diff_type = 0;
124+char *revision = NULL;
125+LINENUM input_lines = 0;
126+int posix = 0;
127+int backup_if_mismatch = -1;
128+
129+enum BackupType backup_type = BACKUP_UNDEFINED;
130+const char *simple_backup_suffix = "~";
131+
132+static int remove_empty_files = 0;
133+static int reverse_flag_specified = 0;
134+static char rejname[PATH_MAX];
135+static char serrbuf[BUFSIZ];
136+static LINENUM last_frozen_line = 0;
137+static int Argc;
138+static char **Argv;
139+static int Argc_last;
140+static char **Argv_last;
141+static FILE *ofp = NULL;
142+static FILE *rejfp = NULL;
143+static int filec = 0;
144+static LINENUM last_offset = 0;
145+static LINENUM maxfuzz = 2;
146+
147+static int do_defines = 0;
148+static char if_defined[128];
149+static char not_defined[128];
150+static const char else_defined[] = "#else\n";
151+static char end_defined[128];
152+
153+/* function declarations */
154+static void reinitialize_almost_everything(void);
155+static void get_some_switches(void);
156+static void reset_getopt_state(void);
157+static LINENUM locate_hunk(LINENUM);
158+static void abort_context_hunk(void);
159+static void rej_line(int, LINENUM);
160+static void abort_hunk(void);
161+static void apply_hunk(LINENUM);
162+static void init_output(const char *);
163+static void init_reject(const char *);
164+static void copy_till(LINENUM, int);
165+static int spew_output(void);
166+static void dump_patch_line(LINENUM, int);
167+static void dump_input_line(LINENUM, int);
168+static int patch_match(LINENUM, LINENUM, LINENUM);
169+static int similar(const char *, const char *, ssize_t);
170+static void usage(void);
171+
172+void re_patch(void);
173+void open_patch_file(const char *);
174+void set_hunkmax(void);
175+int there_is_another_patch(void);
176+int another_hunk(void);
177+int pch_swap(void);
178+char *pfetch(LINENUM);
179+ssize_t pch_line_len(LINENUM);
180+LINENUM pch_first(void);
181+LINENUM pch_ptrn_lines(void);
182+LINENUM pch_newfirst(void);
183+LINENUM pch_repl_lines(void);
184+LINENUM pch_end(void);
185+LINENUM pch_context(void);
186+LINENUM pch_hunk_beg(void);
187+char pch_char(LINENUM);
188+void do_ed_script(void);
189+
190+void re_input(void);
191+void scan_input(const char *);
192+char *ifetch(LINENUM, int);
193+
194+char *fetchname(const char *, int *, int);
195+char *checked_in(char *);
196+LINENUM strtolinenum(char *, char **);
197+int backup_file(const char *);
198+int move_file(const char *, const char *);
199+int copy_file(const char *, const char *);
200+void say(const char *, ...);
201+__dead void fatal(const char *, ...);
202+__dead void pfatal(const char *, ...);
203+void ask(const char *, ...);
204+char *savestr(const char *);
205+void set_signals(int);
206+void ignore_signals(void);
207+void makedirs(const char *, int);
208+__dead void version(void);
209+__dead void my_exit(int);
210+static void *pch_realloc(void *, size_t, size_t);
211+int mkpath(char *);
212+char *find_backup_file_name(const char *);
213+enum BackupType get_version(const char *);
214+
215+static off_t i_size;
216+static char *i_womp;
217+static char **i_ptr;
218+static char empty_line[] = { '\0' };
219+
220+static int tifd = -1;
221+static char *tibuf[2];
222+static LINENUM tiline[2] = {-1, -1};
223+static LINENUM lines_per_buf;
224+static int tireclen;
225+
226+static int rev_in_string(const char *);
227+static int reallocate_lines(size_t *);
228+static int plan_a(const char *);
229+static void plan_b(const char *);
230+
231+static long p_filesize;
232+static LINENUM p_first;
233+static LINENUM p_newfirst;
234+static LINENUM p_ptrn_lines;
235+static LINENUM p_repl_lines;
236+static LINENUM p_end = -1;
237+static LINENUM p_max;
238+static LINENUM p_context = 3;
239+static LINENUM p_input_line = 0;
240+static char **p_line = NULL;
241+static ssize_t *p_len = NULL;
242+static char *p_char = NULL;
243+static int hunkmax = INITHUNKMAX;
244+static int p_indent;
245+static LINENUM p_base;
246+static LINENUM p_bline;
247+static LINENUM p_start;
248+static LINENUM p_sline;
249+static LINENUM p_hunk_beg;
250+static LINENUM p_efake = -1;
251+static LINENUM p_bfake = -1;
252+static FILE *pfp = NULL;
253+static char *bestguess = NULL;
254+
255+static void grow_hunkmax(void);
256+static int intuit_diff_type(void);
257+static void next_intuit_at(LINENUM, LINENUM);
258+static void skip_to(LINENUM, LINENUM);
259+static int pgetline(char **, size_t *, FILE *);
260+static char *best_name(const struct FileName *, int);
261+static char *posix_name(const struct FileName *, int);
262+static size_t num_components(const char *);
263+
264+static char *patch_concat(const char *, const char *);
265+static char *make_version_name(const char *, int);
266+static int max_backup_version(const char *, const char *);
267+static int version_number(const char *, const char *, size_t);
268+static int argmatch(const char *, const char **);
269+static void invalid_arg(const char *, const char *, int);
270+
271+static const char *backup_args[] = {
272+ "none", "never", "simple", "nil", "existing", "t", "numbered", 0
273+};
274+static enum BackupType backup_types[] = {
275+ BACKUP_NONE, BACKUP_SIMPLE, BACKUP_SIMPLE, BACKUP_NUMBERED_EXISTING,
276+ BACKUP_NUMBERED_EXISTING, BACKUP_NUMBERED, BACKUP_NUMBERED
277+};
278+
279+/* implementation */
280+
281+// ?man patch: apply a diff file to an original
282+int
283+main(int argc, char *argv[])
284+{
285+ int error, hunk, failed, i, fd;
286+ LINENUM where, newwhere, fuzz, mymaxfuzz;
287+ const char *tmpdir;
288+ char *v;
289+
290+ error = 0;
291+ where = 0;
292+ bufsz = INITLINELEN;
293+ if ((buf = malloc(bufsz)) == NULL)
294+ pfatal("allocating input buffer");
295+ buf[0] = '\0';
296+
297+ setbuf(stderr, serrbuf);
298+ for (i = 0; i < MAXFILEC; i++)
299+ filearg[i] = NULL;
300+
301+ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
302+ tmpdir = _PATH_TMP;
303+ for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
304+ ;
305+ i++;
306+ if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
307+ fatal("cannot allocate memory");
308+ if ((fd = mkstemp(TMPOUTNAME)) < 0)
309+ pfatal("cant create %s", TMPOUTNAME);
310+ close(fd);
311+
312+ if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
313+ fatal("cannot allocate memory");
314+ if ((fd = mkstemp(TMPINNAME)) < 0)
315+ pfatal("cant create %s", TMPINNAME);
316+ close(fd);
317+
318+ if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
319+ fatal("cannot allocate memory");
320+ if ((fd = mkstemp(TMPREJNAME)) < 0)
321+ pfatal("cant create %s", TMPREJNAME);
322+ close(fd);
323+
324+ if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
325+ fatal("cannot allocate memory");
326+ if ((fd = mkstemp(TMPPATNAME)) < 0)
327+ pfatal("cant create %s", TMPPATNAME);
328+ close(fd);
329+
330+ v = getenv("SIMPLE_BACKUP_SUFFIX");
331+ if (v)
332+ simple_backup_suffix = v;
333+ else
334+ simple_backup_suffix = ORIGEXT;
335+
336+ if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
337+ v = getenv("VERSION_CONTROL");
338+ if (v != NULL)
339+ backup_type = get_version(v);
340+
341+ Argc = argc;
342+ Argv = argv;
343+ get_some_switches();
344+
345+ if (backup_type == BACKUP_UNDEFINED)
346+ backup_type = posix ? BACKUP_NONE : BACKUP_NUMBERED_EXISTING;
347+
348+ set_signals(0);
349+
350+ for (open_patch_file(filearg[1]); there_is_another_patch(); reinitialize_almost_everything()) {
351+ warn_on_invalid_line = 1;
352+ if (outname == NULL)
353+ outname = savestr(filearg[0]);
354+
355+ if (diff_type == ED_DIFF) {
356+ do_ed_script();
357+ continue;
358+ }
359+ if (!skip_rest_of_patch)
360+ init_output(TMPOUTNAME);
361+
362+ init_reject(TMPREJNAME);
363+
364+ if (!skip_rest_of_patch)
365+ scan_input(filearg[0]);
366+
367+ hunk = 0;
368+ failed = 0;
369+ out_of_mem = 0;
370+ while (another_hunk()) {
371+ hunk++;
372+ fuzz = 0;
373+ mymaxfuzz = pch_context();
374+ if (maxfuzz < mymaxfuzz)
375+ mymaxfuzz = maxfuzz;
376+ if (!skip_rest_of_patch) {
377+ do {
378+ where = locate_hunk(fuzz);
379+ if (hunk == 1 && where == 0 && !force) {
380+ if (!pch_swap()) {
381+ if (fuzz == 0)
382+ say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
383+ continue;
384+ }
385+ reverse = !reverse;
386+ where = locate_hunk(fuzz);
387+ if (where == 0) {
388+ if (!pch_swap())
389+ fatal("lost hunk on alloc error!\n");
390+ reverse = !reverse;
391+ } else if (noreverse) {
392+ if (!pch_swap())
393+ fatal("lost hunk on alloc error!\n");
394+ reverse = !reverse;
395+ say("Ignoring previously applied (or reversed) patch.\n");
396+ skip_rest_of_patch = 1;
397+ } else if (batch) {
398+ if (verbose)
399+ say("%seversed (or %spreviously applied) patch detected! %s -R.",
400+ reverse ? "R" : "Unr",
401+ reverse ? "" : "not ",
402+ reverse ? "Assuming" : "Ignoring");
403+ } else {
404+ ask("%seversed (or %spreviously applied) patch detected! %s -R? [y] ",
405+ reverse ? "R" : "Unr",
406+ reverse ? "" : "not ",
407+ reverse ? "Assume" : "Ignore");
408+ if (*buf == 'n') {
409+ ask("Apply anyway? [n] ");
410+ if (*buf != 'y')
411+ skip_rest_of_patch = 1;
412+ where = 0;
413+ reverse = !reverse;
414+ if (!pch_swap())
415+ fatal("lost hunk on alloc error!\n");
416+ }
417+ }
418+ }
419+ } while (!skip_rest_of_patch && where == 0 && ++fuzz <= mymaxfuzz);
420+
421+ if (skip_rest_of_patch) {
422+ if (ferror(ofp) || fclose(ofp)) {
423+ say("Error writing %s\n", TMPOUTNAME);
424+ error = 1;
425+ }
426+ ofp = NULL;
427+ }
428+ }
429+ newwhere = pch_newfirst() + last_offset;
430+ if (skip_rest_of_patch) {
431+ abort_hunk();
432+ failed++;
433+ if (verbose)
434+ say("Hunk #%d ignored at %ld.\n", hunk, newwhere);
435+ } else if (where == 0) {
436+ abort_hunk();
437+ failed++;
438+ if (verbose)
439+ say("Hunk #%d failed at %ld.\n", hunk, newwhere);
440+ } else {
441+ apply_hunk(where);
442+ if (verbose) {
443+ say("Hunk #%d succeeded at %ld", hunk, newwhere);
444+ if (fuzz != 0)
445+ say(" with fuzz %ld", fuzz);
446+ if (last_offset)
447+ say(" (offset %ld line%s)", last_offset, last_offset == 1L ? "" : "s");
448+ say(".\n");
449+ }
450+ }
451+ }
452+
453+ if (out_of_mem && using_plan_a) {
454+ Argc = Argc_last;
455+ Argv = Argv_last;
456+ say("\n\nRan out of memory using Plan A--trying again...\n\n");
457+ if (ofp)
458+ fclose(ofp);
459+ ofp = NULL;
460+ if (rejfp)
461+ fclose(rejfp);
462+ rejfp = NULL;
463+ continue;
464+ }
465+ if (hunk == 0)
466+ fatal("internal error: hunk should not be 0\n");
467+
468+ if (!skip_rest_of_patch && !spew_output()) {
469+ say("cant write %s\n", TMPOUTNAME);
470+ error = 1;
471+ }
472+
473+ ignore_signals();
474+ if (!skip_rest_of_patch) {
475+ struct stat statbuf;
476+ char *realout = outname;
477+
478+ if (!check_only) {
479+ enum BackupType saved = backup_type;
480+ if (failed > 0 && backup_if_mismatch > 0 && backup_type == BACKUP_NONE)
481+ backup_type = BACKUP_SIMPLE;
482+ if (move_file(TMPOUTNAME, outname) < 0) {
483+ toutkeep = 1;
484+ realout = TMPOUTNAME;
485+ chmod(TMPOUTNAME, filemode);
486+ } else
487+ chmod(outname, filemode);
488+ backup_type = saved;
489+
490+ if (remove_empty_files && stat(realout, &statbuf) == 0 && statbuf.st_size == 0) {
491+ if (verbose)
492+ say("Removing %s (empty after patching).\n", realout);
493+ unlink(realout);
494+ }
495+ }
496+ }
497+ if (ferror(rejfp) || fclose(rejfp)) {
498+ say("Error writing %s\n", rejname);
499+ error = 1;
500+ }
501+ rejfp = NULL;
502+ if (failed) {
503+ error = 1;
504+ if (*rejname == '\0') {
505+ if (strlcpy(rejname, outname, sizeof(rejname)) >= sizeof(rejname))
506+ fatal("filename %s is too long\n", outname);
507+ if (strlcat(rejname, REJEXT, sizeof(rejname)) >= sizeof(rejname))
508+ fatal("filename %s is too long\n", outname);
509+ }
510+ if (skip_rest_of_patch) {
511+ say("%d out of %d hunks ignored--saving rejects to %s\n", failed, hunk, rejname);
512+ } else {
513+ say("%d out of %d hunks FAILED -- saving rejects to %s\n", failed, hunk, rejname);
514+ }
515+ if (!check_only && move_file(TMPREJNAME, rejname) < 0)
516+ trejkeep = 1;
517+ }
518+ set_signals(1);
519+ }
520+ my_exit(error);
521+}
522+
523+static void
524+reset_getopt_state(void)
525+{
526+#if defined(__GLIBC__) || defined(__linux__)
527+ optind = 0;
528+#else
529+ optreset = 1;
530+ optind = 1;
531+#endif
532+}
533+
534+static void
535+reinitialize_almost_everything(void)
536+{
537+ re_patch();
538+ re_input();
539+
540+ input_lines = 0;
541+ last_frozen_line = 0;
542+ filec = 0;
543+ if (!out_of_mem) {
544+ free(filearg[0]);
545+ filearg[0] = NULL;
546+ }
547+ free(outname);
548+ outname = NULL;
549+ last_offset = 0;
550+ diff_type = 0;
551+ free(revision);
552+ revision = NULL;
553+ reverse = reverse_flag_specified;
554+ skip_rest_of_patch = 0;
555+ get_some_switches();
556+}
557+
558+static void
559+get_some_switches(void)
560+{
561+ const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
562+ static struct option longopts[] = {
563+ {"backup", no_argument, 0, 'b'},
564+ {"backup-if-mismatch", no_argument, &backup_if_mismatch, 1},
565+ {"batch", no_argument, 0, 't'},
566+ {"check", no_argument, 0, 'C'},
567+ {"context", no_argument, 0, 'c'},
568+ {"debug", required_argument, 0, 'x'},
569+ {"directory", required_argument, 0, 'd'},
570+ {"ed", no_argument, 0, 'e'},
571+ {"force", no_argument, 0, 'f'},
572+ {"forward", no_argument, 0, 'N'},
573+ {"fuzz", required_argument, 0, 'F'},
574+ {"ifdef", required_argument, 0, 'D'},
575+ {"input", required_argument, 0, 'i'},
576+ {"ignore-whitespace", no_argument, 0, 'l'},
577+ {"no-backup-if-mismatch", no_argument, &backup_if_mismatch, 0},
578+ {"normal", no_argument, 0, 'n'},
579+ {"output", required_argument, 0, 'o'},
580+ {"prefix", required_argument, 0, 'B'},
581+ {"quiet", no_argument, 0, 's'},
582+ {"reject-file", required_argument, 0, 'r'},
583+ {"remove-empty-files", no_argument, 0, 'E'},
584+ {"reverse", no_argument, 0, 'R'},
585+ {"silent", no_argument, 0, 's'},
586+ {"strip", required_argument, 0, 'p'},
587+ {"suffix", required_argument, 0, 'z'},
588+ {"unified", no_argument, 0, 'u'},
589+ {"version", no_argument, 0, 'v'},
590+ {"version-control", required_argument, 0, 'V'},
591+ {"posix", no_argument, &posix, 1},
592+ {NULL, 0, 0, 0}
593+ };
594+ int ch;
595+
596+ rejname[0] = '\0';
597+ Argc_last = Argc;
598+ Argv_last = Argv;
599+ if (!Argc)
600+ return;
601+ reset_getopt_state();
602+ while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
603+ switch (ch) {
604+ case 'b':
605+ if (backup_type == BACKUP_UNDEFINED)
606+ backup_type = BACKUP_NUMBERED_EXISTING;
607+ if (optarg == NULL)
608+ break;
609+ if (verbose)
610+ say("Warning, the -b suffix option has been obsoleted by the -z option\n");
611+ /* FALLTHROUGH */
612+ case 'z':
613+ simple_backup_suffix = savestr(optarg);
614+ break;
615+ case 'B':
616+ origprae = savestr(optarg);
617+ break;
618+ case 'c':
619+ diff_type = CONTEXT_DIFF;
620+ break;
621+ case 'C':
622+ check_only = 1;
623+ break;
624+ case 'd':
625+ if (chdir(optarg) < 0)
626+ pfatal("cant cd to %s", optarg);
627+ break;
628+ case 'D':
629+ do_defines = 1;
630+ if (!isalpha((unsigned char)*optarg) && *optarg != '_')
631+ fatal("argument to -D is not an identifier\n");
632+ snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", optarg);
633+ snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", optarg);
634+ snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", optarg);
635+ break;
636+ case 'e':
637+ diff_type = ED_DIFF;
638+ break;
639+ case 'E':
640+ remove_empty_files = 1;
641+ break;
642+ case 'f':
643+ force = 1;
644+ break;
645+ case 'F':
646+ maxfuzz = strtolinenum(optarg, NULL);
647+ break;
648+ case 'i':
649+ free(filearg[1]);
650+ filearg[1] = fetchname(optarg, &warn_on_invalid_line, 0);
651+ break;
652+ case 'l':
653+ canonicalize = 1;
654+ break;
655+ case 'n':
656+ diff_type = NORMAL_DIFF;
657+ break;
658+ case 'N':
659+ noreverse = 1;
660+ break;
661+ case 'o':
662+ outname = savestr(optarg);
663+ break;
664+ case 'p':
665+ strippath = (int)strtolinenum(optarg, NULL);
666+ break;
667+ case 'r':
668+ if (strlcpy(rejname, optarg, sizeof(rejname)) >= sizeof(rejname))
669+ fatal("reject filename %s too long\n", optarg);
670+ break;
671+ case 'R':
672+ reverse = 1;
673+ reverse_flag_specified = 1;
674+ break;
675+ case 's':
676+ verbose = 0;
677+ break;
678+ case 't':
679+ batch = 1;
680+ break;
681+ case 'u':
682+ diff_type = UNI_DIFF;
683+ break;
684+ case 'v':
685+ version();
686+ break;
687+ case 'V':
688+ backup_type = get_version(optarg);
689+ break;
690+ case 'x':
691+#ifdef DEBUGGING
692+ debug = (int)strtolinenum(optarg, NULL);
693+#endif
694+ break;
695+ default:
696+ if (ch == 0)
697+ break;
698+ usage();
699+ }
700+ }
701+ Argc -= optind;
702+ Argv += optind;
703+
704+ for (ch = 0; ch < Argc; ch++) {
705+ if (filec >= MAXFILEC)
706+ usage();
707+ filearg[filec++] = savestr(Argv[ch]);
708+ }
709+}
710+
711+static void
712+usage(void)
713+{
714+ fprintf(stderr, "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] "
715+ "[-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] "
716+ "[-p strip-count] [-r rej-name] [-V method] [-x number] "
717+ "[-z backup-ext] [origfile [patchfile]]\n");
718+ my_exit(2);
719+}
720+
721+static LINENUM
722+locate_hunk(LINENUM fuzz)
723+{
724+ LINENUM first_hunk_line = pch_first();
725+ LINENUM count = pch_ptrn_lines();
726+ LINENUM max_line = input_lines - count + 1;
727+ LINENUM line;
728+ LINENUM offset;
729+
730+ if (!count)
731+ return first_hunk_line + last_offset;
732+
733+ if (first_hunk_line + last_offset <= 0)
734+ offset = 1 - first_hunk_line;
735+ else if (first_hunk_line + last_offset > max_line)
736+ offset = max_line - first_hunk_line;
737+ else
738+ offset = last_offset;
739+
740+ for (line = first_hunk_line + offset; line <= max_line; line++) {
741+ if (patch_match(line, count, fuzz)) {
742+ last_offset = line - first_hunk_line;
743+ return line;
744+ }
745+ }
746+ for (line = first_hunk_line + offset - 1; line >= 1; line--) {
747+ if (patch_match(line, count, fuzz)) {
748+ last_offset = line - first_hunk_line;
749+ return line;
750+ }
751+ }
752+ return 0;
753+}
754+
755+static int
756+patch_match(LINENUM line, LINENUM count, LINENUM fuzz)
757+{
758+ LINENUM i;
759+ LINENUM pat_line;
760+ char *str;
761+ char *pat;
762+ ssize_t len;
763+
764+ for (i = 1; i <= count; i++) {
765+ pat_line = i;
766+ if (i <= fuzz)
767+ continue;
768+ if (i > count - fuzz)
769+ continue;
770+ str = ifetch(line + i - 1 - fuzz, 0);
771+ if (str == NULL)
772+ return 0;
773+ pat = pfetch(pat_line);
774+ len = pch_line_len(pat_line);
775+ if (canonicalize) {
776+ if (!similar(str, pat, len))
777+ return 0;
778+ } else if (strncmp(str, pat, len) != 0)
779+ return 0;
780+ }
781+ return 1;
782+}
783+
784+static int
785+similar(const char *a, const char *b, ssize_t len)
786+{
787+ while (len > 0) {
788+ if (isspace((unsigned char)*b)) {
789+ if (!isspace((unsigned char)*a))
790+ return 0;
791+ while (len > 0 && isspace((unsigned char)*b)) {
792+ b++;
793+ len--;
794+ }
795+ while (isspace((unsigned char)*a) && *a != '\n')
796+ a++;
797+ } else {
798+ if (*a != *b)
799+ return 0;
800+ a++;
801+ b++;
802+ len--;
803+ }
804+ }
805+ if (*a == '\n' || *a == '\r') {
806+ while (*a == '\n' || *a == '\r')
807+ a++;
808+ return *a == '\0';
809+ }
810+ return *a == '\0' || isspace((unsigned char)*a);
811+}
812+
813+static void
814+abort_hunk(void)
815+{
816+ LINENUM i;
817+ LINENUM pat_line;
818+ char *str;
819+ ssize_t len;
820+
821+ for (i = 1; i <= pch_end(); i++) {
822+ pat_line = i;
823+ str = pfetch(pat_line);
824+ len = pch_line_len(pat_line);
825+ rej_line(pat_line, i);
826+ }
827+}
828+
829+static void
830+rej_line(int pat_line, LINENUM i)
831+{
832+ char *str = pfetch(pat_line);
833+ char ch = pch_char(pat_line);
834+ if (rejfp != NULL) {
835+ if (ch == ' ')
836+ fprintf(rejfp, " %s", str);
837+ else
838+ fprintf(rejfp, "%c%s", ch, str);
839+ }
840+}
841+
842+static void
843+abort_context_hunk(void)
844+{
845+ LINENUM i;
846+ for (i = 0; i <= pch_end(); i++) {
847+ rej_line(i, i);
848+ }
849+}
850+
851+static void
852+apply_hunk(LINENUM where)
853+{
854+ LINENUM old = 1;
855+ const LINENUM lastline = pch_ptrn_lines();
856+ LINENUM new = lastline + 1;
857+#define OUTSIDE 0
858+#define IN_IFNDEF 1
859+#define IN_IFDEF 2
860+#define IN_ELSE 3
861+ int def_state = OUTSIDE;
862+ const LINENUM pat_end = pch_end();
863+
864+ where--;
865+ while (pch_char(new) == '=' || pch_char(new) == '\n')
866+ new++;
867+
868+ while (old <= lastline) {
869+ if (pch_char(old) == '-') {
870+ copy_till(where + old - 1, 0);
871+ if (do_defines) {
872+ if (def_state == OUTSIDE) {
873+ fputs(not_defined, ofp);
874+ def_state = IN_IFNDEF;
875+ } else if (def_state == IN_IFDEF) {
876+ fputs(else_defined, ofp);
877+ def_state = IN_ELSE;
878+ }
879+ fputs(pfetch(old), ofp);
880+ }
881+ last_frozen_line++;
882+ old++;
883+ } else if (new > pat_end) {
884+ break;
885+ } else if (pch_char(new) == '+') {
886+ copy_till(where + old - 1, 0);
887+ if (do_defines) {
888+ if (def_state == IN_IFNDEF) {
889+ fputs(else_defined, ofp);
890+ def_state = IN_ELSE;
891+ } else if (def_state == OUTSIDE) {
892+ fputs(if_defined, ofp);
893+ def_state = IN_IFDEF;
894+ }
895+ }
896+ fputs(pfetch(new), ofp);
897+ new++;
898+ } else if (pch_char(new) != pch_char(old)) {
899+ say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
900+ pch_hunk_beg() + old,
901+ pch_hunk_beg() + new);
902+ my_exit(2);
903+ } else if (pch_char(new) == '!') {
904+ copy_till(where + old - 1, 0);
905+ if (do_defines) {
906+ fputs(not_defined, ofp);
907+ def_state = IN_IFNDEF;
908+ }
909+ while (pch_char(old) == '!') {
910+ if (do_defines) {
911+ fputs(pfetch(old), ofp);
912+ }
913+ last_frozen_line++;
914+ old++;
915+ }
916+ if (do_defines) {
917+ fputs(else_defined, ofp);
918+ def_state = IN_ELSE;
919+ }
920+ while (pch_char(new) == '!') {
921+ fputs(pfetch(new), ofp);
922+ new++;
923+ }
924+ } else {
925+ if (pch_char(new) != ' ')
926+ fatal("Internal error: expected ' '\n");
927+ old++;
928+ new++;
929+ if (do_defines && def_state != OUTSIDE) {
930+ fputs(end_defined, ofp);
931+ def_state = OUTSIDE;
932+ }
933+ }
934+ }
935+ if (new <= pat_end && pch_char(new) == '+') {
936+ copy_till(where + old - 1, 0);
937+ if (do_defines) {
938+ if (def_state == OUTSIDE) {
939+ fputs(if_defined, ofp);
940+ def_state = IN_IFDEF;
941+ } else if (def_state == IN_IFNDEF) {
942+ fputs(else_defined, ofp);
943+ def_state = IN_ELSE;
944+ }
945+ }
946+ while (new <= pat_end && pch_char(new) == '+') {
947+ fputs(pfetch(new), ofp);
948+ new++;
949+ }
950+ }
951+ if (do_defines && def_state != OUTSIDE) {
952+ fputs(end_defined, ofp);
953+ }
954+#undef OUTSIDE
955+#undef IN_IFNDEF
956+#undef IN_IFDEF
957+#undef IN_ELSE
958+}
959+
960+static void
961+dump_input_line(LINENUM line, int write_newline)
962+{
963+ char *s = ifetch(line, 0);
964+ if (s == NULL)
965+ return;
966+ for (; *s != '\n' && *s != '\0'; s++)
967+ putc(*s, ofp);
968+ if (write_newline)
969+ putc('\n', ofp);
970+}
971+
972+static void
973+copy_till(LINENUM line, int end)
974+{
975+ if (last_frozen_line > line)
976+ fatal("misordered hunks! output would be garbled\n");
977+ while (last_frozen_line < line) {
978+ if (++last_frozen_line == line && end)
979+ dump_input_line(last_frozen_line, !last_line_missing_eol);
980+ else
981+ dump_input_line(last_frozen_line, 1);
982+ }
983+}
984+
985+static int
986+spew_output(void)
987+{
988+ if (input_lines)
989+ copy_till(input_lines, 1);
990+ if (ferror(ofp) || fclose(ofp))
991+ return 0;
992+ ofp = NULL;
993+ return 1;
994+}
995+
996+static void
997+init_output(const char *name)
998+{
999+ ofp = fopen(name, "w");
1000+ if (ofp == NULL)
1001+ pfatal("cant create %s", name);
1002+}
1003+
1004+static void
1005+init_reject(const char *name)
1006+{
1007+ rejfp = fopen(name, "w");
1008+ if (rejfp == NULL)
1009+ pfatal("cant create %s", name);
1010+}
1011+
1012+void
1013+re_input(void)
1014+{
1015+ if (using_plan_a) {
1016+ i_size = 0;
1017+ free(i_ptr);
1018+ i_ptr = NULL;
1019+ if (i_womp != NULL) {
1020+ munmap(i_womp, i_size);
1021+ i_womp = NULL;
1022+ }
1023+ } else {
1024+ using_plan_a = 1;
1025+ close(tifd);
1026+ tifd = -1;
1027+ free(tibuf[0]);
1028+ free(tibuf[1]);
1029+ tibuf[0] = tibuf[1] = NULL;
1030+ tiline[0] = tiline[1] = -1;
1031+ tireclen = 0;
1032+ }
1033+}
1034+
1035+void
1036+scan_input(const char *filename)
1037+{
1038+ if (!plan_a(filename))
1039+ plan_b(filename);
1040+ if (verbose) {
1041+ say("Patching file %s using Plan %s...\n", filename, (using_plan_a ? "A" : "B"));
1042+ }
1043+}
1044+
1045+static int
1046+reallocate_lines(size_t *lines_allocated)
1047+{
1048+ char **p;
1049+ size_t new_size;
1050+
1051+ new_size = *lines_allocated * 3 / 2;
1052+ p = pch_realloc(i_ptr, new_size + 2, sizeof(char *));
1053+ if (p == NULL) {
1054+ munmap(i_womp, i_size);
1055+ i_womp = NULL;
1056+ free(i_ptr);
1057+ i_ptr = NULL;
1058+ *lines_allocated = 0;
1059+ return 0;
1060+ }
1061+ *lines_allocated = new_size;
1062+ i_ptr = p;
1063+ return 1;
1064+}
1065+
1066+static int
1067+plan_a(const char *filename)
1068+{
1069+ int ifd, statfailed, devnull, pstat;
1070+ char *p, *s, *lbuf;
1071+ struct stat filestat;
1072+ off_t i;
1073+ ptrdiff_t sz;
1074+ size_t iline, lines_allocated, lbufsz;
1075+ pid_t pid;
1076+ char *argp[4] = {NULL};
1077+
1078+#ifdef DEBUGGING
1079+ if (debug & 8)
1080+ return 0;
1081+#endif
1082+
1083+ if (filename == NULL || *filename == '\0')
1084+ return 0;
1085+
1086+ statfailed = stat(filename, &filestat);
1087+ if (statfailed && ok_to_create_file) {
1088+ if (verbose)
1089+ say("(Creating file %s...)\n", filename);
1090+ if (check_only)
1091+ return 1;
1092+ makedirs(filename, 1);
1093+ close(creat(filename, 0666));
1094+ statfailed = stat(filename, &filestat);
1095+ }
1096+ if (statfailed && check_only)
1097+ fatal("%s not found, -C mode, cant probe further\n", filename);
1098+
1099+ if (statfailed || (filestat.st_mode & 0222) == 0 ||
1100+ ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
1101+ char *filebase, *filedir;
1102+ struct stat cstat;
1103+ char *tmp_filename1, *tmp_filename2;
1104+
1105+ tmp_filename1 = strdup(filename);
1106+ tmp_filename2 = strdup(filename);
1107+ if (tmp_filename1 == NULL || tmp_filename2 == NULL)
1108+ fatal("strdupping filename");
1109+ filebase = basename(tmp_filename1);
1110+ filedir = dirname(tmp_filename2);
1111+
1112+ lbufsz = INITLINELEN;
1113+ if ((lbuf = malloc(bufsz)) == NULL)
1114+ pfatal("allocating line buffer");
1115+ lbuf[0] = '\0';
1116+
1117+#define try(f, a1, a2, a3) \
1118+ (snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
1119+
1120+ if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
1121+ try("%s/RCS/%s%s", filedir, filebase, "") ||
1122+ try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
1123+ if (!statfailed) {
1124+ if ((filestat.st_mode & 0222) != 0)
1125+ fatal("file %s seems to be locked by somebody else under RCS\n", filename);
1126+ if (verbose)
1127+ say("Comparing file %s to default RCS version...\n", filename);
1128+ switch (pid = fork()) {
1129+ case -1:
1130+ fatal("cant fork: %s\n", strerror(errno));
1131+ case 0:
1132+ devnull = open("/dev/null", O_RDONLY);
1133+ if (devnull == -1)
1134+ fatal("cant open /dev/null: %s", strerror(errno));
1135+ dup2(devnull, STDOUT_FILENO);
1136+ argp[0] = __UNCONST(RCSDIFF);
1137+ argp[1] = __UNCONST(filename);
1138+ execv(RCSDIFF, argp);
1139+ exit(127);
1140+ }
1141+ pid = waitpid(pid, &pstat, 0);
1142+ if (pid == -1 || WEXITSTATUS(pstat) != 0)
1143+ fatal("cant check out file %s: differs from default RCS version\n", filename);
1144+ }
1145+ if (verbose)
1146+ say("Checking out file %s from RCS...\n", filename);
1147+ switch (pid = fork()) {
1148+ case -1:
1149+ fatal("cant fork: %s\n", strerror(errno));
1150+ case 0:
1151+ argp[0] = __UNCONST(CHECKOUT);
1152+ argp[1] = __UNCONST("-l");
1153+ argp[2] = __UNCONST(filename);
1154+ execv(CHECKOUT, argp);
1155+ exit(127);
1156+ }
1157+ pid = waitpid(pid, &pstat, 0);
1158+ if (pid == -1 || WEXITSTATUS(pstat) != 0 || stat(filename, &filestat))
1159+ fatal("cant check out file %s from RCS\n", filename);
1160+ } else if (statfailed) {
1161+ fatal("cant find %s\n", filename);
1162+ }
1163+#undef try
1164+ free(lbuf);
1165+ free(tmp_filename1);
1166+ free(tmp_filename2);
1167+ }
1168+
1169+ filemode = filestat.st_mode;
1170+ if (!S_ISREG(filemode))
1171+ fatal("%s is not a normal file--cant patch\n", filename);
1172+ i_size = filestat.st_size;
1173+ if (out_of_mem) {
1174+ set_hunkmax();
1175+ out_of_mem = 0;
1176+ return 0;
1177+ }
1178+ if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
1179+ say("block too large to mmap\n");
1180+ return 0;
1181+ }
1182+ if ((ifd = open(filename, O_RDONLY)) < 0)
1183+ pfatal("cant open file %s", filename);
1184+
1185+ if (i_size) {
1186+ i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
1187+ if (i_womp == MAP_FAILED) {
1188+ perror("mmap failed");
1189+ i_womp = NULL;
1190+ close(ifd);
1191+ return 0;
1192+ }
1193+ } else
1194+ i_womp = NULL;
1195+
1196+ close(ifd);
1197+ if (i_size)
1198+ madvise(i_womp, i_size, MADV_SEQUENTIAL);
1199+
1200+ lines_allocated = i_size / 25;
1201+ if (lines_allocated < 100)
1202+ lines_allocated = 100;
1203+
1204+ if (!reallocate_lines(&lines_allocated))
1205+ return 0;
1206+
1207+ iline = 1;
1208+ i_ptr[iline] = i_womp;
1209+ for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
1210+ if (*s == '\n') {
1211+ if (iline == lines_allocated) {
1212+ if (!reallocate_lines(&lines_allocated))
1213+ return 0;
1214+ }
1215+ i_ptr[++iline] = s + 1;
1216+ }
1217+ }
1218+ if (i_size > 0 && i_womp[i_size - 1] != '\n') {
1219+ last_line_missing_eol = 1;
1220+ sz = s - i_ptr[iline];
1221+ p = malloc(sz + 1);
1222+ if (p == NULL) {
1223+ free(i_ptr);
1224+ i_ptr = NULL;
1225+ munmap(i_womp, i_size);
1226+ i_womp = NULL;
1227+ return 0;
1228+ }
1229+ memcpy(p, i_ptr[iline], sz);
1230+ p[sz] = '\n';
1231+ i_ptr[iline] = p;
1232+ i_ptr[++iline] = empty_line;
1233+ } else
1234+ last_line_missing_eol = 0;
1235+
1236+ input_lines = iline - 1;
1237+
1238+ if (revision != NULL) {
1239+ if (!rev_in_string(i_womp)) {
1240+ if (force) {
1241+ if (verbose)
1242+ say("Warning: this file doesnt appear to be the %s version--patching anyway\n", revision);
1243+ } else if (batch) {
1244+ fatal("this file doesnt appear to be the %s version--aborting\n", revision);
1245+ } else {
1246+ ask("This file doesnt appear to be the %s version--patch anyway? [n] ", revision);
1247+ if (*buf != 'y')
1248+ fatal("aborted\n");
1249+ }
1250+ } else if (verbose)
1251+ say("Good. This file appears to be the %s version.\n", revision);
1252+ }
1253+ return 1;
1254+}
1255+
1256+static void
1257+plan_b(const char *filename)
1258+{
1259+ FILE *ifp;
1260+ size_t i = 0, j, maxlen = 1;
1261+ char *p;
1262+ int found_revision = (revision == NULL);
1263+
1264+ using_plan_a = 0;
1265+ if ((ifp = fopen(filename, "r")) == NULL)
1266+ pfatal("cant open file %s", filename);
1267+ unlink(TMPINNAME);
1268+ if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
1269+ pfatal("cant open file %s", TMPINNAME);
1270+ while (getline(&buf, &bufsz, ifp) != -1) {
1271+ if (revision != NULL && !found_revision && rev_in_string(buf))
1272+ found_revision = 1;
1273+ if ((i = strlen(buf)) > maxlen)
1274+ maxlen = i;
1275+ }
1276+ last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
1277+ if (last_line_missing_eol && maxlen == i)
1278+ maxlen++;
1279+
1280+ if (revision != NULL) {
1281+ if (!found_revision) {
1282+ if (force) {
1283+ if (verbose)
1284+ say("Warning: this file doesnt appear to be the %s version--patching anyway\n", revision);
1285+ } else if (batch) {
1286+ fatal("this file doesnt appear to be the %s version--aborting\n", revision);
1287+ } else {
1288+ ask("This file doesnt appear to be the %s version--patch anyway? [n] ", revision);
1289+ if (*buf != 'y')
1290+ fatal("aborted\n");
1291+ }
1292+ } else if (verbose)
1293+ say("Good. This file appears to be the %s version.\n", revision);
1294+ }
1295+ fseek(ifp, 0L, SEEK_SET);
1296+ lines_per_buf = BUFFERSIZE / maxlen;
1297+ tireclen = maxlen;
1298+ tibuf[0] = malloc(BUFFERSIZE + 1);
1299+ if (tibuf[0] == NULL)
1300+ fatal("out of memory\n");
1301+ tibuf[1] = malloc(BUFFERSIZE + 1);
1302+ if (tibuf[1] == NULL)
1303+ fatal("out of memory\n");
1304+ for (i = 1;; i++) {
1305+ p = tibuf[0] + maxlen * (i % lines_per_buf);
1306+ if (i % lines_per_buf == 0) {
1307+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1308+ pfatal("cant write temp file");
1309+ }
1310+ if (fgets(p, maxlen + 1, ifp) == NULL) {
1311+ input_lines = i - 1;
1312+ if (i % lines_per_buf != 0) {
1313+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1314+ pfatal("cant write temp file");
1315+ }
1316+ break;
1317+ }
1318+ j = strlen(p);
1319+ if (j == 0 || p[j - 1] != '\n')
1320+ p[j] = '\n';
1321+ }
1322+ fclose(ifp);
1323+ close(tifd);
1324+ if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
1325+ pfatal("cant reopen file %s", TMPINNAME);
1326+}
1327+
1328+char *
1329+ifetch(LINENUM line, int whichbuf)
1330+{
1331+ if (line < 1 || line > input_lines) {
1332+ if (warn_on_invalid_line) {
1333+ say("No such line %ld in input file, ignoring\n", line);
1334+ warn_on_invalid_line = 0;
1335+ }
1336+ return NULL;
1337+ }
1338+ if (using_plan_a)
1339+ return i_ptr[line];
1340+ else {
1341+ LINENUM offline = line % lines_per_buf;
1342+ LINENUM baseline = line - offline;
1343+
1344+ if (tiline[0] == baseline)
1345+ whichbuf = 0;
1346+ else if (tiline[1] == baseline)
1347+ whichbuf = 1;
1348+ else {
1349+ tiline[whichbuf] = baseline;
1350+ if (lseek(tifd, (off_t)(baseline / lines_per_buf * BUFFERSIZE), SEEK_SET) < 0)
1351+ pfatal("cannot seek in the temporary input file");
1352+ if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
1353+ pfatal("error reading tmp file %s", TMPINNAME);
1354+ }
1355+ return tibuf[whichbuf] + (tireclen * offline);
1356+ }
1357+}
1358+
1359+static int
1360+rev_in_string(const char *string)
1361+{
1362+ const char *s;
1363+ size_t patlen;
1364+
1365+ if (revision == NULL)
1366+ return 1;
1367+ patlen = strlen(revision);
1368+ if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
1369+ return 1;
1370+ for (s = string; *s; s++) {
1371+ if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
1372+ isspace((unsigned char)s[patlen + 1]))
1373+ return 1;
1374+ }
1375+ return 0;
1376+}
1377+
1378+void
1379+re_patch(void)
1380+{
1381+ p_first = 0;
1382+ p_newfirst = 0;
1383+ p_ptrn_lines = 0;
1384+ p_repl_lines = 0;
1385+ p_end = (LINENUM)-1;
1386+ p_max = 0;
1387+ p_indent = 0;
1388+}
1389+
1390+void
1391+open_patch_file(const char *filename)
1392+{
1393+ struct stat filestat;
1394+
1395+ if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
1396+ pfp = fopen(TMPPATNAME, "w");
1397+ if (pfp == NULL)
1398+ pfatal("cant create %s", TMPPATNAME);
1399+ while (getline(&buf, &bufsz, stdin) != -1)
1400+ fprintf(pfp, "%s", buf);
1401+ if (ferror(pfp) || fclose(pfp))
1402+ pfatal("cant write %s", TMPPATNAME);
1403+ filename = TMPPATNAME;
1404+ }
1405+ pfp = fopen(filename, "r");
1406+ if (pfp == NULL)
1407+ pfatal("patch file %s not found", filename);
1408+ fstat(fileno(pfp), &filestat);
1409+ p_filesize = filestat.st_size;
1410+ next_intuit_at(0L, 1L);
1411+ set_hunkmax();
1412+}
1413+
1414+void
1415+set_hunkmax(void)
1416+{
1417+ if (p_line == NULL)
1418+ p_line = calloc((size_t)hunkmax, sizeof(char *));
1419+ if (p_len == NULL)
1420+ p_len = calloc((size_t)hunkmax, sizeof(ssize_t));
1421+ if (p_char == NULL)
1422+ p_char = calloc((size_t)hunkmax, sizeof(char));
1423+}
1424+
1425+static void
1426+grow_hunkmax(void)
1427+{
1428+ int new_hunkmax;
1429+ char **new_p_line;
1430+ ssize_t *new_p_len;
1431+ char *new_p_char;
1432+
1433+ new_hunkmax = hunkmax * 2;
1434+ if (p_line == NULL || p_len == NULL || p_char == NULL)
1435+ fatal("Internal memory allocation error\n");
1436+
1437+ new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
1438+ if (new_p_line == NULL)
1439+ free(p_line);
1440+ new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
1441+ if (new_p_len == NULL)
1442+ free(p_len);
1443+ new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
1444+ if (new_p_char == NULL)
1445+ free(p_char);
1446+
1447+ p_char = new_p_char;
1448+ p_len = new_p_len;
1449+ p_line = new_p_line;
1450+
1451+ if (p_line != NULL && p_len != NULL && p_char != NULL) {
1452+ hunkmax = new_hunkmax;
1453+ return;
1454+ }
1455+
1456+ if (!using_plan_a)
1457+ fatal("out of memory\n");
1458+ out_of_mem = 1;
1459+}
1460+
1461+int
1462+there_is_another_patch(void)
1463+{
1464+ int exists = 0;
1465+
1466+ if (p_base != 0L && p_base >= p_filesize) {
1467+ if (verbose)
1468+ say("done\n");
1469+ return 0;
1470+ }
1471+ if (verbose)
1472+ say("Hmm...");
1473+ diff_type = intuit_diff_type();
1474+ if (!diff_type) {
1475+ if (p_base != 0L) {
1476+ if (verbose)
1477+ say(" Ignoring the trailing garbage.\ndone\n");
1478+ } else
1479+ say(" I cant seem to find a patch in there anywhere\n");
1480+ return 0;
1481+ }
1482+ if (verbose) {
1483+ say(" %sooks like %s to me...\n",
1484+ (p_base == 0L ? "L" : "The next patch l"),
1485+ diff_type == UNI_DIFF ? "a unified diff" :
1486+ diff_type == CONTEXT_DIFF ? "a context diff" :
1487+ diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
1488+ diff_type == NORMAL_DIFF ? "a normal diff" : "an ed script");
1489+ }
1490+ if (p_indent && verbose)
1491+ say("(Patch is indented %d space%s.)\n", p_indent, p_indent == 1 ? "" : "s");
1492+ skip_to(p_start, p_sline);
1493+ while (filearg[0] == NULL) {
1494+ if (force || batch) {
1495+ say("No file to patch. Skipping...\n");
1496+ filearg[0] = savestr(bestguess);
1497+ skip_rest_of_patch = 1;
1498+ return 1;
1499+ }
1500+ ask("File to patch: ");
1501+ if (*buf != '\n') {
1502+ free(bestguess);
1503+ bestguess = savestr(buf);
1504+ filearg[0] = fetchname(buf, &exists, 0);
1505+ }
1506+ if (!exists) {
1507+ ask("No file found--skip this patch? [n] ");
1508+ if (*buf != 'y')
1509+ continue;
1510+ if (verbose)
1511+ say("Skipping patch...\n");
1512+ free(filearg[0]);
1513+ filearg[0] = fetchname(bestguess, &exists, 0);
1514+ skip_rest_of_patch = 1;
1515+ return 1;
1516+ }
1517+ }
1518+ return 1;
1519+}
1520+
1521+static int
1522+intuit_diff_type(void)
1523+{
1524+ long this_line = 0, previous_line;
1525+ long first_command_line = -1;
1526+ LINENUM fcl_line = -1;
1527+ int last_line_was_command = 0, this_is_a_command = 0;
1528+ int stars_last_line = 0, stars_this_line = 0;
1529+ char *s, *t;
1530+ int indent, retval;
1531+ struct FileName names[MAX_FILE];
1532+
1533+ memset(names, 0, sizeof(names));
1534+ ok_to_create_file = 0;
1535+ fseek(pfp, p_base, SEEK_SET);
1536+ p_input_line = p_bline - 1;
1537+ for (;;) {
1538+ previous_line = this_line;
1539+ last_line_was_command = this_is_a_command;
1540+ stars_last_line = stars_this_line;
1541+ this_line = ftell(pfp);
1542+ indent = 0;
1543+ p_input_line++;
1544+ if (getline(&buf, &bufsz, pfp) == -1) {
1545+ if (first_command_line >= 0L) {
1546+ p_start = first_command_line;
1547+ p_sline = fcl_line;
1548+ retval = ED_DIFF;
1549+ goto scan_exit;
1550+ } else {
1551+ p_start = this_line;
1552+ p_sline = p_input_line;
1553+ retval = 0;
1554+ goto scan_exit;
1555+ }
1556+ }
1557+ for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
1558+ if (*s == '\t')
1559+ indent += 8 - (indent % 8);
1560+ else
1561+ indent++;
1562+ }
1563+ for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
1564+ ;
1565+ this_is_a_command = (isdigit((unsigned char)*s) && (*t == 'd' || *t == 'c' || *t == 'a'));
1566+ if (first_command_line < 0L && this_is_a_command) {
1567+ first_command_line = this_line;
1568+ fcl_line = p_input_line;
1569+ p_indent = indent;
1570+ }
1571+ if (!stars_last_line && strnEQ(s, "*** ", 4)) {
1572+ names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1573+ } else if (strnEQ(s, "--- ", 4)) {
1574+ names[NEW_FILE].path = fetchname(s + 4, &names[NEW_FILE].exists, strippath);
1575+ } else if (strnEQ(s, "+++ ", 4)) {
1576+ names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1577+ } else if (strnEQ(s, "Index:", 6)) {
1578+ names[INDEX_FILE].path = fetchname(s + 6, &names[INDEX_FILE].exists, strippath);
1579+ } else if (strnEQ(s, "Prereq:", 7)) {
1580+ for (t = s + 7; isspace((unsigned char)*t); t++)
1581+ ;
1582+ revision = savestr(t);
1583+ for (t = revision; *t && !isspace((unsigned char)*t); t++)
1584+ ;
1585+ *t = '\0';
1586+ if (*revision == '\0') {
1587+ free(revision);
1588+ revision = NULL;
1589+ }
1590+ }
1591+ if ((!diff_type || diff_type == ED_DIFF) && first_command_line >= 0L && strEQ(s, ".\n")) {
1592+ p_indent = indent;
1593+ p_start = first_command_line;
1594+ p_sline = fcl_line;
1595+ retval = ED_DIFF;
1596+ goto scan_exit;
1597+ }
1598+ if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
1599+ if (strnEQ(s + 4, "0,0", 3))
1600+ ok_to_create_file = 1;
1601+ p_indent = indent;
1602+ p_start = this_line;
1603+ p_sline = p_input_line;
1604+ retval = UNI_DIFF;
1605+ goto scan_exit;
1606+ }
1607+ stars_this_line = strnEQ(s, "********", 8);
1608+ if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && strnEQ(s, "*** ", 4)) {
1609+ if (atol(s + 4) == 0)
1610+ ok_to_create_file = 1;
1611+ while (*s != '\n')
1612+ s++;
1613+ p_indent = indent;
1614+ p_start = previous_line;
1615+ p_sline = p_input_line - 1;
1616+ retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
1617+ goto scan_exit;
1618+ }
1619+ if ((!diff_type || diff_type == NORMAL_DIFF) && last_line_was_command &&
1620+ (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
1621+ p_start = previous_line;
1622+ p_sline = p_input_line - 1;
1623+ p_indent = indent;
1624+ retval = NORMAL_DIFF;
1625+ goto scan_exit;
1626+ }
1627+ }
1628+scan_exit:
1629+ if (retval == UNI_DIFF) {
1630+ struct FileName tmp = names[OLD_FILE];
1631+ names[OLD_FILE] = names[NEW_FILE];
1632+ names[NEW_FILE] = tmp;
1633+ }
1634+ if (filearg[0] == NULL) {
1635+ if (posix)
1636+ filearg[0] = posix_name(names, ok_to_create_file);
1637+ else {
1638+ if (names[OLD_FILE].path != NULL || names[NEW_FILE].path != NULL) {
1639+ free(names[INDEX_FILE].path);
1640+ names[INDEX_FILE].path = NULL;
1641+ }
1642+ filearg[0] = best_name(names, ok_to_create_file);
1643+ }
1644+ }
1645+ free(bestguess);
1646+ bestguess = NULL;
1647+ if (filearg[0] != NULL)
1648+ bestguess = savestr(filearg[0]);
1649+ else if (!ok_to_create_file) {
1650+ if (posix)
1651+ bestguess = posix_name(names, 1);
1652+ else
1653+ bestguess = best_name(names, 1);
1654+ }
1655+ free(names[OLD_FILE].path);
1656+ free(names[NEW_FILE].path);
1657+ free(names[INDEX_FILE].path);
1658+ return retval;
1659+}
1660+
1661+static void
1662+next_intuit_at(LINENUM file_pos, LINENUM file_line)
1663+{
1664+ p_base = file_pos;
1665+ p_bline = file_line;
1666+}
1667+
1668+static void
1669+skip_to(LINENUM file_pos, LINENUM file_line)
1670+{
1671+ int ret;
1672+
1673+ if (p_base > file_pos)
1674+ fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
1675+ if (verbose && p_base < file_pos) {
1676+ fseek(pfp, p_base, SEEK_SET);
1677+ say("The text leading up to this was:\n--------------------------\n");
1678+ while (ftell(pfp) < file_pos) {
1679+ ret = getline(&buf, &bufsz, pfp);
1680+ if (ret == -1)
1681+ fatal("Unexpected end of file\n");
1682+ say("|%s", buf);
1683+ }
1684+ say("--------------------------\n");
1685+ } else
1686+ fseek(pfp, file_pos, SEEK_SET);
1687+ p_input_line = file_line - 1;
1688+}
1689+
1690+__dead static void
1691+malformed(void)
1692+{
1693+ fatal("malformed patch at line %ld: %s", p_input_line, buf);
1694+}
1695+
1696+static LINENUM
1697+getlinenum(const char *s)
1698+{
1699+ LINENUM l = (LINENUM)atol(s);
1700+ if (l < 0) {
1701+ l = 0;
1702+ malformed();
1703+ }
1704+ return l;
1705+}
1706+
1707+static LINENUM
1708+getskiplinenum(char **p)
1709+{
1710+ char *s = *p;
1711+ LINENUM l = getlinenum(s);
1712+ while (isdigit((unsigned char)*s))
1713+ s++;
1714+ *p = s;
1715+ return l;
1716+}
1717+
1718+static int
1719+remove_special_line(void)
1720+{
1721+ int c;
1722+
1723+ c = fgetc(pfp);
1724+ if (c == '\\') {
1725+ do {
1726+ c = fgetc(pfp);
1727+ } while (c != EOF && c != '\n');
1728+ return 1;
1729+ }
1730+ if (c != EOF)
1731+ fseek(pfp, -1L, SEEK_CUR);
1732+ return 0;
1733+}
1734+
1735+int
1736+another_hunk(void)
1737+{
1738+ long line_beginning;
1739+ LINENUM repl_beginning;
1740+ LINENUM fillcnt;
1741+ LINENUM fillsrc;
1742+ LINENUM filldst;
1743+ int ptrn_spaces_eaten;
1744+ int repl_could_be_missing;
1745+ int repl_missing;
1746+ long repl_backtrack_position;
1747+ LINENUM repl_patch_line;
1748+ LINENUM ptrn_copiable;
1749+ char *s;
1750+ int context = 0;
1751+ int ret;
1752+
1753+ while (p_end >= 0) {
1754+ if (p_end == p_efake)
1755+ p_end = p_bfake;
1756+ else
1757+ free(p_line[p_end]);
1758+ p_end--;
1759+ }
1760+ p_efake = -1;
1761+ p_max = hunkmax;
1762+
1763+ if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
1764+ line_beginning = ftell(pfp);
1765+ repl_beginning = 0;
1766+ fillcnt = 0;
1767+ fillsrc = 0;
1768+ filldst = 0;
1769+ ptrn_spaces_eaten = 0;
1770+ repl_could_be_missing = 1;
1771+ repl_missing = 0;
1772+ repl_backtrack_position = 0;
1773+ repl_patch_line = 0;
1774+ ptrn_copiable = 0;
1775+
1776+ ret = pgetline(&buf, &bufsz, pfp);
1777+ p_input_line++;
1778+ if (ret == -1 || strnNE(buf, "********", 8)) {
1779+ next_intuit_at(line_beginning, p_input_line);
1780+ return 0;
1781+ }
1782+ p_context = 100;
1783+ p_hunk_beg = p_input_line + 1;
1784+ while (p_end < p_max) {
1785+ ret = pgetline(&buf, &bufsz, pfp);
1786+ p_input_line++;
1787+ if (ret == -1) {
1788+ if (repl_beginning && repl_could_be_missing) {
1789+ repl_missing = 1;
1790+ goto hunk_done;
1791+ }
1792+ fatal("unexpected end of file in patch\n");
1793+ }
1794+ p_end++;
1795+ if (p_end >= hunkmax)
1796+ fatal("Internal error: hunk larger than hunk buffer size");
1797+ p_char[p_end] = *buf;
1798+ p_line[p_end] = NULL;
1799+ switch (*buf) {
1800+ case '*':
1801+ if (strnEQ(buf, "********", 8)) {
1802+ if (repl_beginning && repl_could_be_missing) {
1803+ repl_missing = 1;
1804+ goto hunk_done;
1805+ } else
1806+ fatal("unexpected end of hunk at line %ld\n", p_input_line);
1807+ }
1808+ if (p_end != 0) {
1809+ if (repl_beginning && repl_could_be_missing) {
1810+ repl_missing = 1;
1811+ goto hunk_done;
1812+ }
1813+ fatal("unexpected *** at line %ld: %s", p_input_line, buf);
1814+ }
1815+ context = 0;
1816+ p_line[p_end] = savestr(buf);
1817+ if (out_of_mem) {
1818+ p_end--;
1819+ return 0;
1820+ }
1821+ for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1822+ ;
1823+ if (!*s)
1824+ malformed();
1825+ if (strnEQ(s, "0,0", 3))
1826+ memmove(s, s + 2, strlen(s + 2) + 1);
1827+ p_first = getskiplinenum(&s);
1828+ if (*s == ',') {
1829+ for (; *s && !isdigit((unsigned char)*s); s++)
1830+ ;
1831+ if (!*s)
1832+ malformed();
1833+ p_ptrn_lines = (getlinenum(s)) - p_first + 1;
1834+ if (p_ptrn_lines < 0)
1835+ malformed();
1836+ } else if (p_first)
1837+ p_ptrn_lines = 1;
1838+ else {
1839+ p_ptrn_lines = 0;
1840+ p_first = 1;
1841+ }
1842+ if (p_first >= LINENUM_MAX - p_ptrn_lines || p_ptrn_lines >= LINENUM_MAX - 6)
1843+ malformed();
1844+ p_max = p_ptrn_lines + 6;
1845+ while (p_max >= hunkmax)
1846+ grow_hunkmax();
1847+ p_max = hunkmax;
1848+ break;
1849+ case '-':
1850+ if (buf[1] == '-') {
1851+ if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_char[p_end - 1] == '\n'))) {
1852+ if (p_end == 1) {
1853+ p_end = p_ptrn_lines + 1;
1854+ fillsrc = p_end + 1;
1855+ filldst = 1;
1856+ fillcnt = p_ptrn_lines;
1857+ } else {
1858+ if (repl_beginning) {
1859+ if (repl_could_be_missing) {
1860+ repl_missing = 1;
1861+ goto hunk_done;
1862+ }
1863+ fatal("duplicate \"---\" at line %ld\n", p_input_line);
1864+ } else {
1865+ fatal("premature/overdue \"---\" at line %ld\n", p_input_line);
1866+ }
1867+ }
1868+ }
1869+ repl_beginning = p_end;
1870+ repl_backtrack_position = ftell(pfp);
1871+ repl_patch_line = p_input_line;
1872+ p_line[p_end] = savestr(buf);
1873+ if (out_of_mem) {
1874+ p_end--;
1875+ return 0;
1876+ }
1877+ p_char[p_end] = '=';
1878+ for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1879+ ;
1880+ if (!*s)
1881+ malformed();
1882+ p_newfirst = getskiplinenum(&s);
1883+ if (*s == ',') {
1884+ for (; *s && !isdigit((unsigned char)*s); s++)
1885+ ;
1886+ if (!*s)
1887+ malformed();
1888+ p_repl_lines = (getlinenum(s)) - p_newfirst + 1;
1889+ if (p_repl_lines < 0)
1890+ malformed();
1891+ } else if (p_newfirst)
1892+ p_repl_lines = 1;
1893+ else {
1894+ p_repl_lines = 0;
1895+ p_newfirst = 1;
1896+ }
1897+ if (p_newfirst >= LINENUM_MAX - p_repl_lines || p_repl_lines >= LINENUM_MAX - p_end)
1898+ malformed();
1899+ p_max = p_repl_lines + p_end;
1900+ if (p_max > MAXHUNKSIZE)
1901+ fatal("hunk too large at line %ld\n", p_input_line);
1902+ while (p_max >= hunkmax)
1903+ grow_hunkmax();
1904+ if (p_repl_lines != ptrn_copiable && (p_context != 0 || p_repl_lines != 1))
1905+ repl_could_be_missing = 0;
1906+ break;
1907+ }
1908+ goto change_line;
1909+ case '+':
1910+ case '!':
1911+ repl_could_be_missing = 0;
1912+ change_line:
1913+ if (buf[1] == '\n' && canonicalize)
1914+ strlcpy(buf + 1, " \n", bufsz - 1);
1915+ if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' &&
1916+ repl_beginning && repl_could_be_missing) {
1917+ repl_missing = 1;
1918+ goto hunk_done;
1919+ }
1920+ if (context >= 0) {
1921+ if (context < p_context)
1922+ p_context = context;
1923+ context = -1000;
1924+ }
1925+ p_line[p_end] = savestr(buf + 2);
1926+ if (out_of_mem) {
1927+ p_end--;
1928+ return 0;
1929+ }
1930+ if (p_end == p_ptrn_lines) {
1931+ if (remove_special_line()) {
1932+ int length = strlen(p_line[p_end]) - 1;
1933+ (p_line[p_end])[length] = 0;
1934+ }
1935+ }
1936+ break;
1937+ case '\t':
1938+ case '\n':
1939+ if (repl_beginning && repl_could_be_missing &&
1940+ (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) {
1941+ repl_missing = 1;
1942+ goto hunk_done;
1943+ }
1944+ p_line[p_end] = savestr(buf);
1945+ if (out_of_mem) {
1946+ p_end--;
1947+ return 0;
1948+ }
1949+ if (p_end != p_ptrn_lines + 1) {
1950+ ptrn_spaces_eaten |= (repl_beginning != 0);
1951+ context++;
1952+ if (!repl_beginning)
1953+ ptrn_copiable++;
1954+ p_char[p_end] = ' ';
1955+ }
1956+ break;
1957+ case ' ':
1958+ if (!isspace((unsigned char)buf[1]) && repl_beginning && repl_could_be_missing) {
1959+ repl_missing = 1;
1960+ goto hunk_done;
1961+ }
1962+ context++;
1963+ if (!repl_beginning)
1964+ ptrn_copiable++;
1965+ p_line[p_end] = savestr(buf + 2);
1966+ if (out_of_mem) {
1967+ p_end--;
1968+ return 0;
1969+ }
1970+ break;
1971+ default:
1972+ if (repl_beginning && repl_could_be_missing) {
1973+ repl_missing = 1;
1974+ goto hunk_done;
1975+ }
1976+ malformed();
1977+ }
1978+ if (p_line[p_end])
1979+ p_len[p_end] = strlen(p_line[p_end]);
1980+ else
1981+ p_len[p_end] = 0;
1982+ }
1983+
1984+hunk_done:
1985+ if (p_end >= 0 && !repl_beginning)
1986+ fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
1987+
1988+ if (repl_missing) {
1989+ p_input_line = repl_patch_line;
1990+ for (p_end--; p_end > repl_beginning; p_end--)
1991+ free(p_line[p_end]);
1992+ fseek(pfp, repl_backtrack_position, SEEK_SET);
1993+
1994+ if (!p_context && p_repl_lines == 1) {
1995+ p_repl_lines = 0;
1996+ p_max--;
1997+ }
1998+ fillsrc = 1;
1999+ filldst = repl_beginning + 1;
2000+ fillcnt = p_repl_lines;
2001+ p_end = p_max;
2002+ } else if (!p_context && fillcnt == 1) {
2003+ while (filldst < p_end) {
2004+ p_line[filldst] = p_line[filldst + 1];
2005+ p_char[filldst] = p_char[filldst + 1];
2006+ p_len[filldst] = p_len[filldst + 1];
2007+ filldst++;
2008+ }
2009+ p_end--;
2010+ p_first++;
2011+ fillcnt = 0;
2012+ p_ptrn_lines = 0;
2013+ }
2014+ if (diff_type == CONTEXT_DIFF && (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
2015+ diff_type = NEW_CONTEXT_DIFF;
2016+ }
2017+ if (fillcnt) {
2018+ p_bfake = filldst;
2019+ p_efake = filldst + fillcnt - 1;
2020+ while (fillcnt-- > 0) {
2021+ while (fillsrc <= p_end && p_char[fillsrc] != ' ')
2022+ fillsrc++;
2023+ if (fillsrc > p_end)
2024+ fatal("replacement text mangled in hunk at line %ld\n", p_hunk_beg);
2025+ p_line[filldst] = p_line[fillsrc];
2026+ p_char[filldst] = p_char[fillsrc];
2027+ p_len[filldst] = p_len[fillsrc];
2028+ fillsrc++;
2029+ filldst++;
2030+ }
2031+ while (fillsrc <= p_end && fillsrc != repl_beginning && p_char[fillsrc] != ' ')
2032+ fillsrc++;
2033+ if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
2034+ malformed();
2035+ if (filldst != p_end + 1 && filldst != repl_beginning)
2036+ malformed();
2037+ }
2038+ if (p_line[p_end] != NULL) {
2039+ if (remove_special_line()) {
2040+ p_len[p_end] -= 1;
2041+ (p_line[p_end])[p_len[p_end]] = 0;
2042+ }
2043+ }
2044+ } else if (diff_type == UNI_DIFF) {
2045+ LINENUM fillold;
2046+ LINENUM fillnew;
2047+ char ch;
2048+
2049+ line_beginning = ftell(pfp);
2050+ ret = pgetline(&buf, &bufsz, pfp);
2051+ p_input_line++;
2052+ if (ret == -1 || strnNE(buf, "@@ -", 4)) {
2053+ next_intuit_at(line_beginning, p_input_line);
2054+ return 0;
2055+ }
2056+ s = buf + 4;
2057+ if (!*s)
2058+ malformed();
2059+ p_first = getskiplinenum(&s);
2060+ if (*s == ',') {
2061+ s++;
2062+ p_ptrn_lines = getskiplinenum(&s);
2063+ } else
2064+ p_ptrn_lines = 1;
2065+ if (p_first >= LINENUM_MAX - p_ptrn_lines)
2066+ malformed();
2067+ if (*s == ' ')
2068+ s++;
2069+ if (*s != '+' || !*++s)
2070+ malformed();
2071+ p_newfirst = getskiplinenum(&s);
2072+ if (*s == ',') {
2073+ s++;
2074+ p_repl_lines = getskiplinenum(&s);
2075+ } else
2076+ p_repl_lines = 1;
2077+ if (*s == ' ')
2078+ s++;
2079+ if (*s != '@')
2080+ malformed();
2081+ if (p_first >= LINENUM_MAX - p_ptrn_lines ||
2082+ p_newfirst > LINENUM_MAX - p_repl_lines ||
2083+ p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2084+ malformed();
2085+ if (!p_ptrn_lines)
2086+ p_first++;
2087+ p_max = p_ptrn_lines + p_repl_lines + 1;
2088+ while (p_max >= hunkmax)
2089+ grow_hunkmax();
2090+ fillold = 1;
2091+ fillnew = fillold + p_ptrn_lines;
2092+ p_end = fillnew + p_repl_lines;
2093+ snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first, p_first + p_ptrn_lines - 1);
2094+ p_line[0] = savestr(buf);
2095+ if (out_of_mem) {
2096+ p_end = -1;
2097+ return 0;
2098+ }
2099+ p_char[0] = '*';
2100+ snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst, p_newfirst + p_repl_lines - 1);
2101+ p_line[fillnew] = savestr(buf);
2102+ if (out_of_mem) {
2103+ p_end = 0;
2104+ return 0;
2105+ }
2106+ p_char[fillnew++] = '=';
2107+ p_context = 100;
2108+ context = 0;
2109+ p_hunk_beg = p_input_line + 1;
2110+ while (fillold <= p_ptrn_lines || fillnew <= p_end) {
2111+ ret = pgetline(&buf, &bufsz, pfp);
2112+ p_input_line++;
2113+ if (ret == -1) {
2114+ if (p_max - fillnew < 3)
2115+ strlcpy(buf, " \n", bufsz);
2116+ else
2117+ fatal("unexpected end of file in patch\n");
2118+ }
2119+ if (*buf == '\t' || *buf == '\n') {
2120+ ch = ' ';
2121+ s = savestr(buf);
2122+ } else {
2123+ ch = *buf;
2124+ s = savestr(buf + 1);
2125+ }
2126+ if (out_of_mem) {
2127+ while (--fillnew > p_ptrn_lines)
2128+ free(p_line[fillnew]);
2129+ p_end = fillold - 1;
2130+ return 0;
2131+ }
2132+ switch (ch) {
2133+ case '-':
2134+ if (fillold > p_ptrn_lines) {
2135+ free(s);
2136+ p_end = fillnew - 1;
2137+ malformed();
2138+ }
2139+ p_char[fillold] = ch;
2140+ p_line[fillold] = s;
2141+ p_len[fillold++] = strlen(s);
2142+ if (fillold > p_ptrn_lines) {
2143+ if (remove_special_line()) {
2144+ p_len[fillold - 1] -= 1;
2145+ s[p_len[fillold - 1]] = 0;
2146+ }
2147+ }
2148+ break;
2149+ case '=':
2150+ ch = ' ';
2151+ /* FALL THROUGH */
2152+ case ' ':
2153+ if (fillold > p_ptrn_lines) {
2154+ free(s);
2155+ while (--fillnew > p_ptrn_lines)
2156+ free(p_line[fillnew]);
2157+ p_end = fillold - 1;
2158+ malformed();
2159+ }
2160+ context++;
2161+ p_char[fillold] = ch;
2162+ p_line[fillold] = s;
2163+ p_len[fillold++] = strlen(s);
2164+ s = savestr(s);
2165+ if (out_of_mem) {
2166+ while (--fillnew > p_ptrn_lines)
2167+ free(p_line[fillnew]);
2168+ p_end = fillold - 1;
2169+ return 0;
2170+ }
2171+ if (fillold > p_ptrn_lines) {
2172+ if (remove_special_line()) {
2173+ p_len[fillold - 1] -= 1;
2174+ s[p_len[fillold - 1]] = 0;
2175+ }
2176+ }
2177+ /* FALL THROUGH */
2178+ case '+':
2179+ if (fillnew > p_end) {
2180+ free(s);
2181+ while (--fillnew > p_ptrn_lines)
2182+ free(p_line[fillnew]);
2183+ p_end = fillold - 1;
2184+ malformed();
2185+ }
2186+ p_char[fillnew] = ch;
2187+ p_line[fillnew] = s;
2188+ p_len[fillnew++] = strlen(s);
2189+ if (fillold > p_ptrn_lines) {
2190+ if (remove_special_line()) {
2191+ p_len[fillnew - 1] -= 1;
2192+ s[p_len[fillnew - 1]] = 0;
2193+ }
2194+ }
2195+ break;
2196+ default:
2197+ p_end = fillnew;
2198+ malformed();
2199+ }
2200+ if (ch != ' ' && context > 0) {
2201+ if (context < p_context)
2202+ p_context = context;
2203+ context = -1000;
2204+ }
2205+ }
2206+ } else {
2207+ char hunk_type;
2208+ int i;
2209+ LINENUM min, max;
2210+
2211+ line_beginning = ftell(pfp);
2212+ p_context = 0;
2213+ ret = pgetline(&buf, &bufsz, pfp);
2214+ p_input_line++;
2215+ if (ret == -1 || !isdigit((unsigned char)*buf)) {
2216+ next_intuit_at(line_beginning, p_input_line);
2217+ return 0;
2218+ }
2219+ s = buf;
2220+ p_first = getskiplinenum(&s);
2221+ if (*s == ',') {
2222+ s++;
2223+ p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
2224+ } else
2225+ p_ptrn_lines = (*s != 'a');
2226+ if (p_first >= LINENUM_MAX - p_ptrn_lines)
2227+ malformed();
2228+ hunk_type = *s++;
2229+ if (hunk_type == 'a')
2230+ p_first++;
2231+ min = getskiplinenum(&s);
2232+ if (*s == ',')
2233+ max = getlinenum(++s);
2234+ else
2235+ max = min;
2236+ if (min < 0 || min > max || max - min == LINENUM_MAX)
2237+ malformed();
2238+ if (hunk_type == 'd')
2239+ min++;
2240+ p_end = p_ptrn_lines + p_repl_lines + 1;
2241+ p_newfirst = min;
2242+ p_repl_lines = max - min + 1;
2243+ if (p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2244+ malformed();
2245+ p_end = p_ptrn_lines + p_repl_lines + 1;
2246+ if (p_end > MAXHUNKSIZE)
2247+ fatal("hunk too large at line %ld\n", p_input_line);
2248+ while (p_end >= hunkmax)
2249+ grow_hunkmax();
2250+ snprintf(buf, bufsz, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
2251+ p_line[0] = savestr(buf);
2252+ if (out_of_mem) {
2253+ p_end = -1;
2254+ return 0;
2255+ }
2256+ p_char[0] = '*';
2257+ for (i = 1; i <= p_ptrn_lines; i++) {
2258+ ret = pgetline(&buf, &bufsz, pfp);
2259+ p_input_line++;
2260+ if (ret == -1)
2261+ fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2262+ if (*buf != '<')
2263+ fatal("< expected at line %ld of patch\n", p_input_line);
2264+ p_line[i] = savestr(buf + 2);
2265+ if (out_of_mem) {
2266+ p_end = i - 1;
2267+ return 0;
2268+ }
2269+ p_len[i] = strlen(p_line[i]);
2270+ p_char[i] = '-';
2271+ }
2272+ if (remove_special_line()) {
2273+ p_len[i - 1] -= 1;
2274+ (p_line[i - 1])[p_len[i - 1]] = 0;
2275+ }
2276+ if (hunk_type == 'c') {
2277+ ret = pgetline(&buf, &bufsz, pfp);
2278+ p_input_line++;
2279+ if (ret == -1)
2280+ fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2281+ if (*buf != '-')
2282+ fatal("--- expected at line %ld of patch\n", p_input_line);
2283+ }
2284+ snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
2285+ p_line[i] = savestr(buf);
2286+ if (out_of_mem) {
2287+ p_end = i - 1;
2288+ return 0;
2289+ }
2290+ p_char[i] = '=';
2291+ for (i++; i <= p_end; i++) {
2292+ ret = pgetline(&buf, &bufsz, pfp);
2293+ p_input_line++;
2294+ if (ret == -1)
2295+ fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2296+ if (*buf != '>')
2297+ fatal("> expected at line %ld of patch\n", p_input_line);
2298+ p_line[i] = savestr(buf + 2);
2299+ if (out_of_mem) {
2300+ p_end = i - 1;
2301+ return 0;
2302+ }
2303+ p_len[i] = strlen(p_line[i]);
2304+ p_char[i] = '+';
2305+ }
2306+ if (remove_special_line()) {
2307+ p_len[i - 1] -= 1;
2308+ (p_line[i - 1])[p_len[i - 1]] = 0;
2309+ }
2310+ }
2311+ if (reverse) {
2312+ if (!pch_swap())
2313+ say("Not enough memory to swap next hunk!\n");
2314+ }
2315+ if (p_end + 1 < hunkmax)
2316+ p_char[p_end + 1] = '^';
2317+ return 1;
2318+}
2319+
2320+int
2321+pgetline(char **bf, size_t *sz, FILE *fp)
2322+{
2323+ char *s;
2324+ int indent = 0;
2325+ int ret;
2326+
2327+ ret = getline(bf, sz, fp);
2328+ if (p_indent && ret != -1) {
2329+ for (s = buf; indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
2330+ if (*s == '\t')
2331+ indent += 8 - (indent % 7);
2332+ else
2333+ indent++;
2334+ }
2335+ if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
2336+ fatal("buffer too small in pgetline()\n");
2337+ }
2338+ return ret;
2339+}
2340+
2341+int
2342+pch_swap(void)
2343+{
2344+ char **tp_line;
2345+ ssize_t *tp_len;
2346+ char *tp_char;
2347+ LINENUM i;
2348+ LINENUM n;
2349+ int blankline = 0;
2350+ char *s;
2351+
2352+ i = p_first;
2353+ p_first = p_newfirst;
2354+ p_newfirst = i;
2355+
2356+ tp_line = p_line;
2357+ tp_len = p_len;
2358+ tp_char = p_char;
2359+ p_line = NULL;
2360+ p_len = NULL;
2361+ p_char = NULL;
2362+ set_hunkmax();
2363+ if (p_line == NULL || p_len == NULL || p_char == NULL) {
2364+ free(p_line);
2365+ p_line = tp_line;
2366+ free(p_len);
2367+ p_len = tp_len;
2368+ free(p_char);
2369+ p_char = tp_char;
2370+ return 0;
2371+ }
2372+
2373+ i = p_ptrn_lines + 1;
2374+ if (tp_char[i] == '\n') {
2375+ blankline = 1;
2376+ i++;
2377+ }
2378+ if (p_efake >= 0) {
2379+ if (p_efake <= i)
2380+ n = p_end - i + 1;
2381+ else
2382+ n = -i;
2383+ p_efake += n;
2384+ p_bfake += n;
2385+ }
2386+ for (n = 0; i <= p_end; i++, n++) {
2387+ p_line[n] = tp_line[i];
2388+ p_char[n] = tp_char[i];
2389+ if (p_char[n] == '+')
2390+ p_char[n] = '-';
2391+ p_len[n] = tp_len[i];
2392+ }
2393+ if (blankline) {
2394+ i = p_ptrn_lines + 1;
2395+ p_line[n] = tp_line[i];
2396+ p_char[n] = tp_char[i];
2397+ p_len[n] = tp_len[i];
2398+ n++;
2399+ }
2400+ if (p_char[0] != '=')
2401+ fatal("expected '=' found '%c'\n", p_char[0]);
2402+ p_char[0] = '*';
2403+ for (s = p_line[0]; *s; s++)
2404+ if (*s == '-')
2405+ *s = '*';
2406+
2407+ if (p_char[0] != '*')
2408+ fatal("expected '*' found '%c'\n", p_char[0]);
2409+ tp_char[0] = '=';
2410+ for (s = tp_line[0]; *s; s++)
2411+ if (*s == '*')
2412+ *s = '-';
2413+ for (i = 0; n <= p_end; i++, n++) {
2414+ p_line[n] = tp_line[i];
2415+ p_char[n] = tp_char[i];
2416+ if (p_char[n] == '-')
2417+ p_char[n] = '+';
2418+ p_len[n] = tp_len[i];
2419+ }
2420+ if (i != p_ptrn_lines + 1)
2421+ fatal("expected %ld lines, got %ld\n", p_ptrn_lines + 1, i);
2422+
2423+ i = p_ptrn_lines;
2424+ p_ptrn_lines = p_repl_lines;
2425+ p_repl_lines = i;
2426+
2427+ free(tp_line);
2428+ free(tp_len);
2429+ free(tp_char);
2430+ return 1;
2431+}
2432+
2433+LINENUM
2434+pch_first(void)
2435+{
2436+ return p_first;
2437+}
2438+
2439+LINENUM
2440+pch_ptrn_lines(void)
2441+{
2442+ return p_ptrn_lines;
2443+}
2444+
2445+LINENUM
2446+pch_newfirst(void)
2447+{
2448+ return p_newfirst;
2449+}
2450+
2451+LINENUM
2452+pch_repl_lines(void)
2453+{
2454+ return p_repl_lines;
2455+}
2456+
2457+LINENUM
2458+pch_end(void)
2459+{
2460+ return p_end;
2461+}
2462+
2463+LINENUM
2464+pch_context(void)
2465+{
2466+ return p_context;
2467+}
2468+
2469+ssize_t
2470+pch_line_len(LINENUM line)
2471+{
2472+ return p_len[line];
2473+}
2474+
2475+char
2476+pch_char(LINENUM line)
2477+{
2478+ return p_char[line];
2479+}
2480+
2481+char *
2482+pfetch(LINENUM line)
2483+{
2484+ return p_line[line];
2485+}
2486+
2487+LINENUM
2488+pch_hunk_beg(void)
2489+{
2490+ return p_hunk_beg;
2491+}
2492+
2493+void
2494+do_ed_script(void)
2495+{
2496+ char *t;
2497+ long beginning_of_this_line;
2498+ FILE *pipefp = NULL;
2499+ int continuation;
2500+
2501+ if (!skip_rest_of_patch) {
2502+ if (copy_file(filearg[0], TMPOUTNAME) < 0) {
2503+ unlink(TMPOUTNAME);
2504+ fatal("cant create temp file %s", TMPOUTNAME);
2505+ }
2506+ snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED, verbose ? "" : "s", TMPOUTNAME);
2507+ pipefp = popen(buf, "w");
2508+ }
2509+ for (;;) {
2510+ beginning_of_this_line = ftell(pfp);
2511+ if (pgetline(&buf, &bufsz, pfp) == -1) {
2512+ next_intuit_at(beginning_of_this_line, p_input_line);
2513+ break;
2514+ }
2515+ p_input_line++;
2516+ for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
2517+ ;
2518+ if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) {
2519+ if (pipefp != NULL)
2520+ fprintf(pipefp, "%s", buf);
2521+ if (*t == 's') {
2522+ for (;;) {
2523+ continuation = 0;
2524+ t = strchr(buf, '\0') - 1;
2525+ while (--t >= buf && *t == '\\')
2526+ continuation = !continuation;
2527+ if (!continuation || pgetline(&buf, &bufsz, pfp) == -1)
2528+ break;
2529+ if (pipefp != NULL)
2530+ fprintf(pipefp, "%s", buf);
2531+ }
2532+ } else if (*t != 'd') {
2533+ while (pgetline(&buf, &bufsz, pfp) != -1) {
2534+ p_input_line++;
2535+ if (pipefp != NULL)
2536+ fprintf(pipefp, "%s", buf);
2537+ if (strEQ(buf, ".\n"))
2538+ break;
2539+ }
2540+ }
2541+ } else {
2542+ next_intuit_at(beginning_of_this_line, p_input_line);
2543+ break;
2544+ }
2545+ }
2546+ if (pipefp == NULL)
2547+ return;
2548+ fprintf(pipefp, "w\n");
2549+ fprintf(pipefp, "q\n");
2550+ fflush(pipefp);
2551+ pclose(pipefp);
2552+ ignore_signals();
2553+ if (!check_only) {
2554+ if (move_file(TMPOUTNAME, outname) < 0) {
2555+ toutkeep = 1;
2556+ chmod(TMPOUTNAME, filemode);
2557+ } else
2558+ chmod(outname, filemode);
2559+ }
2560+ set_signals(1);
2561+}
2562+
2563+static char *
2564+posix_name(const struct FileName *names, int assume_exists)
2565+{
2566+ char *path = NULL;
2567+ int i;
2568+
2569+ for (i = 0; i < MAX_FILE; i++) {
2570+ if (names[i].path != NULL && names[i].exists) {
2571+ path = names[i].path;
2572+ break;
2573+ }
2574+ }
2575+ if (path == NULL && !assume_exists) {
2576+ for (i = 0; i < MAX_FILE; i++) {
2577+ if (names[i].path != NULL && (path = checked_in(names[i].path)) != NULL)
2578+ break;
2579+ }
2580+ if (path == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2581+ path = names[NEW_FILE].path;
2582+ }
2583+ return path ? savestr(path) : NULL;
2584+}
2585+
2586+static char *
2587+best_name(const struct FileName *names, int assume_exists)
2588+{
2589+ size_t min_components, min_baselen, min_len, tmp;
2590+ char *best = NULL;
2591+ int i;
2592+
2593+ min_components = min_baselen = min_len = SIZE_MAX;
2594+ for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2595+ if (names[i].path == NULL || (!names[i].exists && !assume_exists))
2596+ continue;
2597+ if ((tmp = num_components(names[i].path)) > min_components)
2598+ continue;
2599+ min_components = tmp;
2600+ if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2601+ continue;
2602+ min_baselen = tmp;
2603+ if ((tmp = strlen(names[i].path)) > min_len)
2604+ continue;
2605+ min_len = tmp;
2606+ best = names[i].path;
2607+ }
2608+ if (best == NULL) {
2609+ min_components = min_baselen = min_len = SIZE_MAX;
2610+ for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2611+ if (names[i].path == NULL || checked_in(names[i].path) == NULL)
2612+ continue;
2613+ if ((tmp = num_components(names[i].path)) > min_components)
2614+ continue;
2615+ min_components = tmp;
2616+ if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2617+ continue;
2618+ min_baselen = tmp;
2619+ if ((tmp = strlen(names[i].path)) > min_len)
2620+ continue;
2621+ min_len = tmp;
2622+ best = names[i].path;
2623+ }
2624+ if (best == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2625+ best = names[NEW_FILE].path;
2626+ }
2627+ return best ? savestr(best) : NULL;
2628+}
2629+
2630+static size_t
2631+num_components(const char *path)
2632+{
2633+ size_t n;
2634+ const char *cp;
2635+
2636+ for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
2637+ while (*cp == '/')
2638+ cp++;
2639+ }
2640+ return n;
2641+}
2642+
2643+LINENUM
2644+strtolinenum(char *nptr, char **endptr)
2645+{
2646+ LINENUM rv;
2647+ char c;
2648+ char *p;
2649+ const char *errstr;
2650+
2651+ for (p = nptr; isdigit((unsigned char)*p); p++)
2652+ ;
2653+
2654+ if (p == nptr)
2655+ malformed();
2656+
2657+ c = *p;
2658+ *p = '\0';
2659+
2660+ rv = strtonum(nptr, 0, LINENUM_MAX, &errstr);
2661+ if (errstr != NULL)
2662+ fatal("invalid line number at line %ld: `%s' is %s\n",
2663+ p_input_line, nptr, errstr);
2664+
2665+ *p = c;
2666+ *endptr = p;
2667+
2668+ return rv;
2669+}
2670+
2671+int
2672+move_file(const char *from, const char *to)
2673+{
2674+ int fromfd;
2675+ ssize_t i;
2676+
2677+ if (strEQ(to, "-")) {
2678+ fromfd = open(from, O_RDONLY);
2679+ if (fromfd < 0)
2680+ pfatal("internal error, cant reopen %s", from);
2681+ while ((i = read(fromfd, buf, bufsz)) > 0) {
2682+ if (write(STDOUT_FILENO, buf, i) != i)
2683+ pfatal("write failed");
2684+ }
2685+ close(fromfd);
2686+ return 0;
2687+ }
2688+ if (backup_file(to) < 0) {
2689+ say("Cant backup %s, output is in %s: %s\n", to, from, strerror(errno));
2690+ return -1;
2691+ }
2692+ if (rename(from, to) < 0) {
2693+ if (errno != EXDEV || copy_file(from, to) < 0) {
2694+ say("Cant create %s, output is in %s: %s\n", to, from, strerror(errno));
2695+ return -1;
2696+ }
2697+ }
2698+ return 0;
2699+}
2700+
2701+int
2702+backup_file(const char *orig)
2703+{
2704+ struct stat filestat;
2705+ char bakname[PATH_MAX], *s, *simplename;
2706+ dev_t orig_device;
2707+ ino_t orig_inode;
2708+
2709+ if (backup_type == BACKUP_NONE || stat(orig, &filestat) != 0)
2710+ return 0;
2711+ if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
2712+ unlink(orig);
2713+ return 0;
2714+ }
2715+ orig_device = filestat.st_dev;
2716+ orig_inode = filestat.st_ino;
2717+
2718+ if (origprae) {
2719+ if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
2720+ strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
2721+ fatal("filename too long\n");
2722+ } else {
2723+ if ((s = find_backup_file_name(orig)) == NULL)
2724+ fatal("out of memory\n");
2725+ if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
2726+ fatal("filename too long\n");
2727+ free(s);
2728+ }
2729+
2730+ if ((simplename = strrchr(bakname, '/')) != NULL)
2731+ simplename = simplename + 1;
2732+ else
2733+ simplename = bakname;
2734+
2735+ while (stat(bakname, &filestat) == 0 && orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
2736+ for (s = simplename; *s && !islower((unsigned char)*s); s++)
2737+ ;
2738+ if (*s)
2739+ *s = toupper((unsigned char)*s);
2740+ else
2741+ memmove(simplename, simplename + 1, strlen(simplename + 1) + 1);
2742+ }
2743+ if (rename(orig, bakname) < 0) {
2744+ if (errno != EXDEV || copy_file(orig, bakname) < 0)
2745+ return -1;
2746+ }
2747+ return 0;
2748+}
2749+
2750+int
2751+copy_file(const char *from, const char *to)
2752+{
2753+ int tofd, fromfd;
2754+ ssize_t i;
2755+
2756+ tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
2757+ if (tofd < 0)
2758+ return -1;
2759+ fromfd = open(from, O_RDONLY, 0);
2760+ if (fromfd < 0)
2761+ pfatal("internal error, cant reopen %s", from);
2762+ while ((i = read(fromfd, buf, bufsz)) > 0) {
2763+ if (write(tofd, buf, i) != i)
2764+ pfatal("write to %s failed", to);
2765+ }
2766+ close(fromfd);
2767+ close(tofd);
2768+ return 0;
2769+}
2770+
2771+char *
2772+savestr(const char *s)
2773+{
2774+ char *rv;
2775+
2776+ if (!s)
2777+ s = "Oops";
2778+ rv = strdup(s);
2779+ if (rv == NULL) {
2780+ if (using_plan_a)
2781+ out_of_mem = 1;
2782+ else
2783+ fatal("out of memory\n");
2784+ }
2785+ return rv;
2786+}
2787+
2788+void
2789+say(const char *fmt, ...)
2790+{
2791+ va_list ap;
2792+
2793+ va_start(ap, fmt);
2794+ vfprintf(stderr, fmt, ap);
2795+ va_end(ap);
2796+ fflush(stderr);
2797+}
2798+
2799+__dead void
2800+fatal(const char *fmt, ...)
2801+{
2802+ va_list ap;
2803+
2804+ va_start(ap, fmt);
2805+ fprintf(stderr, "patch: **** ");
2806+ vfprintf(stderr, fmt, ap);
2807+ va_end(ap);
2808+ my_exit(2);
2809+}
2810+
2811+__dead void
2812+pfatal(const char *fmt, ...)
2813+{
2814+ va_list ap;
2815+ int errnum = errno;
2816+
2817+ fprintf(stderr, "patch: **** ");
2818+ va_start(ap, fmt);
2819+ vfprintf(stderr, fmt, ap);
2820+ va_end(ap);
2821+ fprintf(stderr, ": %s\n", strerror(errnum));
2822+ my_exit(2);
2823+}
2824+
2825+void
2826+ask(const char *fmt, ...)
2827+{
2828+ va_list ap;
2829+ ssize_t nr = 0;
2830+ static int ttyfd = -1;
2831+
2832+ va_start(ap, fmt);
2833+ vfprintf(stdout, fmt, ap);
2834+ va_end(ap);
2835+ fflush(stdout);
2836+ if (ttyfd < 0)
2837+ ttyfd = open(_PATH_TTY, O_RDONLY);
2838+ if (ttyfd >= 0) {
2839+ if ((nr = read(ttyfd, buf, bufsz)) > 0 && buf[nr - 1] == '\n')
2840+ buf[nr - 1] = '\0';
2841+ }
2842+ if (ttyfd < 0 || nr <= 0) {
2843+ putchar('\n');
2844+ buf[0] = '\0';
2845+ }
2846+}
2847+
2848+void
2849+set_signals(int reset)
2850+{
2851+ static sig_t hupval, intval;
2852+
2853+ if (!reset) {
2854+ hupval = signal(SIGHUP, SIG_IGN);
2855+ if (hupval != SIG_IGN)
2856+ hupval = (sig_t)my_exit;
2857+ intval = signal(SIGINT, SIG_IGN);
2858+ if (intval != SIG_IGN)
2859+ intval = (sig_t)my_exit;
2860+ }
2861+ signal(SIGHUP, hupval);
2862+ signal(SIGINT, intval);
2863+}
2864+
2865+void
2866+ignore_signals(void)
2867+{
2868+ signal(SIGHUP, SIG_IGN);
2869+ signal(SIGINT, SIG_IGN);
2870+}
2871+
2872+void
2873+makedirs(const char *filename, int striplast)
2874+{
2875+ char *tmpbuf;
2876+
2877+ if ((tmpbuf = strdup(filename)) == NULL)
2878+ fatal("out of memory\n");
2879+
2880+ if (striplast) {
2881+ char *s = strrchr(tmpbuf, '/');
2882+ if (s == NULL) {
2883+ free(tmpbuf);
2884+ return;
2885+ }
2886+ *s = '\0';
2887+ }
2888+ if (mkpath(tmpbuf) != 0)
2889+ pfatal("creation of %s failed", tmpbuf);
2890+ free(tmpbuf);
2891+}
2892+
2893+char *
2894+fetchname(const char *at, int *exists, int strip_leading)
2895+{
2896+ char *fullname, *name, *t;
2897+ int sleading, tab;
2898+ struct stat filestat;
2899+
2900+ if (at == NULL || *at == '\0')
2901+ return NULL;
2902+ while (isspace((unsigned char)*at))
2903+ at++;
2904+ if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
2905+ return NULL;
2906+ name = fullname = t = savestr(at);
2907+ tab = strchr(t, '\t') != NULL;
2908+ for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || !isspace((unsigned char)*t)); t++) {
2909+ if (t[0] == '/' && t[1] != '/' && t[1] != '\0') {
2910+ if (--sleading >= 0)
2911+ name = t + 1;
2912+ }
2913+ }
2914+ *t = '\0';
2915+
2916+ if (strip_leading == 957 && name != fullname && *fullname != '/') {
2917+ name[-1] = '\0';
2918+ if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
2919+ name[-1] = '/';
2920+ name = fullname;
2921+ }
2922+ }
2923+ name = savestr(name);
2924+ free(fullname);
2925+
2926+ *exists = stat(name, &filestat) == 0;
2927+ return name;
2928+}
2929+
2930+char *
2931+checked_in(char *file)
2932+{
2933+ char *filebase, *filedir, tmpbuf[PATH_MAX];
2934+ struct stat filestat;
2935+
2936+ filebase = basename(file);
2937+ filedir = dirname(file);
2938+
2939+#define try(f, a1, a2, a3) \
2940+(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
2941+
2942+ if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
2943+ try("%s/RCS/%s%s", filedir, filebase, "") ||
2944+ try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
2945+ try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
2946+ try("%s/%s%s", filedir, SCCSPREFIX, filebase))
2947+ return file;
2948+
2949+ return NULL;
2950+}
2951+
2952+__dead void
2953+version(void)
2954+{
2955+ printf("Patch version 2.0-12u9-NetBSD\n");
2956+ my_exit(EXIT_SUCCESS);
2957+}
2958+
2959+__dead void
2960+my_exit(int status)
2961+{
2962+ unlink(TMPINNAME);
2963+ if (!toutkeep)
2964+ unlink(TMPOUTNAME);
2965+ if (!trejkeep)
2966+ unlink(TMPREJNAME);
2967+ unlink(TMPPATNAME);
2968+ exit(status);
2969+}
2970+
2971+static void *
2972+pch_realloc(void *ptr, size_t number, size_t size)
2973+{
2974+ if (number > SIZE_MAX / size) {
2975+ errno = EOVERFLOW;
2976+ return NULL;
2977+ }
2978+ return realloc(ptr, number * size);
2979+}
2980+
2981+int
2982+mkpath(char *path)
2983+{
2984+ struct stat sb;
2985+ char *slash;
2986+ int done = 0;
2987+
2988+ slash = path;
2989+ while (!done) {
2990+ slash += strspn(slash, "/");
2991+ slash += strcspn(slash, "/");
2992+ done = (*slash == '\0');
2993+ *slash = '\0';
2994+ if (stat(path, &sb)) {
2995+ if (errno != ENOENT || (mkdir(path, 0777) && errno != EEXIST)) {
2996+ warn("%s", path);
2997+ return -1;
2998+ }
2999+ } else if (!S_ISDIR(sb.st_mode)) {
3000+ warnx("%s: %s", path, strerror(ENOTDIR));
3001+ return -1;
3002+ }
3003+ *slash = '/';
3004+ }
3005+ return 0;
3006+}
3007+
3008+char *
3009+find_backup_file_name(const char *file)
3010+{
3011+ char *dir, *base_versions, *tmp_file;
3012+ int highest_backup;
3013+
3014+ if (backup_type == BACKUP_SIMPLE)
3015+ return patch_concat(file, simple_backup_suffix);
3016+ tmp_file = strdup(file);
3017+ if (tmp_file == NULL)
3018+ return NULL;
3019+ base_versions = patch_concat(basename(tmp_file), ".~");
3020+ free(tmp_file);
3021+ if (base_versions == NULL)
3022+ return NULL;
3023+ tmp_file = strdup(file);
3024+ if (tmp_file == NULL) {
3025+ free(base_versions);
3026+ return NULL;
3027+ }
3028+ dir = dirname(tmp_file);
3029+ if (dir == NULL) {
3030+ free(base_versions);
3031+ free(tmp_file);
3032+ return NULL;
3033+ }
3034+ highest_backup = max_backup_version(base_versions, dir);
3035+ free(base_versions);
3036+ free(tmp_file);
3037+ if (backup_type == BACKUP_NUMBERED_EXISTING && highest_backup == 0)
3038+ return patch_concat(file, simple_backup_suffix);
3039+ return make_version_name(file, highest_backup + 1);
3040+}
3041+
3042+static int
3043+max_backup_version(const char *file, const char *dir)
3044+{
3045+ DIR *dirp;
3046+ struct dirent *dp;
3047+ int highest_version, this_version;
3048+ size_t file_name_length;
3049+
3050+ dirp = opendir(dir);
3051+ if (dirp == NULL)
3052+ return 0;
3053+
3054+ highest_version = 0;
3055+ file_name_length = strlen(file);
3056+ while ((dp = readdir(dirp)) != NULL) {
3057+ if (strlen(dp->d_name) <= file_name_length)
3058+ continue;
3059+ this_version = version_number(file, dp->d_name, file_name_length);
3060+ if (this_version > highest_version)
3061+ highest_version = this_version;
3062+ }
3063+ closedir(dirp);
3064+ return highest_version;
3065+}
3066+
3067+static char *
3068+make_version_name(const char *file, int version_num)
3069+{
3070+ char *backup_name;
3071+
3072+ if (asprintf(&backup_name, "%s.~%d~", file, version_num) == -1)
3073+ return NULL;
3074+ return backup_name;
3075+}
3076+
3077+static int
3078+version_number(const char *base, const char *backup, size_t base_length)
3079+{
3080+ int version_num;
3081+ const char *p;
3082+
3083+ version_num = 0;
3084+ if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
3085+ for (p = &backup[base_length]; ISDIGIT(*p); ++p)
3086+ version_num = version_num * 10 + *p - '0';
3087+ if (p[0] != '~' || p[1])
3088+ version_num = 0;
3089+ }
3090+ return version_num;
3091+}
3092+
3093+static char *
3094+patch_concat(const char *str1, const char *str2)
3095+{
3096+ char *newstr;
3097+
3098+ if (asprintf(&newstr, "%s%s", str1, str2) == -1)
3099+ return NULL;
3100+ return newstr;
3101+}
3102+
3103+static int
3104+argmatch(const char *arg, const char **optlist)
3105+{
3106+ int i;
3107+ size_t arglen;
3108+ int matchind = -1;
3109+ int ambiguous = 0;
3110+
3111+ arglen = strlen(arg);
3112+ for (i = 0; optlist[i]; i++) {
3113+ if (!strncmp(optlist[i], arg, arglen)) {
3114+ if (strlen(optlist[i]) == arglen)
3115+ return i;
3116+ else if (matchind == -1)
3117+ matchind = i;
3118+ else
3119+ ambiguous = 1;
3120+ }
3121+ }
3122+ if (ambiguous)
3123+ return -2;
3124+ else
3125+ return matchind;
3126+}
3127+
3128+static void
3129+invalid_arg(const char *kind, const char *value, int problem)
3130+{
3131+ fprintf(stderr, "patch: ");
3132+ if (problem == -1)
3133+ fprintf(stderr, "invalid");
3134+ else
3135+ fprintf(stderr, "ambiguous");
3136+ fprintf(stderr, " %s `%s'\n", kind, value);
3137+}
3138+
3139+enum BackupType
3140+get_version(const char *version_str)
3141+{
3142+ int i;
3143+
3144+ if (version_str == NULL || *version_str == '\0')
3145+ return BACKUP_NUMBERED_EXISTING;
3146+ i = argmatch(version_str, backup_args);
3147+ if (i >= 0)
3148+ return backup_types[i];
3149+ invalid_arg("version control type", version_str, i);
3150+ exit(2);
3151+}
+0,
-251
1@@ -1,251 +0,0 @@
2-/*
3- * $OpenBSD: backupfile.c,v 1.19 2006/03/11 19:41:30 otto Exp $
4- * $DragonFly: src/usr.bin/patch/backupfile.c,v 1.5 2008/08/11 00:05:06 joerg Exp $
5- * $NetBSD: backupfile.c,v 1.16 2021/02/19 17:46:53 nia Exp $
6- */
7-
8-/*
9- * backupfile.c -- make Emacs style backup file names
10- *
11- * Copyright (C) 1990 Free Software Foundation, Inc.
12- *
13- * This program is free software; you can redistribute it and/or modify it
14- * without restriction.
15- *
16- * This program is distributed in the hope that it will be useful, but WITHOUT
17- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18- * FITNESS FOR A PARTICULAR PURPOSE.
19- */
20-
21-/*
22- * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
23- */
24-
25-#include <ctype.h>
26-#include <dirent.h>
27-#include <libgen.h>
28-#include <stdio.h>
29-#include <stdlib.h>
30-#include <string.h>
31-#include <unistd.h>
32-
33-#include "backupfile.h"
34-
35-
36-#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
37-
38-/* Which type of backup file names are generated. */
39-enum backup_type backup_type = undefined;
40-
41-/*
42- * The extension added to file names to produce a simple (as opposed to
43- * numbered) backup file name.
44- */
45-const char *simple_backup_suffix = "~";
46-
47-static char *concat(const char *, const char *);
48-static char *make_version_name(const char *, int);
49-static int max_backup_version(const char *, const char *);
50-static int version_number(const char *, const char *, size_t);
51-static int argmatch(const char *, const char **);
52-static void invalid_arg(const char *, const char *, int);
53-
54-/*
55- * Return the name of the new backup file for file FILE, allocated with
56- * malloc. Return 0 if out of memory. FILE must not end with a '/' unless it
57- * is the root directory. Do not call this function if backup_type == none.
58- */
59-char *
60-find_backup_file_name(const char *file)
61-{
62- char *dir, *base_versions, *tmp_file;
63- int highest_backup;
64-
65- if (backup_type == simple)
66- return concat(file, simple_backup_suffix);
67- tmp_file = strdup(file);
68- if (tmp_file == NULL)
69- return NULL;
70- base_versions = concat(basename(tmp_file), ".~");
71- free(tmp_file);
72- if (base_versions == NULL)
73- return NULL;
74- tmp_file = strdup(file);
75- if (tmp_file == NULL) {
76- free(base_versions);
77- return NULL;
78- }
79- dir = dirname(tmp_file);
80- if (dir == NULL) {
81- free(base_versions);
82- free(tmp_file);
83- return NULL;
84- }
85- highest_backup = max_backup_version(base_versions, dir);
86- free(base_versions);
87- free(tmp_file);
88- if (backup_type == numbered_existing && highest_backup == 0)
89- return concat(file, simple_backup_suffix);
90- return make_version_name(file, highest_backup + 1);
91-}
92-
93-/*
94- * Return the number of the highest-numbered backup file for file FILE in
95- * directory DIR. If there are no numbered backups of FILE in DIR, or an
96- * error occurs reading DIR, return 0. FILE should already have ".~" appended
97- * to it.
98- */
99-static int
100-max_backup_version(const char *file, const char *dir)
101-{
102- DIR *dirp;
103- struct dirent *dp;
104- int highest_version, this_version;
105- size_t file_name_length;
106-
107- dirp = opendir(dir);
108- if (dirp == NULL)
109- return 0;
110-
111- highest_version = 0;
112- file_name_length = strlen(file);
113-
114- while ((dp = readdir(dirp)) != NULL) {
115- if (strlen(dp->d_name) <= file_name_length)
116- continue;
117-
118- this_version = version_number(file, dp->d_name, file_name_length);
119- if (this_version > highest_version)
120- highest_version = this_version;
121- }
122- closedir(dirp);
123- return highest_version;
124-}
125-
126-/*
127- * Return a string, allocated with malloc, containing "FILE.~VERSION~".
128- * Return 0 if out of memory.
129- */
130-static char *
131-make_version_name(const char *file, int version)
132-{
133- char *backup_name;
134-
135- if (asprintf(&backup_name, "%s.~%d~", file, version) == -1)
136- return NULL;
137- return backup_name;
138-}
139-
140-/*
141- * If BACKUP is a numbered backup of BASE, return its version number;
142- * otherwise return 0. BASE_LENGTH is the length of BASE. BASE should
143- * already have ".~" appended to it.
144- */
145-static int
146-version_number(const char *base, const char *backup, size_t base_length)
147-{
148- int version;
149- const char *p;
150-
151- version = 0;
152- if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
153- for (p = &backup[base_length]; ISDIGIT(*p); ++p)
154- version = version * 10 + *p - '0';
155- if (p[0] != '~' || p[1])
156- version = 0;
157- }
158- return version;
159-}
160-
161-/*
162- * Return the newly-allocated concatenation of STR1 and STR2. If out of
163- * memory, return 0.
164- */
165-static char *
166-concat(const char *str1, const char *str2)
167-{
168- char *newstr;
169-
170- if (asprintf(&newstr, "%s%s", str1, str2) == -1)
171- return NULL;
172- return newstr;
173-}
174-
175-/*
176- * If ARG is an unambiguous match for an element of the null-terminated array
177- * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it
178- * does not match any element or -2 if it is ambiguous (is a prefix of more
179- * than one element).
180- */
181-static int
182-argmatch(const char *arg, const char **optlist)
183-{
184- int i; /* Temporary index in OPTLIST. */
185- size_t arglen; /* Length of ARG. */
186- int matchind = -1; /* Index of first nonexact match. */
187- int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
188-
189- arglen = strlen(arg);
190-
191- /* Test all elements for either exact match or abbreviated matches. */
192- for (i = 0; optlist[i]; i++) {
193- if (!strncmp(optlist[i], arg, arglen)) {
194- if (strlen(optlist[i]) == arglen)
195- /* Exact match found. */
196- return i;
197- else if (matchind == -1)
198- /* First nonexact match found. */
199- matchind = i;
200- else
201- /* Second nonexact match found. */
202- ambiguous = 1;
203- }
204- }
205- if (ambiguous)
206- return -2;
207- else
208- return matchind;
209-}
210-
211-/*
212- * Error reporting for argmatch. KIND is a description of the type of entity
213- * that was being matched. VALUE is the invalid value that was given. PROBLEM
214- * is the return value from argmatch.
215- */
216-static void
217-invalid_arg(const char *kind, const char *value, int problem)
218-{
219- fprintf(stderr, "patch: ");
220- if (problem == -1)
221- fprintf(stderr, "invalid");
222- else /* Assume -2. */
223- fprintf(stderr, "ambiguous");
224- fprintf(stderr, " %s `%s'\n", kind, value);
225-}
226-
227-static const char *backup_args[] = {
228- "none", "never", "simple", "nil", "existing", "t", "numbered", 0
229-};
230-
231-static enum backup_type backup_types[] = {
232- none, simple, simple, numbered_existing,
233- numbered_existing, numbered, numbered
234-};
235-
236-/*
237- * Return the type of backup indicated by VERSION. Unique abbreviations are
238- * accepted.
239- */
240-enum backup_type
241-get_version(const char *version)
242-{
243- int i;
244-
245- if (version == NULL || *version == '\0')
246- return numbered_existing;
247- i = argmatch(version, backup_args);
248- if (i >= 0)
249- return backup_types[i];
250- invalid_arg("version control type", version, i);
251- exit(2);
252-}
+0,
-44
1@@ -1,44 +0,0 @@
2-/*
3- * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $
4- * $DragonFly: src/usr.bin/patch/backupfile.h,v 1.3 2007/09/29 23:11:10 swildner Exp $
5- * $NetBSD: backupfile.h,v 1.7 2021/02/19 17:46:53 nia Exp $
6- */
7-
8-/*
9- * backupfile.h -- declarations for making Emacs style backup file names
10- * Copyright (C) 1990 Free Software Foundation, Inc.
11- *
12- * This program is free software; you can redistribute it and/or modify it
13- * without restriction.
14- *
15- * This program is distributed in the hope that it will be useful, but WITHOUT
16- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17- * FITNESS FOR A PARTICULAR PURPOSE.
18- *
19- */
20-
21-/* When to make backup files. */
22-enum backup_type {
23- undefined,
24-
25- /* Never make backups. */
26- none,
27-
28- /* Make simple backups of every file. */
29- simple,
30-
31- /*
32- * Make numbered backups of files that already have numbered backups,
33- * and simple backups of the others.
34- */
35- numbered_existing,
36-
37- /* Make numbered backups of every file. */
38- numbered
39-};
40-
41-extern enum backup_type backup_type;
42-extern const char *simple_backup_suffix;
43-
44-char *find_backup_file_name(const char *file);
45-enum backup_type get_version(const char *version);
+0,
-136
1@@ -1,136 +0,0 @@
2-/*
3- * $OpenBSD: common.h,v 1.26 2006/03/11 19:41:30 otto Exp $
4- * $DragonFly: src/usr.bin/patch/common.h,v 1.5 2008/08/10 23:50:12 joerg Exp $
5- * $NetBSD: common.h,v 1.22 2021/05/25 11:25:59 cjep Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#include <sys/types.h>
35-
36-#include <limits.h>
37-#include <stdbool.h>
38-#include <stdint.h>
39-
40-#include <util.h>
41-
42-#ifndef __dead
43-#define __dead __attribute__((__noreturn__))
44-#endif
45-#ifndef __printflike
46-#define __printflike(a, b) __attribute__((__format__(printf, a, b)))
47-#endif
48-#ifndef __UNCONST
49-#define __UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
50-#endif
51-
52-#define DEBUGGING
53-
54-/* constants */
55-
56-#define MAXHUNKSIZE 100000 /* is this enough lines? */
57-#define INITHUNKMAX 125 /* initial dynamic allocation size */
58-#define INITLINELEN 8192
59-#define BUFFERSIZE 1024
60-#define LINENUM_MAX LONG_MAX
61-
62-#define SCCSPREFIX "s."
63-#define GET "get -e %s"
64-#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
65-
66-#define RCSSUFFIX ",v"
67-#define CHECKOUT "/usr/bin/co"
68-#define RCSDIFF "/usr/bin/rcsdiff"
69-
70-#define ORIGEXT ".orig"
71-#define REJEXT ".rej"
72-
73-/* handy definitions */
74-
75-#define strNE(s1,s2) (strcmp(s1, s2))
76-#define strEQ(s1,s2) (!strcmp(s1, s2))
77-#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
78-#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
79-
80-/* typedefs */
81-
82-typedef long LINENUM; /* must be signed */
83-
84-/* globals */
85-
86-extern mode_t filemode;
87-
88-extern char *buf; /* general purpose buffer */
89-extern size_t bufsz; /* general purpose buffer size */
90-
91-extern bool using_plan_a; /* try to keep everything in memory */
92-extern bool out_of_mem; /* ran out of memory in plan a */
93-
94-#define MAXFILEC 2
95-
96-extern char *filearg[MAXFILEC];
97-extern bool ok_to_create_file;
98-extern char *outname;
99-extern char *origprae;
100-
101-extern char *TMPOUTNAME;
102-extern char *TMPINNAME;
103-extern char *TMPREJNAME;
104-extern char *TMPPATNAME;
105-extern bool toutkeep;
106-extern bool trejkeep;
107-
108-#ifdef DEBUGGING
109-extern int debug;
110-#endif
111-
112-extern bool force;
113-extern bool batch;
114-extern bool verbose;
115-extern bool reverse;
116-extern bool noreverse;
117-extern bool skip_rest_of_patch;
118-extern int strippath;
119-extern bool canonicalize;
120-/* TRUE if -C was specified on command line. */
121-extern bool check_only;
122-extern bool warn_on_invalid_line;
123-extern bool last_line_missing_eol;
124-
125-
126-#define CONTEXT_DIFF 1
127-#define NORMAL_DIFF 2
128-#define ED_DIFF 3
129-#define NEW_CONTEXT_DIFF 4
130-#define UNI_DIFF 5
131-
132-extern int diff_type;
133-extern char *revision; /* prerequisite revision, if any */
134-extern LINENUM input_lines; /* how long is input file in lines */
135-
136-extern int posix;
137-
+0,
-519
1@@ -1,519 +0,0 @@
2-/*
3- * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $
4- * $DragonFly: src/usr.bin/patch/inp.c,v 1.6 2007/09/29 23:11:10 swildner Exp $
5- * $NetBSD: inp.c,v 1.30 2023/06/16 23:36:26 wiz Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#include <sys/types.h>
35-#include <sys/file.h>
36-#include <sys/stat.h>
37-#include <sys/mman.h>
38-#include <sys/wait.h>
39-
40-#include <ctype.h>
41-#include <errno.h>
42-#include <fcntl.h>
43-#include <libgen.h>
44-#include <limits.h>
45-#include <stddef.h>
46-#include <stdio.h>
47-#include <stdlib.h>
48-#include <string.h>
49-#include <unistd.h>
50-
51-#include "common.h"
52-#include "util.h"
53-#include "pch.h"
54-#include "inp.h"
55-
56-
57-/* Input-file-with-indexable-lines abstract type */
58-
59-static off_t i_size; /* size of the input file */
60-static char *i_womp; /* plan a buffer for entire file */
61-static char **i_ptr; /* pointers to lines in i_womp */
62-static char empty_line[] = { '\0' };
63-
64-static int tifd = -1; /* plan b virtual string array */
65-static char *tibuf[2]; /* plan b buffers */
66-static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
67-static LINENUM lines_per_buf; /* how many lines per buffer */
68-static int tireclen; /* length of records in tmp file */
69-
70-static bool rev_in_string(const char *);
71-static bool reallocate_lines(size_t *);
72-
73-/* returns false if insufficient memory */
74-static bool plan_a(const char *);
75-
76-static void plan_b(const char *);
77-
78-/* New patch--prepare to edit another file. */
79-
80-void
81-re_input(void)
82-{
83- if (using_plan_a) {
84- i_size = 0;
85- free(i_ptr);
86- i_ptr = NULL;
87- if (i_womp != NULL) {
88- munmap(i_womp, i_size);
89- i_womp = NULL;
90- }
91- } else {
92- using_plan_a = true; /* maybe the next one is smaller */
93- close(tifd);
94- tifd = -1;
95- free(tibuf[0]);
96- free(tibuf[1]);
97- tibuf[0] = tibuf[1] = NULL;
98- tiline[0] = tiline[1] = -1;
99- tireclen = 0;
100- }
101-}
102-
103-/* Construct the line index, somehow or other. */
104-
105-void
106-scan_input(const char *filename)
107-{
108- if (!plan_a(filename))
109- plan_b(filename);
110- if (verbose) {
111- say("Patching file %s using Plan %s...\n", filename,
112- (using_plan_a ? "A" : "B"));
113- }
114-}
115-
116-static bool
117-reallocate_lines(size_t *lines_allocated)
118-{
119- char **p;
120- size_t new_size;
121-
122- new_size = *lines_allocated * 3 / 2;
123- p = pch_realloc(i_ptr, new_size + 2, sizeof(char *));
124- if (p == NULL) { /* shucks, it was a near thing */
125- munmap(i_womp, i_size);
126- i_womp = NULL;
127- free(i_ptr);
128- i_ptr = NULL;
129- *lines_allocated = 0;
130- return false;
131- }
132- *lines_allocated = new_size;
133- i_ptr = p;
134- return true;
135-}
136-
137-/* Try keeping everything in memory. */
138-
139-static bool
140-plan_a(const char *filename)
141-{
142- int ifd, statfailed, devnull, pstat;
143- char *p, *s, *lbuf;
144- struct stat filestat;
145- off_t i;
146- ptrdiff_t sz;
147- size_t iline, lines_allocated, lbufsz;
148- pid_t pid;
149- char *argp[4] = {NULL};
150-
151-#ifdef DEBUGGING
152- if (debug & 8)
153- return false;
154-#endif
155-
156- if (filename == NULL || *filename == '\0')
157- return false;
158-
159- statfailed = stat(filename, &filestat);
160- if (statfailed && ok_to_create_file) {
161- if (verbose)
162- say("(Creating file %s...)\n", filename);
163-
164- /*
165- * in check_patch case, we still display `Creating file' even
166- * though we're not. The rule is that -C should be as similar
167- * to normal patch behavior as possible
168- */
169- if (check_only)
170- return true;
171- makedirs(filename, true);
172- close(creat(filename, 0666));
173- statfailed = stat(filename, &filestat);
174- }
175- if (statfailed && check_only)
176- fatal("%s not found, -C mode, can't probe further\n", filename);
177- /* For nonexistent or read-only files, look for RCS versions. */
178- if (statfailed ||
179- /* No one can write to it. */
180- (filestat.st_mode & 0222) == 0 ||
181- /* I can't write to it. */
182- ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
183- char *filebase, *filedir;
184- struct stat cstat;
185- char *tmp_filename1, *tmp_filename2;
186-
187- tmp_filename1 = strdup(filename);
188- tmp_filename2 = strdup(filename);
189- if (tmp_filename1 == NULL || tmp_filename2 == NULL)
190- fatal("strdupping filename");
191-
192- filebase = basename(tmp_filename1);
193- filedir = dirname(tmp_filename2);
194-
195- lbufsz = INITLINELEN;
196- if ((lbuf = malloc(bufsz)) == NULL)
197- pfatal("allocating line buffer");
198- lbuf[0] = '\0';
199-
200-#define try(f, a1, a2, a3) \
201- (snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
202-
203- /*
204- * else we can't write to it but it's not under a version
205- * control system, so just proceed.
206- */
207- if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
208- try("%s/RCS/%s%s", filedir, filebase, "") ||
209- try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
210- if (!statfailed) {
211- if ((filestat.st_mode & 0222) != 0)
212- /* The owner can write to it. */
213- fatal("file %s seems to be locked "
214- "by somebody else under RCS\n",
215- filename);
216- /*
217- * It might be checked out unlocked. See if
218- * it's safe to check out the default version
219- * locked.
220- */
221- if (verbose)
222- say("Comparing file %s to default "
223- "RCS version...\n", filename);
224-
225- switch (pid = fork()) {
226- case -1:
227- fatal("can't fork: %s\n",
228- strerror(errno));
229- case 0:
230- devnull = open("/dev/null", O_RDONLY);
231- if (devnull == -1) {
232- fatal("can't open /dev/null: %s",
233- strerror(errno));
234- }
235- (void)dup2(devnull, STDOUT_FILENO);
236- argp[0] = __UNCONST(RCSDIFF);
237- argp[1] = __UNCONST(filename);
238- execv(RCSDIFF, argp);
239- exit(127);
240- }
241- pid = waitpid(pid, &pstat, 0);
242- if (pid == -1 || WEXITSTATUS(pstat) != 0) {
243- fatal("can't check out file %s: "
244- "differs from default RCS version\n",
245- filename);
246- }
247- }
248-
249- if (verbose)
250- say("Checking out file %s from RCS...\n",
251- filename);
252-
253- switch (pid = fork()) {
254- case -1:
255- fatal("can't fork: %s\n", strerror(errno));
256- case 0:
257- argp[0] = __UNCONST(CHECKOUT);
258- argp[1] = __UNCONST("-l");
259- argp[2] = __UNCONST(filename);
260- execv(CHECKOUT, argp);
261- exit(127);
262- }
263- pid = waitpid(pid, &pstat, 0);
264- if (pid == -1 || WEXITSTATUS(pstat) != 0 ||
265- stat(filename, &filestat)) {
266- fatal("can't check out file %s from RCS\n",
267- filename);
268- }
269- } else if (statfailed) {
270- fatal("can't find %s\n", filename);
271- }
272- free(lbuf);
273- free(tmp_filename1);
274- free(tmp_filename2);
275- }
276-
277- filemode = filestat.st_mode;
278- if (!S_ISREG(filemode))
279- fatal("%s is not a normal file--can't patch\n", filename);
280- i_size = filestat.st_size;
281- if (out_of_mem) {
282- set_hunkmax(); /* make sure dynamic arrays are allocated */
283- out_of_mem = false;
284- return false; /* force plan b because plan a bombed */
285- }
286- if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
287- say("block too large to mmap\n");
288- return false;
289- }
290- if ((ifd = open(filename, O_RDONLY)) < 0)
291- pfatal("can't open file %s", filename);
292-
293- if (i_size) {
294- i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
295- if (i_womp == MAP_FAILED) {
296- perror("mmap failed");
297- i_womp = NULL;
298- close(ifd);
299- return false;
300- }
301- } else {
302- i_womp = NULL;
303- }
304-
305- close(ifd);
306- if (i_size)
307- madvise(i_womp, i_size, MADV_SEQUENTIAL);
308-
309- /* estimate the number of lines */
310- lines_allocated = i_size / 25;
311- if (lines_allocated < 100)
312- lines_allocated = 100;
313-
314- if (!reallocate_lines(&lines_allocated))
315- return false;
316-
317- /* now scan the buffer and build pointer array */
318- iline = 1;
319- i_ptr[iline] = i_womp;
320- /* test for NUL too, to maintain the behavior of the original code */
321- for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
322- if (*s == '\n') {
323- if (iline == lines_allocated) {
324- if (!reallocate_lines(&lines_allocated))
325- return false;
326- }
327- /* these are NOT NUL terminated */
328- i_ptr[++iline] = s + 1;
329- }
330- }
331- /* if the last line contains no EOL, append one */
332- if (i_size > 0 && i_womp[i_size - 1] != '\n') {
333- last_line_missing_eol = true;
334- /* fix last line */
335- sz = s - i_ptr[iline];
336- p = malloc(sz + 1);
337- if (p == NULL) {
338- free(i_ptr);
339- i_ptr = NULL;
340- munmap(i_womp, i_size);
341- i_womp = NULL;
342- return false;
343- }
344-
345- memcpy(p, i_ptr[iline], sz);
346- p[sz] = '\n';
347- i_ptr[iline] = p;
348- /* count the extra line and make it point to some valid mem */
349- i_ptr[++iline] = empty_line;
350- } else
351- last_line_missing_eol = false;
352-
353- input_lines = iline - 1;
354-
355- /* now check for revision, if any */
356-
357- if (revision != NULL) {
358- if (!rev_in_string(i_womp)) {
359- if (force) {
360- if (verbose)
361- say("Warning: this file doesn't appear "
362- "to be the %s version--patching anyway.\n",
363- revision);
364- } else if (batch) {
365- fatal("this file doesn't appear to be the "
366- "%s version--aborting.\n",
367- revision);
368- } else {
369- ask("This file doesn't appear to be the "
370- "%s version--patch anyway? [n] ",
371- revision);
372- if (*buf != 'y')
373- fatal("aborted\n");
374- }
375- } else if (verbose)
376- say("Good. This file appears to be the %s version.\n",
377- revision);
378- }
379- return true; /* plan a will work */
380-}
381-
382-/* Keep (virtually) nothing in memory. */
383-
384-static void
385-plan_b(const char *filename)
386-{
387- FILE *ifp;
388- size_t i = 0, j, maxlen = 1;
389- char *p;
390- bool found_revision = (revision == NULL);
391-
392- using_plan_a = false;
393- if ((ifp = fopen(filename, "r")) == NULL)
394- pfatal("can't open file %s", filename);
395- unlink(TMPINNAME);
396- if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
397- pfatal("can't open file %s", TMPINNAME);
398- while (getline(&buf, &bufsz, ifp) != -1) {
399- if (revision != NULL && !found_revision && rev_in_string(buf))
400- found_revision = true;
401- if ((i = strlen(buf)) > maxlen)
402- maxlen = i; /* find longest line */
403- }
404- last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
405- if (last_line_missing_eol && maxlen == i)
406- maxlen++;
407-
408- if (revision != NULL) {
409- if (!found_revision) {
410- if (force) {
411- if (verbose)
412- say("Warning: this file doesn't appear "
413- "to be the %s version--patching anyway.\n",
414- revision);
415- } else if (batch) {
416- fatal("this file doesn't appear to be the "
417- "%s version--aborting.\n",
418- revision);
419- } else {
420- ask("This file doesn't appear to be the %s "
421- "version--patch anyway? [n] ",
422- revision);
423- if (*buf != 'y')
424- fatal("aborted\n");
425- }
426- } else if (verbose)
427- say("Good. This file appears to be the %s version.\n",
428- revision);
429- }
430- fseek(ifp, 0L, SEEK_SET); /* rewind file */
431- lines_per_buf = BUFFERSIZE / maxlen;
432- tireclen = maxlen;
433- tibuf[0] = malloc(BUFFERSIZE + 1);
434- if (tibuf[0] == NULL)
435- fatal("out of memory\n");
436- tibuf[1] = malloc(BUFFERSIZE + 1);
437- if (tibuf[1] == NULL)
438- fatal("out of memory\n");
439- for (i = 1;; i++) {
440- p = tibuf[0] + maxlen * (i % lines_per_buf);
441- if (i % lines_per_buf == 0) /* new block */
442- if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
443- pfatal("can't write temp file");
444- if (fgets(p, maxlen + 1, ifp) == NULL) {
445- input_lines = i - 1;
446- if (i % lines_per_buf != 0)
447- if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
448- pfatal("can't write temp file");
449- break;
450- }
451- j = strlen(p);
452- /* These are '\n' terminated strings, so no need to add a NUL */
453- if (j == 0 || p[j - 1] != '\n')
454- p[j] = '\n';
455- }
456- fclose(ifp);
457- close(tifd);
458- if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
459- pfatal("can't reopen file %s", TMPINNAME);
460-}
461-
462-/*
463- * Fetch a line from the input file, \n terminated, not necessarily \0.
464- */
465-char *
466-ifetch(LINENUM line, int whichbuf)
467-{
468- if (line < 1 || line > input_lines) {
469- if (warn_on_invalid_line) {
470- say("No such line %ld in input file, ignoring\n", line);
471- warn_on_invalid_line = false;
472- }
473- return NULL;
474- }
475- if (using_plan_a)
476- return i_ptr[line];
477- else {
478- LINENUM offline = line % lines_per_buf;
479- LINENUM baseline = line - offline;
480-
481- if (tiline[0] == baseline)
482- whichbuf = 0;
483- else if (tiline[1] == baseline)
484- whichbuf = 1;
485- else {
486- tiline[whichbuf] = baseline;
487-
488- if (lseek(tifd, (off_t) (baseline / lines_per_buf *
489- BUFFERSIZE), SEEK_SET) < 0)
490- pfatal("cannot seek in the temporary input file");
491-
492- if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
493- pfatal("error reading tmp file %s", TMPINNAME);
494- }
495- return tibuf[whichbuf] + (tireclen * offline);
496- }
497-}
498-
499-/*
500- * True if the string argument contains the revision number we want.
501- */
502-static bool
503-rev_in_string(const char *string)
504-{
505- const char *s;
506- size_t patlen;
507-
508- if (revision == NULL)
509- return true;
510- patlen = strlen(revision);
511- if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
512- return true;
513- for (s = string; *s; s++) {
514- if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
515- isspace((unsigned char)s[patlen + 1])) {
516- return true;
517- }
518- }
519- return false;
520-}
+0,
-35
1@@ -1,35 +0,0 @@
2-/*
3- * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $
4- * $DragonFly: src/usr.bin/patch/inp.h,v 1.1 2004/09/24 18:44:28 joerg Exp $
5- * $NetBSD: inp.h,v 1.10 2008/09/19 18:33:34 joerg Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-void re_input(void);
35-void scan_input(const char *);
36-char *ifetch(LINENUM, int);
+0,
-81
1@@ -1,81 +0,0 @@
2-/*
3- * $OpenBSD: mkpath.c,v 1.2 2005/06/20 07:14:06 otto Exp $
4- * $DragonFly: src/usr.bin/patch/mkpath.c,v 1.1 2007/09/29 23:11:10 swildner Exp $
5- * $NetBSD: mkpath.c,v 1.1 2008/09/19 18:33:34 joerg Exp $
6- */
7-
8-/*
9- * Copyright (c) 1983, 1992, 1993
10- * The Regents of the University of California. All rights reserved.
11- *
12- * Redistribution and use in source and binary forms, with or without
13- * modification, are permitted provided that the following conditions
14- * are met:
15- * 1. Redistributions of source code must retain the above copyright
16- * notice, this list of conditions and the following disclaimer.
17- * 2. Redistributions in binary form must reproduce the above copyright
18- * notice, this list of conditions and the following disclaimer in the
19- * documentation and/or other materials provided with the distribution.
20- * 3. Neither the name of the University nor the names of its contributors
21- * may be used to endorse or promote products derived from this software
22- * without specific prior written permission.
23- *
24- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34- * SUCH DAMAGE.
35- */
36-
37-#include <sys/types.h>
38-#include <sys/stat.h>
39-#include <err.h>
40-#include <errno.h>
41-#include <string.h>
42-
43-int mkpath(char *);
44-
45-/* Code taken directly from mkdir(1).
46-
47- * mkpath -- create directories.
48- * path - path
49- */
50-int
51-mkpath(char *path)
52-{
53- struct stat sb;
54- char *slash;
55- int done = 0;
56-
57- slash = path;
58-
59- while (!done) {
60- slash += strspn(slash, "/");
61- slash += strcspn(slash, "/");
62-
63- done = (*slash == '\0');
64- *slash = '\0';
65-
66- if (stat(path, &sb)) {
67- if (errno != ENOENT || (mkdir(path, 0777) &&
68- errno != EEXIST)) {
69- warn("%s", path);
70- return (-1);
71- }
72- } else if (!S_ISDIR(sb.st_mode)) {
73- warnx("%s: %s", path, strerror(ENOTDIR));
74- return (-1);
75- }
76-
77- *slash = '/';
78- }
79-
80- return (0);
81-}
82-
+0,
-1338
1@@ -1,1338 +0,0 @@
2-/*
3- * $OpenBSD: patch.c,v 1.45 2007/04/18 21:52:24 sobrado Exp $
4- * $DragonFly: src/usr.bin/patch/patch.c,v 1.10 2008/08/10 23:39:56 joerg Exp $
5- * $NetBSD: patch.c,v 1.35 2024/07/12 15:48:39 manu Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-/* ?man patch: apply a diff file to an original
35-synopsis: [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count] [-r rej-name] [-V t | nil | never | none] [-x number] [-z backup-ext] [--backup-if-mismatch] [--no-backup-if-mismatch] [--posix] [origfile [patchfile]]
36-patch will take a patch file containing any of the four forms of difference listing produced by the `diff(1)` program and apply those differences to an original file, producing a patched version. If `patchfile` is omitted, or is a hyphen, the patch will be read from the standard input.
37-
38-patch will attempt to determine the type of the diff listing, unless over-ruled by a `-c`, `-e`, `-n`, or `-u` option. Context diffs (old-style, new-style, and unified) and normal diffs are applied directly by the patch program itself, whereas ed diffs are simply fed to the `ed(1)` editor via a pipe.
39-
40-If the `patchfile` contains more than one patch, patch will try to apply each of them as if they came from separate patch files. This means, among other things, that it is assumed that the name of the file to patch must be determined for each diff listing, and that the garbage before each diff listing will be examined for interesting things such as file names and revision level (see the section on Filename Determination below).
41-
42-## OPTIONS
43-### -B backup-prefix, --prefix backup-prefix
44-Causes the next argument to be interpreted as a prefix to the backup file name. If this argument is specified, any argument to `-z` will be ignored.
45-
46-### -b, --backup
47-Save a backup copy of the file before it is modified. By default the original file is saved with a backup extension of `.orig` unless the file already has a numbered backup, in which case a numbered backup is made. This is equivalent to specifying `-V existing`. This option is currently the default, unless `--posix` is specified.
48-
49-### --backup-if-mismatch
50-Create a backup file if the patch doesn't apply cleanly. This option only makes sense when `--backup` is disabled, i.e. when in `--posix` mode.
51-
52-### -C, --check
53-Checks that the patch would apply cleanly, but does not modify anything.
54-
55-### -c, --context
56-Forces patch to interpret the patch file as a context diff.
57-
58-### -D symbol, --ifdef symbol
59-Causes patch to use the `#ifdef...#endif` construct to mark changes. The argument following will be used as the differentiating symbol. Note that, unlike the C compiler, there must be a space between the `-D` and the argument.
60-
61-### -d directory, --directory directory
62-Causes patch to interpret the next argument as a directory, and change the working directory to it before doing anything else.
63-
64-### -E, --remove-empty-files
65-Causes patch to remove output files that are empty after the patches have been applied. This option is useful when applying patches that create or remove files.
66-
67-### -e, --ed
68-Forces patch to interpret the patch file as an `ed(1)` script.
69-
70-### -F max-fuzz, --fuzz max-fuzz
71-Sets the maximum fuzz factor. This option only applies to context diffs, and causes patch to ignore up to that many lines in looking for places to install a hunk. Note that a larger fuzz factor increases the odds of a faulty patch. The default fuzz factor is 2, and it may not be set to more than the number of lines of context in the context diff, ordinarily 3.
72-
73-### -f, --force
74-Forces patch to assume that the user knows exactly what he or she is doing, and to not ask any questions. It assumes the following: skip patches for which a file to patch can't be found; patch files even though they have the wrong version for the `Prereq:` line in the patch; and assume that patches are not reversed even if they look like they are. This option does not suppress commentary; use `-s` for that.
75-
76-### -i patchfile, --input patchfile
77-Causes the next argument to be interpreted as the input file name (i.e., a patchfile). This option may be specified multiple times.
78-
79-### -l, --ignore-whitespace
80-Causes the pattern matching to be done loosely, in case the tabs and spaces have been munged in your input file. Any sequence of whitespace in the pattern line will match any sequence in the input file. Normal characters must still match exactly. Each line of the context must still match a line in the input file.
81-
82-### -N, --forward
83-Causes patch to ignore patches that it thinks are reversed or already applied. See also `-R`.
84-
85-### -n, --normal
86-Forces patch to interpret the patch file as a normal diff.
87-
88-### --no-backup-if-mismatch
89-Turn off `--backup-if-mismatch`. This option exists mostly for compatibility with GNU patch.
90-
91-### -o out-file, --output out-file
92-Causes the next argument to be interpreted as the output file name.
93-
94-### -p strip-count, --strip strip-count
95-Sets the pathname strip count, which controls how pathnames found in the patch file are treated, in case you keep your files in a different directory than the person who sent out the patch. The strip count specifies how many slashes are to be stripped from the front of the pathname. (Any intervening directory names also go away.) For example, supposing the file name in the patch file was `/u/howard/src/blurfl/blurfl.c`:
96-
97-Setting `-p0` gives the entire pathname unmodified.
98-
99-`-p1` gives
100-
101-`u/howard/src/blurfl/blurfl.c`
102-
103-without the leading slash.
104-
105-`-p4` gives
106-
107-`blurfl/blurfl.c`
108-
109-Not specifying `-p` at all just gives you `blurfl.c`, unless all of the directories in the leading path (`u/howard/src/blurfl`) exist and that path is relative, in which case you get the entire pathname unmodified. Whatever you end up with is looked for either in the current directory, or the directory specified by the `-d` option.
110-
111-### -R, --reverse
112-Tells patch that this patch was created with the old and new files swapped. (Yes, I'm afraid that does happen occasionally, human nature being what it is.) patch will attempt to swap each hunk around before applying it. Rejects will come out in the swapped format. The `-R` option will not work with ed diff scripts because there is too little information to reconstruct the reverse operation.
113-
114-If the first hunk of a patch fails, patch will reverse the hunk to see if it can be applied that way. If it can, you will be asked if you want to have the `-R` option set. If it can't, the patch will continue to be applied normally. (Note: this method cannot detect a reversed patch if it is a normal diff and if the first command is an append (i.e., it should have been a delete) since appends always succeed, due to the fact that a null context will match anywhere. Luckily, most patches add or change lines rather than delete them, so most reversed normal diffs will begin with a delete, which will fail, triggering the heuristic.)
115-
116-### -r rej-name, --reject-file rej-name
117-Causes the next argument to be interpreted as the reject file name.
118-
119-### -s, --quiet, --silent
120-Makes patch do its work silently, unless an error occurs.
121-
122-### -t, --batch
123-Similar to `-f`, in that it suppresses questions, but makes some different assumptions: skip patches for which a file to patch can't be found (the same as `-f`); skip patches for which the file has the wrong version for the `Prereq:` line in the patch; and assume that patches are reversed if they look like they are.
124-
125-### -u, --unified
126-Forces patch to interpret the patch file as a unified context diff (a unidiff).
127-
128-### -V t | nil | never | none, --version-control t | nil | never | none
129-Causes the next argument to be interpreted as a method for creating backup file names. The type of backups made can also be given in the `PATCH_VERSION_CONTROL` or `VERSION_CONTROL` environment variables, which are overridden by this option. The `-B` option overrides this option, causing the prefix to always be used for making backup file names. The values of the `PATCH_VERSION_CONTROL` and `VERSION_CONTROL` environment variables and the argument to the `-V` option are like the GNU Emacs "version-control" variable; they also recognize synonyms that are more descriptive. The valid values are (unique abbreviations are accepted):
130-
131-`t`, numbered
132-: Always make numbered backups.
133-
134-`nil`, existing
135-: Make numbered backups of files that already have them, simple backups of the others.
136-
137-`never`, simple
138-: Always make simple backups.
139-
140-`none`
141-: No backups are created.
142-
143-### -v, --version
144-Causes patch to print out its revision header and patch level.
145-
146-### -x number, --debug number
147-Sets internal debugging flags, and is of interest only to patchers.
148-
149-### -z backup-ext, --suffix backup-ext
150-Causes the next argument to be interpreted as the backup extension, to be used in place of `.orig`.
151-
152-### --posix
153-Enables strict POSIX.1-2004 conformance, specifically:
154-
155-Backup files are not created unless the `-b` option is specified.
156-
157-If unspecified, the file name used is the first of the old, new and index files that exists.
158-
159-## Patch Application
160-patch will try to skip any leading garbage, apply the diff, and then skip any trailing garbage. Thus you could feed an article or message containing a diff listing to patch, and it should work. If the entire diff is indented by a consistent amount, this will be taken into account.
161-
162-With context diffs, and to a lesser extent with normal diffs, patch can detect when the line numbers mentioned in the patch are incorrect, and will attempt to find the correct place to apply each hunk of the patch. As a first guess, it takes the line number mentioned for the hunk, plus or minus any offset used in applying the previous hunk. If that is not the correct place, patch will scan both forwards and backwards for a set of lines matching the context given in the hunk. First patch looks for a place where all lines of the context match. If no such place is found, and it's a context diff, and the maximum fuzz factor is set to 1 or more, then another scan takes place ignoring the first and last line of context. If that fails, and the maximum fuzz factor is set to 2 or more, the first two and last two lines of context are ignored, and another scan is made. (The default maximum fuzz factor is 2.)
163-
164-If patch cannot find a place to install that hunk of the patch, it will put the hunk out to a reject file, which normally is the name of the output file plus `.rej`. (Note that the rejected hunk will come out in context diff form whether the input patch was a context diff or a normal diff. If the input was a normal diff, many of the contexts will simply be null.) The line numbers on the hunks in the reject file may be different than in the patch file: they reflect the approximate location patch thinks the failed hunks belong in the new file rather than the old one.
165-
166-As each hunk is completed, you will be told whether the hunk succeeded or failed, and which line (in the new file) patch thought the hunk should go on. If this is different from the line number specified in the diff, you will be told the offset. A single large offset MAY be an indication that a hunk was installed in the wrong place. You will also be told if a fuzz factor was used to make the match, in which case you should also be slightly suspicious.
167-
168-## Filename Determination
169-If no original file is specified on the command line, patch will try to figure out from the leading garbage what the name of the file to edit is. When checking a prospective file name, pathname components are stripped as specified by the `-p` option and the file's existence and writability are checked relative to the current working directory (or the directory specified by the `-d` option).
170-
171-If the diff is a context or unified diff, patch is able to determine the old and new file names from the diff header. For context diffs, the "old" file is specified in the line beginning with "***" and the "new" file is specified in the line beginning with "---". For a unified diff, the "old" file is specified in the line beginning with "---" and the "new" file is specified in the line beginning with "+++". If there is an `Index:` line in the leading garbage (regardless of the diff type), patch will use the file name from that line as the "index" file.
172-
173-patch will choose the file name by performing the following steps, with the first match used:
174-
175-If patch is operating in strict POSIX.1-2004 mode, the first of the "old", "new" and "index" file names that exist is used. Otherwise, patch will examine either the "old" and "new" file names or, for a non-context diff, the "index" file name, and choose the file name with the fewest path components, the shortest basename, and the shortest total file name length (in that order).
176-
177-If no file exists, patch checks for the existence of the files in an RCS directory using the criteria specified above. If found, patch will attempt to get or check out the file.
178-
179-If no suitable file was found to patch, the patch file is a context or unified diff, and the old file was zero length, the new file name is created and used.
180-
181-If the file name still cannot be determined, patch will prompt the user for the file name to use.
182-
183-Additionally, if the leading garbage contains a `Prereq:` line, patch will take the first word from the prerequisites line (normally a version number) and check the input file to see if that word can be found. If not, patch will ask for confirmation before proceeding.
184-
185-The upshot of all this is that you should be able to say, while in a news interface, the following:
186-
187-`| patch -d /usr/src/local/blurfl`
188-
189-and patch a file in the blurfl directory directly from the article containing the patch.
190-
191-## Backup Files
192-By default, the patched version is put in place of the original, with the original file backed up to the same name with the extension `.orig`, or as specified by the `-B`, `-V`, or `-z` options. The extension used for making backup files may also be specified in the `SIMPLE_BACKUP_SUFFIX` environment variable, which is overridden by the options above.
193-
194-If the backup file is a symbolic or hard link to the original file, patch creates a new backup file name by changing the first lowercase letter in the last component of the file's name into uppercase. If there are no more lowercase letters in the name, it removes the first character from the name. It repeats this process until it comes up with a backup file that does not already exist or is not linked to the original file.
195-
196-You may also specify where you want the output to go with the `-o` option; if that file already exists, it is backed up first.
197-
198-## Notes For Patch Senders
199-There are several things you should bear in mind if you are going to be sending out patches:
200-
201-First, you can save people a lot of grief by keeping a `patchlevel.h` file which is patched to increment the patch level as the first diff in the patch file you send out. If you put a `Prereq:` line in with the patch, it won't let them apply patches out of order without some warning.
202-
203-Second, make sure you've specified the file names right, either in a context diff header, or with an `Index:` line. If you are patching something in a subdirectory, be sure to tell the patch user to specify a `-p` option as needed.
204-
205-Third, you can create a file by sending out a diff that compares a null file to the file you want to create. This will only work if the file you want to create doesn't exist already in the target directory.
206-
207-Fourth, take care not to send out reversed patches, since it makes people wonder whether they already applied the patch.
208-
209-Fifth, while you may be able to get away with putting 582 diff listings into one file, it is probably wiser to group related patches into separate files in case something goes haywire.
210-
211-## ENVIRONMENT
212-`POSIXLY_CORRECT`
213-: When set, patch behaves as if the `--posix` option has been specified.
214-
215-`SIMPLE_BACKUP_SUFFIX`
216-: Extension to use for backup file names instead of `.orig`.
217-
218-`TMPDIR`
219-: Directory to put temporary files in; default is `/tmp`.
220-
221-`PATCH_VERSION_CONTROL`
222-: Selects when numbered backup files are made.
223-
224-`VERSION_CONTROL`
225-: Same as `PATCH_VERSION_CONTROL`.
226-
227-## FILES
228-`$TMPDIR/patch*`
229-: patch temporary files
230-
231-`/dev/tty`
232-: used to read input when patch prompts the user
233-
234-## DIAGNOSTICS
235-Too many to list here, but generally indicative that patch couldn't parse your patch file.
236-
237-The message "Hmm..." indicates that there is unprocessed text in the patch file and that patch is attempting to intuit whether there is a patch in that text and, if so, what kind of patch it is.
238-
239-The patch utility exits with one of the following values:
240-
241-`0`
242-: Successful completion.
243-
244-`1`
245-: One or more lines were written to a reject file.
246-
247-`>1`
248-: An error occurred.
249-
250-When applying a set of patches in a loop it behooves you to check this exit status so you don't apply a later patch to a partially patched file.
251-
252-## SEE ALSO
253-`diff(1)`
254-
255-## STANDARDS
256-The patch utility is compliant with the POSIX.1-2004 specification (except as detailed above for the `--posix` option), though the presence of patch itself is optional.
257-
258-The flags `-C`, `-E`, `-f`, `-s`, `-t`, `-u`, `-v`, `-B`, `-F`, `-V`, `-x`, and `-z` and `--posix` are extensions to that specification.
259-
260-## AUTHORS
261-Larry Wall with many other contributors.
262-
263-## CAVEATS
264-patch cannot tell if the line numbers are off in an ed script, and can only detect bad line numbers in a normal diff when it finds a "change" or a "delete" command. A context diff using fuzz factor 3 may have the same problem. Until a suitable interactive interface is added, you should probably do a context diff in these cases to see if the changes made sense. Of course, compiling without errors is a pretty good indication that the patch worked, but not always.
265-
266-patch usually produces the correct results, even when it has to do a lot of guessing. However, the results are guaranteed to be correct only when the patch is applied to exactly the same version of the file that the patch was generated from.
267-
268-## BUGS
269-Could be smarter about partial matches, excessively deviant offsets and swapped code, but that would take an extra pass.
270-
271-Check patch mode (`-C`) will fail if you try to check several patches in succession that build on each other. The entire patch code would have to be restructured to keep temporary files around so that it can handle this situation.
272-
273-If code has been duplicated (for instance with `#ifdef OLDCODE ... #else ... #endif`), patch is incapable of patching both versions, and, if it works at all, will likely patch the wrong one, and tell you that it succeeded to boot.
274-
275-If you apply a patch you've already applied, patch will think it is a reversed patch, and offer to un-apply the patch. This could be construed as a feature.
276-*/
277-
278-#include <sys/types.h>
279-#include <sys/stat.h>
280-
281-#include <ctype.h>
282-#include <getopt.h>
283-#include <limits.h>
284-#include <stdio.h>
285-#include <string.h>
286-#include <stdlib.h>
287-#include <unistd.h>
288-
289-#include "common.h"
290-#include "util.h"
291-#include "pch.h"
292-#include "inp.h"
293-#include "backupfile.h"
294-#include "pathnames.h"
295-
296-mode_t filemode = 0644;
297-
298-char *buf; /* general purpose buffer */
299-size_t bufsz; /* general purpose buffer size */
300-
301-bool using_plan_a = true; /* try to keep everything in memory */
302-bool out_of_mem = false; /* ran out of memory in plan a */
303-
304-#define MAXFILEC 2
305-
306-char *filearg[MAXFILEC];
307-bool ok_to_create_file = false;
308-char *outname = NULL;
309-char *origprae = NULL;
310-char *TMPOUTNAME;
311-char *TMPINNAME;
312-char *TMPREJNAME;
313-char *TMPPATNAME;
314-bool toutkeep = false;
315-bool trejkeep = false;
316-bool warn_on_invalid_line;
317-bool last_line_missing_eol;
318-
319-#ifdef DEBUGGING
320-int debug = 0;
321-#endif
322-
323-bool force = false;
324-bool batch = false;
325-bool verbose = true;
326-bool reverse = false;
327-bool noreverse = false;
328-bool skip_rest_of_patch = false;
329-int strippath = 957;
330-bool canonicalize = false;
331-bool check_only = false;
332-int diff_type = 0;
333-char *revision = NULL; /* prerequisite revision, if any */
334-LINENUM input_lines = 0; /* how long is input file in lines */
335-int posix = 0; /* strict POSIX mode? */
336-int backup_if_mismatch = -1;/* create backup file when patch doesn't apply cleanly */
337-
338-static void reinitialize_almost_everything(void);
339-static void get_some_switches(void);
340-static void reset_getopt_state(void);
341-static LINENUM locate_hunk(LINENUM);
342-static void abort_context_hunk(void);
343-static void rej_line(int, LINENUM);
344-static void abort_hunk(void);
345-static void apply_hunk(LINENUM);
346-static void init_output(const char *);
347-static void init_reject(const char *);
348-static void copy_till(LINENUM, bool);
349-static bool spew_output(void);
350-static void dump_line(LINENUM, bool);
351-static bool patch_match(LINENUM, LINENUM, LINENUM);
352-static bool similar(const char *, const char *, ssize_t);
353-__dead static void usage(void);
354-
355-/* true if -E was specified on command line. */
356-static bool remove_empty_files = false;
357-
358-/* true if -R was specified on command line. */
359-static bool reverse_flag_specified = false;
360-
361-/* buffer holding the name of the rejected patch file. */
362-static char rejname[PATH_MAX];
363-
364-/* buffer for stderr */
365-static char serrbuf[BUFSIZ];
366-
367-/* how many input lines have been irretractibly output */
368-static LINENUM last_frozen_line = 0;
369-
370-static int Argc; /* guess */
371-static char **Argv;
372-static int Argc_last; /* for restarting plan_b */
373-static char **Argv_last;
374-
375-static FILE *ofp = NULL; /* output file pointer */
376-static FILE *rejfp = NULL; /* reject file pointer */
377-
378-static int filec = 0; /* how many file arguments? */
379-static LINENUM last_offset = 0;
380-static LINENUM maxfuzz = 2;
381-
382-/* patch using ifdef, ifndef, etc. */
383-static bool do_defines = false;
384-/* #ifdef xyzzy */
385-static char if_defined[128];
386-/* #ifndef xyzzy */
387-static char not_defined[128];
388-/* #else */
389-static const char else_defined[] = "#else\n";
390-/* #endif xyzzy */
391-static char end_defined[128];
392-
393-
394-/* Apply a set of diffs as appropriate. */
395-
396-int
397-main(int argc, char *argv[])
398-{
399- int error = 0, hunk, failed, i, fd;
400- LINENUM where = 0, newwhere, fuzz, mymaxfuzz;
401- const char *tmpdir;
402- char *v;
403-
404- bufsz = INITLINELEN;
405- if ((buf = malloc(bufsz)) == NULL)
406- pfatal("allocating input buffer");
407- buf[0] = '\0';
408-
409- setbuf(stderr, serrbuf);
410- for (i = 0; i < MAXFILEC; i++)
411- filearg[i] = NULL;
412-
413- /* Cons up the names of the temporary files. */
414- if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
415- tmpdir = _PATH_TMP;
416- for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
417- ;
418- i++;
419- if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
420- fatal("cannot allocate memory");
421- if ((fd = mkstemp(TMPOUTNAME)) < 0)
422- pfatal("can't create %s", TMPOUTNAME);
423- close(fd);
424-
425- if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
426- fatal("cannot allocate memory");
427- if ((fd = mkstemp(TMPINNAME)) < 0)
428- pfatal("can't create %s", TMPINNAME);
429- close(fd);
430-
431- if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
432- fatal("cannot allocate memory");
433- if ((fd = mkstemp(TMPREJNAME)) < 0)
434- pfatal("can't create %s", TMPREJNAME);
435- close(fd);
436-
437- if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
438- fatal("cannot allocate memory");
439- if ((fd = mkstemp(TMPPATNAME)) < 0)
440- pfatal("can't create %s", TMPPATNAME);
441- close(fd);
442-
443- v = getenv("SIMPLE_BACKUP_SUFFIX");
444- if (v)
445- simple_backup_suffix = v;
446- else
447- simple_backup_suffix = ORIGEXT;
448-
449- if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
450- v = getenv("VERSION_CONTROL");
451- if (v != NULL)
452- backup_type = get_version(v);
453-
454- /* parse switches */
455- Argc = argc;
456- Argv = argv;
457- get_some_switches();
458-
459- if (backup_type == undefined)
460- backup_type = posix ? none : numbered_existing;
461-
462- /* make sure we clean up /tmp in case of disaster */
463- set_signals(0);
464-
465- for (open_patch_file(filearg[1]); there_is_another_patch();
466- reinitialize_almost_everything()) {
467- /* for each patch in patch file */
468-
469- warn_on_invalid_line = true;
470-
471- if (outname == NULL)
472- outname = savestr(filearg[0]);
473-
474- /* for ed script just up and do it and exit */
475- if (diff_type == ED_DIFF) {
476- do_ed_script();
477- continue;
478- }
479- /* initialize the patched file */
480- if (!skip_rest_of_patch)
481- init_output(TMPOUTNAME);
482-
483- /* initialize reject file */
484- init_reject(TMPREJNAME);
485-
486- /* find out where all the lines are */
487- if (!skip_rest_of_patch)
488- scan_input(filearg[0]);
489-
490- /* from here on, open no standard i/o files, because malloc */
491- /* might misfire and we can't catch it easily */
492-
493- /* apply each hunk of patch */
494- hunk = 0;
495- failed = 0;
496- out_of_mem = false;
497- while (another_hunk()) {
498- hunk++;
499- fuzz = 0;
500- mymaxfuzz = pch_context();
501- if (maxfuzz < mymaxfuzz)
502- mymaxfuzz = maxfuzz;
503- if (!skip_rest_of_patch) {
504- do {
505- where = locate_hunk(fuzz);
506- if (hunk == 1 && where == 0 && !force) {
507- /* dwim for reversed patch? */
508- if (!pch_swap()) {
509- if (fuzz == 0)
510- say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
511- continue;
512- }
513- reverse = !reverse;
514- /* try again */
515- where = locate_hunk(fuzz);
516- if (where == 0) {
517- /* didn't find it swapped */
518- if (!pch_swap())
519- /* put it back to normal */
520- fatal("lost hunk on alloc error!\n");
521- reverse = !reverse;
522- } else if (noreverse) {
523- if (!pch_swap())
524- /* put it back to normal */
525- fatal("lost hunk on alloc error!\n");
526- reverse = !reverse;
527- say("Ignoring previously applied (or reversed) patch.\n");
528- skip_rest_of_patch = true;
529- } else if (batch) {
530- if (verbose)
531- say("%seversed (or %spreviously applied) patch detected! %s -R.",
532- reverse ? "R" : "Unr",
533- reverse ? "" : "not ",
534- reverse ? "Assuming" : "Ignoring");
535- } else {
536- ask("%seversed (or %spreviously applied) patch detected! %s -R? [y] ",
537- reverse ? "R" : "Unr",
538- reverse ? "" : "not ",
539- reverse ? "Assume" : "Ignore");
540- if (*buf == 'n') {
541- ask("Apply anyway? [n] ");
542- if (*buf != 'y')
543- skip_rest_of_patch = true;
544- where = 0;
545- reverse = !reverse;
546- if (!pch_swap())
547- /* put it back to normal */
548- fatal("lost hunk on alloc error!\n");
549- }
550- }
551- }
552- } while (!skip_rest_of_patch && where == 0 &&
553- ++fuzz <= mymaxfuzz);
554-
555- if (skip_rest_of_patch) { /* just got decided */
556- if (ferror(ofp) || fclose(ofp)) {
557- say("Error writing %s\n",
558- TMPOUTNAME);
559- error = 1;
560- }
561- ofp = NULL;
562- }
563- }
564- newwhere = pch_newfirst() + last_offset;
565- if (skip_rest_of_patch) {
566- abort_hunk();
567- failed++;
568- if (verbose)
569- say("Hunk #%d ignored at %ld.\n",
570- hunk, newwhere);
571- } else if (where == 0) {
572- abort_hunk();
573- failed++;
574- if (verbose)
575- say("Hunk #%d failed at %ld.\n",
576- hunk, newwhere);
577- } else {
578- apply_hunk(where);
579- if (verbose) {
580- say("Hunk #%d succeeded at %ld",
581- hunk, newwhere);
582- if (fuzz != 0)
583- say(" with fuzz %ld", fuzz);
584- if (last_offset)
585- say(" (offset %ld line%s)",
586- last_offset,
587- last_offset == 1L ? "" : "s");
588- say(".\n");
589- }
590- }
591- }
592-
593- if (out_of_mem && using_plan_a) {
594- Argc = Argc_last;
595- Argv = Argv_last;
596- say("\n\nRan out of memory using Plan A--trying again...\n\n");
597- if (ofp)
598- fclose(ofp);
599- ofp = NULL;
600- if (rejfp)
601- fclose(rejfp);
602- rejfp = NULL;
603- continue;
604- }
605- if (hunk == 0)
606- fatal("Internal error: hunk should not be 0\n");
607-
608- /* finish spewing out the new file */
609- if (!skip_rest_of_patch && !spew_output()) {
610- say("Can't write %s\n", TMPOUTNAME);
611- error = 1;
612- }
613-
614- /* and put the output where desired */
615- ignore_signals();
616- if (!skip_rest_of_patch) {
617- struct stat statbuf;
618- char *realout = outname;
619-
620- if (!check_only) {
621- /* handle --backup-if-mismatch */
622- enum backup_type saved = backup_type;
623- if (failed > 0 && backup_if_mismatch > 0 && backup_type == none)
624- backup_type = simple;
625- if (move_file(TMPOUTNAME, outname) < 0) {
626- toutkeep = true;
627- realout = TMPOUTNAME;
628- chmod(TMPOUTNAME, filemode);
629- } else
630- chmod(outname, filemode);
631- backup_type = saved;
632-
633- if (remove_empty_files &&
634- stat(realout, &statbuf) == 0 &&
635- statbuf.st_size == 0) {
636- if (verbose)
637- say("Removing %s (empty after patching).\n",
638- realout);
639- unlink(realout);
640- }
641- }
642- }
643- if (ferror(rejfp) || fclose(rejfp)) {
644- say("Error writing %s\n", rejname);
645- error = 1;
646- }
647- rejfp = NULL;
648- if (failed) {
649- error = 1;
650- if (*rejname == '\0') {
651- if (strlcpy(rejname, outname,
652- sizeof(rejname)) >= sizeof(rejname))
653- fatal("filename %s is too long\n", outname);
654- if (strlcat(rejname, REJEXT,
655- sizeof(rejname)) >= sizeof(rejname))
656- fatal("filename %s is too long\n", outname);
657- }
658- if (skip_rest_of_patch) {
659- say("%d out of %d hunks ignored--saving rejects to %s\n",
660- failed, hunk, rejname);
661- } else {
662- say("%d out of %d hunks FAILED -- saving rejects to %s\n",
663- failed, hunk, rejname);
664- }
665- if (!check_only && move_file(TMPREJNAME, rejname) < 0)
666- trejkeep = true;
667- }
668- set_signals(1);
669- }
670- my_exit(error);
671- /* NOTREACHED */
672-}
673-
674-static void
675-reset_getopt_state(void)
676-{
677-#if defined(__GLIBC__) || defined(__linux__)
678- optind = 0;
679-#else
680- optreset = 1;
681- optind = 1;
682-#endif
683-}
684-
685-/* Prepare to find the next patch to do in the patch file. */
686-
687-static void
688-reinitialize_almost_everything(void)
689-{
690- re_patch();
691- re_input();
692-
693- input_lines = 0;
694- last_frozen_line = 0;
695-
696- filec = 0;
697- if (!out_of_mem) {
698- free(filearg[0]);
699- filearg[0] = NULL;
700- }
701-
702- free(outname);
703- outname = NULL;
704-
705- last_offset = 0;
706- diff_type = 0;
707-
708- free(revision);
709- revision = NULL;
710-
711- reverse = reverse_flag_specified;
712- skip_rest_of_patch = false;
713-
714- get_some_switches();
715-}
716-
717-/* Process switches and filenames. */
718-
719-static void
720-get_some_switches(void)
721-{
722- const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
723- static struct option longopts[] = {
724- {"backup", no_argument, 0, 'b'},
725- {"backup-if-mismatch", no_argument, &backup_if_mismatch, 1},
726- {"batch", no_argument, 0, 't'},
727- {"check", no_argument, 0, 'C'},
728- {"context", no_argument, 0, 'c'},
729- {"debug", required_argument, 0, 'x'},
730- {"directory", required_argument, 0, 'd'},
731- {"ed", no_argument, 0, 'e'},
732- {"force", no_argument, 0, 'f'},
733- {"forward", no_argument, 0, 'N'},
734- {"fuzz", required_argument, 0, 'F'},
735- {"ifdef", required_argument, 0, 'D'},
736- {"input", required_argument, 0, 'i'},
737- {"ignore-whitespace", no_argument, 0, 'l'},
738- {"no-backup-if-mismatch", no_argument, &backup_if_mismatch, 0},
739- {"normal", no_argument, 0, 'n'},
740- {"output", required_argument, 0, 'o'},
741- {"prefix", required_argument, 0, 'B'},
742- {"quiet", no_argument, 0, 's'},
743- {"reject-file", required_argument, 0, 'r'},
744- {"remove-empty-files", no_argument, 0, 'E'},
745- {"reverse", no_argument, 0, 'R'},
746- {"silent", no_argument, 0, 's'},
747- {"strip", required_argument, 0, 'p'},
748- {"suffix", required_argument, 0, 'z'},
749- {"unified", no_argument, 0, 'u'},
750- {"version", no_argument, 0, 'v'},
751- {"version-control", required_argument, 0, 'V'},
752- {"posix", no_argument, &posix, 1},
753- {NULL, 0, 0, 0}
754- };
755- int ch;
756-
757- rejname[0] = '\0';
758- Argc_last = Argc;
759- Argv_last = Argv;
760- if (!Argc)
761- return;
762- reset_getopt_state();
763- while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
764- switch (ch) {
765- case 'b':
766- if (backup_type == undefined)
767- backup_type = numbered_existing;
768- if (optarg == NULL)
769- break;
770- if (verbose)
771- say("Warning, the ``-b suffix'' option has been"
772- " obsoleted by the -z option.\n");
773- /* FALLTHROUGH */
774- case 'z':
775- /* must directly follow 'b' case for backwards compat */
776- simple_backup_suffix = savestr(optarg);
777- break;
778- case 'B':
779- origprae = savestr(optarg);
780- break;
781- case 'c':
782- diff_type = CONTEXT_DIFF;
783- break;
784- case 'C':
785- check_only = true;
786- break;
787- case 'd':
788- if (chdir(optarg) < 0)
789- pfatal("can't cd to %s", optarg);
790- break;
791- case 'D':
792- do_defines = true;
793- if (!isalpha((unsigned char)*optarg) && *optarg != '_')
794- fatal("argument to -D is not an identifier\n");
795- snprintf(if_defined, sizeof if_defined,
796- "#ifdef %s\n", optarg);
797- snprintf(not_defined, sizeof not_defined,
798- "#ifndef %s\n", optarg);
799- snprintf(end_defined, sizeof end_defined,
800- "#endif /* %s */\n", optarg);
801- break;
802- case 'e':
803- diff_type = ED_DIFF;
804- break;
805- case 'E':
806- remove_empty_files = true;
807- break;
808- case 'f':
809- force = true;
810- break;
811- case 'F':
812- maxfuzz = atoi(optarg);
813- break;
814- case 'i':
815- if (++filec == MAXFILEC)
816- fatal("too many file arguments\n");
817- filearg[filec] = savestr(optarg);
818- break;
819- case 'l':
820- canonicalize = true;
821- break;
822- case 'n':
823- diff_type = NORMAL_DIFF;
824- break;
825- case 'N':
826- noreverse = true;
827- break;
828- case 'o':
829- outname = savestr(optarg);
830- break;
831- case 'p':
832- strippath = atoi(optarg);
833- break;
834- case 'r':
835- if (strlcpy(rejname, optarg,
836- sizeof(rejname)) >= sizeof(rejname))
837- fatal("argument for -r is too long\n");
838- break;
839- case 'R':
840- reverse = true;
841- reverse_flag_specified = true;
842- break;
843- case 's':
844- verbose = false;
845- break;
846- case 't':
847- batch = true;
848- break;
849- case 'u':
850- diff_type = UNI_DIFF;
851- break;
852- case 'v':
853- version();
854- break;
855- case 'V':
856- backup_type = get_version(optarg);
857- break;
858-#ifdef DEBUGGING
859- case 'x':
860- debug = atoi(optarg);
861- break;
862-#endif
863- default:
864- if (ch != '\0')
865- usage();
866- break;
867- }
868- }
869- Argc -= optind;
870- Argv += optind;
871-
872- if (Argc > 0) {
873- filearg[0] = savestr(*Argv++);
874- Argc--;
875- while (Argc > 0) {
876- if (++filec == MAXFILEC)
877- fatal("too many file arguments\n");
878- filearg[filec] = savestr(*Argv++);
879- Argc--;
880- }
881- }
882-
883- if (getenv("POSIXLY_CORRECT") != NULL)
884- posix = 1;
885-
886- if (backup_if_mismatch == -1) {
887- backup_if_mismatch = posix ? 0 : 1;
888- }
889-}
890-
891-static void
892-usage(void)
893-{
894- fprintf(stderr,
895-"usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n"
896-" [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
897-" [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n"
898-" [--backup-if-mismatch] [--no-backup-if-mismatch] [--posix]\n"
899-" [origfile [patchfile]]\n"
900-" patch <patchfile\n");
901- my_exit(EXIT_FAILURE);
902-}
903-
904-/*
905- * Attempt to find the right place to apply this hunk of patch.
906- */
907-static LINENUM
908-locate_hunk(LINENUM fuzz)
909-{
910- LINENUM first_guess = pch_first() + last_offset;
911- LINENUM offset;
912- LINENUM pat_lines = pch_ptrn_lines();
913- LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1;
914- LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
915-
916- if (pat_lines == 0) { /* null range matches always */
917- if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
918- || diff_type == NEW_CONTEXT_DIFF
919- || diff_type == UNI_DIFF)) {
920- say("Empty context always matches.\n");
921- }
922- return (first_guess);
923- }
924- if (max_neg_offset >= first_guess) /* do not try lines < 0 */
925- max_neg_offset = first_guess - 1;
926- if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
927- return first_guess;
928- for (offset = 1; ; offset++) {
929- bool check_after = (offset <= max_pos_offset);
930- bool check_before = (offset <= max_neg_offset);
931-
932- if (check_after && patch_match(first_guess, offset, fuzz)) {
933-#ifdef DEBUGGING
934- if (debug & 1)
935- say("Offset changing from %ld to %ld\n",
936- last_offset, offset);
937-#endif
938- last_offset = offset;
939- return first_guess + offset;
940- } else if (check_before && patch_match(first_guess, -offset, fuzz)) {
941-#ifdef DEBUGGING
942- if (debug & 1)
943- say("Offset changing from %ld to %ld\n",
944- last_offset, -offset);
945-#endif
946- last_offset = -offset;
947- return first_guess - offset;
948- } else if (!check_before && !check_after)
949- return 0;
950- }
951-}
952-
953-/* We did not find the pattern, dump out the hunk so they can handle it. */
954-
955-static void
956-abort_context_hunk(void)
957-{
958- LINENUM i;
959- const LINENUM pat_end = pch_end();
960- /*
961- * add in last_offset to guess the same as the previous successful
962- * hunk
963- */
964- const LINENUM oldfirst = pch_first() + last_offset;
965- const LINENUM newfirst = pch_newfirst() + last_offset;
966- const LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
967- const LINENUM newlast = newfirst + pch_repl_lines() - 1;
968- const char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
969- const char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
970-
971- fprintf(rejfp, "***************\n");
972- for (i = 0; i <= pat_end; i++) {
973- switch (pch_char(i)) {
974- case '*':
975- if (oldlast < oldfirst)
976- fprintf(rejfp, "*** 0%s\n", stars);
977- else if (oldlast == oldfirst)
978- fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
979- else
980- fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
981- oldlast, stars);
982- break;
983- case '=':
984- if (newlast < newfirst)
985- fprintf(rejfp, "--- 0%s\n", minuses);
986- else if (newlast == newfirst)
987- fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
988- else
989- fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
990- newlast, minuses);
991- break;
992- case '\n':
993- fprintf(rejfp, "%s", pfetch(i));
994- break;
995- case ' ':
996- case '-':
997- case '+':
998- case '!':
999- fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
1000- break;
1001- default:
1002- fatal("fatal internal error in abort_context_hunk\n");
1003- }
1004- }
1005-}
1006-
1007-static void
1008-rej_line(int ch, LINENUM i)
1009-{
1010- size_t len;
1011- const char *line = pfetch(i);
1012-
1013- len = strlen(line);
1014-
1015- fprintf(rejfp, "%c%s", ch, line);
1016- if (len == 0 || line[len-1] != '\n')
1017- fprintf(rejfp, "\n\\ No newline at end of file\n");
1018-}
1019-
1020-static void
1021-abort_hunk(void)
1022-{
1023- LINENUM i, j, split;
1024- int ch1, ch2;
1025- const LINENUM pat_end = pch_end();
1026- const LINENUM oldfirst = pch_first() + last_offset;
1027- const LINENUM newfirst = pch_newfirst() + last_offset;
1028-
1029- if (diff_type != UNI_DIFF) {
1030- abort_context_hunk();
1031- return;
1032- }
1033- split = -1;
1034- for (i = 0; i <= pat_end; i++) {
1035- if (pch_char(i) == '=') {
1036- split = i;
1037- break;
1038- }
1039- }
1040- if (split == -1) {
1041- fprintf(rejfp, "malformed hunk: no split found\n");
1042- return;
1043- }
1044- i = 0;
1045- j = split + 1;
1046- fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
1047- pch_ptrn_lines() ? oldfirst : 0,
1048- pch_ptrn_lines(), newfirst, pch_repl_lines());
1049- while (i < split || j <= pat_end) {
1050- ch1 = i < split ? pch_char(i) : -1;
1051- ch2 = j <= pat_end ? pch_char(j) : -1;
1052- if (ch1 == '-') {
1053- rej_line('-', i);
1054- i++;
1055- } else if (ch1 == ' ' && ch2 == ' ') {
1056- rej_line(' ', i);
1057- i++;
1058- j++;
1059- } else if (ch1 == '!' && ch2 == '!') {
1060- while (i < split && ch1 == '!') {
1061- rej_line('-', i);
1062- i++;
1063- ch1 = i < split ? pch_char(i) : -1;
1064- }
1065- while (j <= pat_end && ch2 == '!') {
1066- rej_line('+', j);
1067- j++;
1068- ch2 = j <= pat_end ? pch_char(j) : -1;
1069- }
1070- } else if (ch1 == '*') {
1071- i++;
1072- } else if (ch2 == '+' || ch2 == ' ') {
1073- rej_line(ch2, j);
1074- j++;
1075- } else {
1076- fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
1077- i, split, j);
1078- rej_line(ch1, i);
1079- rej_line(ch2, j);
1080- return;
1081- }
1082- }
1083-}
1084-
1085-/* We found where to apply it (we hope), so do it. */
1086-
1087-static void
1088-apply_hunk(LINENUM where)
1089-{
1090- LINENUM old = 1;
1091- const LINENUM lastline = pch_ptrn_lines();
1092- LINENUM new = lastline + 1;
1093-#define OUTSIDE 0
1094-#define IN_IFNDEF 1
1095-#define IN_IFDEF 2
1096-#define IN_ELSE 3
1097- int def_state = OUTSIDE;
1098- const LINENUM pat_end = pch_end();
1099-
1100- where--;
1101- while (pch_char(new) == '=' || pch_char(new) == '\n')
1102- new++;
1103-
1104- while (old <= lastline) {
1105- if (pch_char(old) == '-') {
1106- copy_till(where + old - 1, false);
1107- if (do_defines) {
1108- if (def_state == OUTSIDE) {
1109- fputs(not_defined, ofp);
1110- def_state = IN_IFNDEF;
1111- } else if (def_state == IN_IFDEF) {
1112- fputs(else_defined, ofp);
1113- def_state = IN_ELSE;
1114- }
1115- fputs(pfetch(old), ofp);
1116- }
1117- last_frozen_line++;
1118- old++;
1119- } else if (new > pat_end) {
1120- break;
1121- } else if (pch_char(new) == '+') {
1122- copy_till(where + old - 1, false);
1123- if (do_defines) {
1124- if (def_state == IN_IFNDEF) {
1125- fputs(else_defined, ofp);
1126- def_state = IN_ELSE;
1127- } else if (def_state == OUTSIDE) {
1128- fputs(if_defined, ofp);
1129- def_state = IN_IFDEF;
1130- }
1131- }
1132- fputs(pfetch(new), ofp);
1133- new++;
1134- } else if (pch_char(new) != pch_char(old)) {
1135- say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
1136- pch_hunk_beg() + old,
1137- pch_hunk_beg() + new);
1138-#ifdef DEBUGGING
1139- say("oldchar = '%c', newchar = '%c'\n",
1140- pch_char(old), pch_char(new));
1141-#endif
1142- my_exit(2);
1143- } else if (pch_char(new) == '!') {
1144- copy_till(where + old - 1, false);
1145- if (do_defines) {
1146- fputs(not_defined, ofp);
1147- def_state = IN_IFNDEF;
1148- }
1149- while (pch_char(old) == '!') {
1150- if (do_defines) {
1151- fputs(pfetch(old), ofp);
1152- }
1153- last_frozen_line++;
1154- old++;
1155- }
1156- if (do_defines) {
1157- fputs(else_defined, ofp);
1158- def_state = IN_ELSE;
1159- }
1160- while (pch_char(new) == '!') {
1161- fputs(pfetch(new), ofp);
1162- new++;
1163- }
1164- } else {
1165- if (pch_char(new) != ' ')
1166- fatal("Internal error: expected ' '\n");
1167- old++;
1168- new++;
1169- if (do_defines && def_state != OUTSIDE) {
1170- fputs(end_defined, ofp);
1171- def_state = OUTSIDE;
1172- }
1173- }
1174- }
1175- if (new <= pat_end && pch_char(new) == '+') {
1176- copy_till(where + old - 1, false);
1177- if (do_defines) {
1178- if (def_state == OUTSIDE) {
1179- fputs(if_defined, ofp);
1180- def_state = IN_IFDEF;
1181- } else if (def_state == IN_IFNDEF) {
1182- fputs(else_defined, ofp);
1183- def_state = IN_ELSE;
1184- }
1185- }
1186- while (new <= pat_end && pch_char(new) == '+') {
1187- fputs(pfetch(new), ofp);
1188- new++;
1189- }
1190- }
1191- if (do_defines && def_state != OUTSIDE) {
1192- fputs(end_defined, ofp);
1193- }
1194-}
1195-
1196-/*
1197- * Open the new file.
1198- */
1199-static void
1200-init_output(const char *name)
1201-{
1202- ofp = fopen(name, "w");
1203- if (ofp == NULL)
1204- pfatal("can't create %s", name);
1205-}
1206-
1207-/*
1208- * Open a file to put hunks we can't locate.
1209- */
1210-static void
1211-init_reject(const char *name)
1212-{
1213- rejfp = fopen(name, "w");
1214- if (rejfp == NULL)
1215- pfatal("can't create %s", name);
1216-}
1217-
1218-/*
1219- * Copy input file to output, up to wherever hunk is to be applied.
1220- * If endoffile is true, treat the last line specially since it may
1221- * lack a newline.
1222- */
1223-static void
1224-copy_till(LINENUM lastline, bool endoffile)
1225-{
1226- if (last_frozen_line > lastline)
1227- fatal("misordered hunks! output would be garbled\n");
1228- while (last_frozen_line < lastline) {
1229- if (++last_frozen_line == lastline && endoffile)
1230- dump_line(last_frozen_line, !last_line_missing_eol);
1231- else
1232- dump_line(last_frozen_line, true);
1233- }
1234-}
1235-
1236-/*
1237- * Finish copying the input file to the output file.
1238- */
1239-static bool
1240-spew_output(void)
1241-{
1242- int rv;
1243-
1244-#ifdef DEBUGGING
1245- if (debug & 256)
1246- say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1247-#endif
1248- if (input_lines)
1249- copy_till(input_lines, true); /* dump remainder of file */
1250- rv = ferror(ofp) == 0 && fclose(ofp) == 0;
1251- ofp = NULL;
1252- return rv;
1253-}
1254-
1255-/*
1256- * Copy one line from input to output.
1257- */
1258-static void
1259-dump_line(LINENUM line, bool write_newline)
1260-{
1261- char *s;
1262-
1263- s = ifetch(line, 0);
1264- if (s == NULL)
1265- return;
1266- /* Note: string is not NUL terminated. */
1267- for (; *s != '\n'; s++)
1268- putc(*s, ofp);
1269- if (write_newline)
1270- putc('\n', ofp);
1271-}
1272-
1273-/*
1274- * Does the patch pattern match at line base+offset?
1275- */
1276-static bool
1277-patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
1278-{
1279- LINENUM pline = 1 + fuzz;
1280- LINENUM iline;
1281- LINENUM pat_lines = pch_ptrn_lines() - fuzz;
1282- const char *ilineptr;
1283- const char *plineptr;
1284- ssize_t plinelen;
1285-
1286- for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
1287- ilineptr = ifetch(iline, offset >= 0);
1288- if (ilineptr == NULL)
1289- return false;
1290- plineptr = pfetch(pline);
1291- plinelen = pch_line_len(pline);
1292- if (canonicalize) {
1293- if (!similar(ilineptr, plineptr, plinelen))
1294- return false;
1295- } else if (strnNE(ilineptr, plineptr, plinelen))
1296- return false;
1297- if (iline == input_lines) {
1298- /*
1299- * We are looking at the last line of the file.
1300- * If the file has no eol, the patch line should
1301- * not have one either and vice-versa. Note that
1302- * plinelen > 0.
1303- */
1304- if (last_line_missing_eol) {
1305- if (plineptr[plinelen - 1] == '\n')
1306- return false;
1307- } else {
1308- if (plineptr[plinelen - 1] != '\n')
1309- return false;
1310- }
1311- }
1312- }
1313- return true;
1314-}
1315-
1316-/*
1317- * Do two lines match with canonicalized white space?
1318- */
1319-static bool
1320-similar(const char *a, const char *b, ssize_t len)
1321-{
1322- while (len) {
1323- if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */
1324- if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */
1325- return false;
1326- while (len && isspace((unsigned char)*b) && *b != '\n')
1327- b++, len--; /* skip pattern whitespace */
1328- while (isspace((unsigned char)*a) && *a != '\n')
1329- a++; /* skip target whitespace */
1330- if (*a == '\n' || *b == '\n')
1331- return (*a == *b); /* should end in sync */
1332- } else if (*a++ != *b++) /* match non-whitespace chars */
1333- return false;
1334- else
1335- len--; /* probably not necessary */
1336- }
1337- return true; /* actually, this is not reached */
1338- /* since there is always a \n */
1339-}
+0,
-14
1@@ -1,14 +0,0 @@
2-/*
3- * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
4- * $DragonFly: src/usr.bin/patch/pathnames.h,v 1.2 2008/08/11 00:04:12 joerg Exp $
5- * $NetBSD: pathnames.h,v 1.1 2008/09/19 18:33:34 joerg Exp $
6- */
7-
8-/*
9- * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
10- * on July 29, 2003.
11- */
12-
13-#include <paths.h>
14-
15-#define _PATH_ED "/bin/ed"
+0,
-1596
1@@ -1,1596 +0,0 @@
2-/*
3- * $OpenBSD: pch.c,v 1.37 2007/09/02 15:19:33 deraadt Exp $
4- * $DragonFly: src/usr.bin/patch/pch.c,v 1.6 2008/08/10 23:35:40 joerg Exp $
5- * $NetBSD: pch.c,v 1.34 2024/07/12 15:48:39 manu Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#include <sys/types.h>
35-#include <sys/stat.h>
36-
37-#include <ctype.h>
38-#include <libgen.h>
39-#include <limits.h>
40-#include <stdio.h>
41-#include <stdlib.h>
42-#include <string.h>
43-#include <unistd.h>
44-
45-#include "common.h"
46-#include "util.h"
47-#include "pch.h"
48-#include "pathnames.h"
49-
50-/* Patch (diff listing) abstract type. */
51-
52-static long p_filesize; /* size of the patch file */
53-static LINENUM p_first; /* 1st line number */
54-static LINENUM p_newfirst; /* 1st line number of replacement */
55-static LINENUM p_ptrn_lines; /* # lines in pattern */
56-static LINENUM p_repl_lines; /* # lines in replacement text */
57-static LINENUM p_end = -1; /* last line in hunk */
58-static LINENUM p_max; /* max allowed value of p_end */
59-static LINENUM p_context = 3; /* # of context lines */
60-static LINENUM p_input_line = 0; /* current line # from patch file */
61-static char **p_line = NULL;/* the text of the hunk */
62-static ssize_t *p_len = NULL; /* length of each line */
63-static char *p_char = NULL; /* +, -, and ! */
64-static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */
65-static int p_indent; /* indent to patch */
66-static LINENUM p_base; /* where to intuit this time */
67-static LINENUM p_bline; /* line # of p_base */
68-static LINENUM p_start; /* where intuit found a patch */
69-static LINENUM p_sline; /* and the line number for it */
70-static LINENUM p_hunk_beg; /* line number of current hunk */
71-static LINENUM p_efake = -1; /* end of faked up lines--don't free */
72-static LINENUM p_bfake = -1; /* beg of faked up lines */
73-static FILE *pfp = NULL; /* patch file pointer */
74-static char *bestguess = NULL; /* guess at correct filename */
75-
76-static void grow_hunkmax(void);
77-static int intuit_diff_type(void);
78-static void next_intuit_at(LINENUM, LINENUM);
79-static void skip_to(LINENUM, LINENUM);
80-static int pgetline(char **, size_t *, FILE *);
81-static char *best_name(const struct file_name *, bool);
82-static char *posix_name(const struct file_name *, bool);
83-static size_t num_components(const char *);
84-
85-/*
86- * Prepare to look for the next patch in the patch file.
87- */
88-void
89-re_patch(void)
90-{
91- p_first = 0;
92- p_newfirst = 0;
93- p_ptrn_lines = 0;
94- p_repl_lines = 0;
95- p_end = (LINENUM) - 1;
96- p_max = 0;
97- p_indent = 0;
98-}
99-
100-/*
101- * Open the patch file at the beginning of time.
102- */
103-void
104-open_patch_file(const char *filename)
105-{
106- struct stat filestat;
107-
108- if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
109- pfp = fopen(TMPPATNAME, "w");
110- if (pfp == NULL)
111- pfatal("can't create %s", TMPPATNAME);
112- while (getline(&buf, &bufsz, stdin) != -1)
113- fprintf(pfp, "%s", buf);
114- if (ferror(pfp) || fclose(pfp))
115- pfatal("can't write %s", TMPPATNAME);
116- filename = TMPPATNAME;
117- }
118- pfp = fopen(filename, "r");
119- if (pfp == NULL)
120- pfatal("patch file %s not found", filename);
121- fstat(fileno(pfp), &filestat);
122- p_filesize = filestat.st_size;
123- next_intuit_at(0L, 1L); /* start at the beginning */
124- set_hunkmax();
125-}
126-
127-/*
128- * Make sure our dynamically realloced tables are malloced to begin with.
129- */
130-void
131-set_hunkmax(void)
132-{
133- if (p_line == NULL)
134- p_line = calloc((size_t) hunkmax, sizeof(char *));
135- if (p_len == NULL)
136- p_len = calloc((size_t) hunkmax, sizeof(ssize_t));
137- if (p_char == NULL)
138- p_char = calloc((size_t) hunkmax, sizeof(char));
139-}
140-
141-/*
142- * Enlarge the arrays containing the current hunk of patch.
143- */
144-static void
145-grow_hunkmax(void)
146-{
147- int new_hunkmax;
148- char **new_p_line;
149- ssize_t *new_p_len;
150- char *new_p_char;
151-
152- new_hunkmax = hunkmax * 2;
153-
154- if (p_line == NULL || p_len == NULL || p_char == NULL)
155- fatal("Internal memory allocation error\n");
156-
157- new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
158- if (new_p_line == NULL)
159- free(p_line);
160-
161- new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
162- if (new_p_len == NULL)
163- free(p_len);
164-
165- new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
166- if (new_p_char == NULL)
167- free(p_char);
168-
169- p_char = new_p_char;
170- p_len = new_p_len;
171- p_line = new_p_line;
172-
173- if (p_line != NULL && p_len != NULL && p_char != NULL) {
174- hunkmax = new_hunkmax;
175- return;
176- }
177-
178- if (!using_plan_a)
179- fatal("out of memory\n");
180- out_of_mem = true; /* whatever is null will be allocated again */
181- /* from within plan_a(), of all places */
182-}
183-
184-/* True if the remainder of the patch file contains a diff of some sort. */
185-
186-bool
187-there_is_another_patch(void)
188-{
189- bool exists = false;
190-
191- if (p_base != 0L && p_base >= p_filesize) {
192- if (verbose)
193- say("done\n");
194- return false;
195- }
196- if (verbose)
197- say("Hmm...");
198- diff_type = intuit_diff_type();
199- if (!diff_type) {
200- if (p_base != 0L) {
201- if (verbose)
202- say(" Ignoring the trailing garbage.\ndone\n");
203- } else
204- say(" I can't seem to find a patch in there anywhere.\n");
205- return false;
206- }
207- if (verbose)
208- say(" %sooks like %s to me...\n",
209- (p_base == 0L ? "L" : "The next patch l"),
210- diff_type == UNI_DIFF ? "a unified diff" :
211- diff_type == CONTEXT_DIFF ? "a context diff" :
212- diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
213- diff_type == NORMAL_DIFF ? "a normal diff" :
214- "an ed script");
215- if (p_indent && verbose)
216- say("(Patch is indented %d space%s.)\n", p_indent,
217- p_indent == 1 ? "" : "s");
218- skip_to(p_start, p_sline);
219- while (filearg[0] == NULL) {
220- if (force || batch) {
221- say("No file to patch. Skipping...\n");
222- filearg[0] = savestr(bestguess);
223- skip_rest_of_patch = true;
224- return true;
225- }
226- ask("File to patch: ");
227- if (*buf != '\n') {
228- free(bestguess);
229- bestguess = savestr(buf);
230- filearg[0] = fetchname(buf, &exists, 0);
231- }
232- if (!exists) {
233- ask("No file found--skip this patch? [n] ");
234- if (*buf != 'y')
235- continue;
236- if (verbose)
237- say("Skipping patch...\n");
238- free(filearg[0]);
239- filearg[0] = fetchname(bestguess, &exists, 0);
240- skip_rest_of_patch = true;
241- return true;
242- }
243- }
244- return true;
245-}
246-
247-/* Determine what kind of diff is in the remaining part of the patch file. */
248-
249-static int
250-intuit_diff_type(void)
251-{
252- long this_line = 0, previous_line;
253- long first_command_line = -1;
254- LINENUM fcl_line = -1;
255- bool last_line_was_command = false, this_is_a_command = false;
256- bool stars_last_line = false, stars_this_line = false;
257- char *s, *t;
258- int indent, retval;
259- struct file_name names[MAX_FILE];
260-
261- memset(names, 0, sizeof(names));
262- ok_to_create_file = false;
263- fseek(pfp, p_base, SEEK_SET);
264- p_input_line = p_bline - 1;
265- for (;;) {
266- previous_line = this_line;
267- last_line_was_command = this_is_a_command;
268- stars_last_line = stars_this_line;
269- this_line = ftell(pfp);
270- indent = 0;
271- p_input_line++;
272- if (getline(&buf, &bufsz, pfp) == -1) {
273- if (first_command_line >= 0L) {
274- /* nothing but deletes!? */
275- p_start = first_command_line;
276- p_sline = fcl_line;
277- retval = ED_DIFF;
278- goto scan_exit;
279- } else {
280- p_start = this_line;
281- p_sline = p_input_line;
282- retval = 0;
283- goto scan_exit;
284- }
285- }
286- for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
287- if (*s == '\t')
288- indent += 8 - (indent % 8);
289- else
290- indent++;
291- }
292- for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
293- ;
294- this_is_a_command = (isdigit((unsigned char)*s) &&
295- (*t == 'd' || *t == 'c' || *t == 'a'));
296- if (first_command_line < 0L && this_is_a_command) {
297- first_command_line = this_line;
298- fcl_line = p_input_line;
299- p_indent = indent; /* assume this for now */
300- }
301- if (!stars_last_line && strnEQ(s, "*** ", 4))
302- names[OLD_FILE].path = fetchname(s + 4,
303- &names[OLD_FILE].exists, strippath);
304- else if (strnEQ(s, "--- ", 4))
305- names[NEW_FILE].path = fetchname(s + 4,
306- &names[NEW_FILE].exists, strippath);
307- else if (strnEQ(s, "+++ ", 4))
308- /* pretend it is the old name */
309- names[OLD_FILE].path = fetchname(s + 4,
310- &names[OLD_FILE].exists, strippath);
311- else if (strnEQ(s, "Index:", 6))
312- names[INDEX_FILE].path = fetchname(s + 6,
313- &names[INDEX_FILE].exists, strippath);
314- else if (strnEQ(s, "Prereq:", 7)) {
315- for (t = s + 7; isspace((unsigned char)*t); t++)
316- ;
317- revision = savestr(t);
318- for (t = revision; *t && !isspace((unsigned char)*t); t++)
319- ;
320- *t = '\0';
321- if (*revision == '\0') {
322- free(revision);
323- revision = NULL;
324- }
325- }
326- if ((!diff_type || diff_type == ED_DIFF) &&
327- first_command_line >= 0L &&
328- strEQ(s, ".\n")) {
329- p_indent = indent;
330- p_start = first_command_line;
331- p_sline = fcl_line;
332- retval = ED_DIFF;
333- goto scan_exit;
334- }
335- if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
336- if (strnEQ(s + 4, "0,0", 3))
337- ok_to_create_file = true;
338- p_indent = indent;
339- p_start = this_line;
340- p_sline = p_input_line;
341- retval = UNI_DIFF;
342- goto scan_exit;
343- }
344- stars_this_line = strnEQ(s, "********", 8);
345- if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
346- strnEQ(s, "*** ", 4)) {
347- if (atol(s + 4) == 0)
348- ok_to_create_file = true;
349- /*
350- * If this is a new context diff the character just
351- * before the newline is a '*'.
352- */
353- while (*s != '\n')
354- s++;
355- p_indent = indent;
356- p_start = previous_line;
357- p_sline = p_input_line - 1;
358- retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
359- goto scan_exit;
360- }
361- if ((!diff_type || diff_type == NORMAL_DIFF) &&
362- last_line_was_command &&
363- (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
364- p_start = previous_line;
365- p_sline = p_input_line - 1;
366- p_indent = indent;
367- retval = NORMAL_DIFF;
368- goto scan_exit;
369- }
370- }
371-scan_exit:
372- if (retval == UNI_DIFF) {
373- /* unswap old and new */
374- struct file_name tmp = names[OLD_FILE];
375- names[OLD_FILE] = names[NEW_FILE];
376- names[NEW_FILE] = tmp;
377- }
378- if (filearg[0] == NULL) {
379- if (posix)
380- filearg[0] = posix_name(names, ok_to_create_file);
381- else {
382- /* Ignore the Index: name for context diffs, like GNU */
383- if (names[OLD_FILE].path != NULL ||
384- names[NEW_FILE].path != NULL) {
385- free(names[INDEX_FILE].path);
386- names[INDEX_FILE].path = NULL;
387- }
388- filearg[0] = best_name(names, ok_to_create_file);
389- }
390- }
391-
392- free(bestguess);
393- bestguess = NULL;
394- if (filearg[0] != NULL)
395- bestguess = savestr(filearg[0]);
396- else if (!ok_to_create_file) {
397- /*
398- * We don't want to create a new file but we need a
399- * filename to set bestguess. Avoid setting filearg[0]
400- * so the file is not created automatically.
401- */
402- if (posix)
403- bestguess = posix_name(names, true);
404- else
405- bestguess = best_name(names, true);
406- }
407- free(names[OLD_FILE].path);
408- free(names[NEW_FILE].path);
409- free(names[INDEX_FILE].path);
410- return retval;
411-}
412-
413-/*
414- * Remember where this patch ends so we know where to start up again.
415- */
416-static void
417-next_intuit_at(LINENUM file_pos, LINENUM file_line)
418-{
419- p_base = file_pos;
420- p_bline = file_line;
421-}
422-
423-/*
424- * Basically a verbose fseek() to the actual diff listing.
425- */
426-static void
427-skip_to(LINENUM file_pos, LINENUM file_line)
428-{
429- int ret;
430-
431- if (p_base > file_pos)
432- fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
433- if (verbose && p_base < file_pos) {
434- fseek(pfp, p_base, SEEK_SET);
435- say("The text leading up to this was:\n--------------------------\n");
436- while (ftell(pfp) < file_pos) {
437- ret = getline(&buf, &bufsz, pfp);
438- if (ret == -1)
439- fatal("Unexpected end of file\n");
440- say("|%s", buf);
441- }
442- say("--------------------------\n");
443- } else
444- fseek(pfp, file_pos, SEEK_SET);
445- p_input_line = file_line - 1;
446-}
447-
448-/* Make this a function for better debugging. */
449-__dead static void
450-malformed(void)
451-{
452- fatal("malformed patch at line %ld: %s", p_input_line, buf);
453- /* about as informative as "Syntax error" in C */
454-}
455-
456-static LINENUM
457-getlinenum(const char *s)
458-{
459- LINENUM l = (LINENUM)atol(s);
460- if (l < 0) {
461- l = 0;
462- malformed();
463- }
464- return l;
465-}
466-
467-static LINENUM
468-getskiplinenum(char **p)
469-{
470- char *s = *p;
471- LINENUM l = getlinenum(s);
472- while (isdigit((unsigned char)*s))
473- s++;
474- *p = s;
475- return l;
476-}
477-
478-/*
479- * True if the line has been discarded (i.e., it is a line saying
480- * "\ No newline at end of file".)
481- */
482-static bool
483-remove_special_line(void)
484-{
485- int c;
486-
487- c = fgetc(pfp);
488- if (c == '\\') {
489- do {
490- c = fgetc(pfp);
491- } while (c != EOF && c != '\n');
492-
493- return true;
494- }
495- if (c != EOF)
496- fseek(pfp, -1L, SEEK_CUR);
497-
498- return false;
499-}
500-
501-/*
502- * True if there is more of the current diff listing to process.
503- */
504-bool
505-another_hunk(void)
506-{
507- long line_beginning; /* file pos of the current line */
508- LINENUM repl_beginning; /* index of --- line */
509- LINENUM fillcnt; /* #lines of missing ptrn or repl */
510- LINENUM fillsrc; /* index of first line to copy */
511- LINENUM filldst; /* index of first missing line */
512- bool ptrn_spaces_eaten; /* ptrn was slightly misformed */
513- bool repl_could_be_missing; /* no + or ! lines in this hunk */
514- bool repl_missing; /* we are now backtracking */
515- long repl_backtrack_position; /* file pos of first repl line */
516- LINENUM repl_patch_line; /* input line number for same */
517- LINENUM ptrn_copiable; /* # of copiable lines in ptrn */
518- char *s;
519- int context = 0;
520- int ret;
521-
522- while (p_end >= 0) {
523- if (p_end == p_efake)
524- p_end = p_bfake; /* don't free twice */
525- else
526- free(p_line[p_end]);
527- p_end--;
528- }
529- p_efake = -1;
530-
531- p_max = hunkmax; /* gets reduced when --- found */
532- if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
533- line_beginning = ftell(pfp);
534- repl_beginning = 0;
535- fillcnt = 0;
536- fillsrc = 0;
537- filldst = 0;
538- ptrn_spaces_eaten = false;
539- repl_could_be_missing = true;
540- repl_missing = false;
541- repl_backtrack_position = 0;
542- repl_patch_line = 0;
543- ptrn_copiable = 0;
544-
545- ret = pgetline(&buf, &bufsz, pfp);
546- p_input_line++;
547- if (ret == -1 || strnNE(buf, "********", 8)) {
548- next_intuit_at(line_beginning, p_input_line);
549- return false;
550- }
551- p_context = 100;
552- p_hunk_beg = p_input_line + 1;
553- while (p_end < p_max) {
554- // line_beginning = ftell(pfp);
555- ret = pgetline(&buf, &bufsz, pfp);
556- p_input_line++;
557- if (ret == -1) {
558- if (repl_beginning && repl_could_be_missing) {
559- repl_missing = true;
560- goto hunk_done;
561- }
562- fatal("unexpected end of file in patch\n");
563- }
564- p_end++;
565- if (p_end >= hunkmax)
566- fatal("Internal error: hunk larger than hunk "
567- "buffer size");
568- p_char[p_end] = *buf;
569- p_line[p_end] = NULL;
570- switch (*buf) {
571- case '*':
572- if (strnEQ(buf, "********", 8)) {
573- if (repl_beginning && repl_could_be_missing) {
574- repl_missing = true;
575- goto hunk_done;
576- } else
577- fatal("unexpected end of hunk "
578- "at line %ld\n",
579- p_input_line);
580- }
581- if (p_end != 0) {
582- if (repl_beginning && repl_could_be_missing) {
583- repl_missing = true;
584- goto hunk_done;
585- }
586- fatal("unexpected *** at line %ld: %s",
587- p_input_line, buf);
588- }
589- context = 0;
590- p_line[p_end] = savestr(buf);
591- if (out_of_mem) {
592- p_end--;
593- return false;
594- }
595- for (s = buf; *s && !isdigit((unsigned char)*s); s++)
596- ;
597- if (!*s)
598- malformed();
599- if (strnEQ(s, "0,0", 3))
600- memmove(s, s + 2, strlen(s + 2) + 1);
601- p_first = getskiplinenum(&s);
602- if (*s == ',') {
603- for (; *s && !isdigit((unsigned char)*s); s++)
604- ;
605- if (!*s)
606- malformed();
607- p_ptrn_lines = (getlinenum(s)) - p_first + 1;
608- if (p_ptrn_lines < 0)
609- malformed();
610- } else if (p_first)
611- p_ptrn_lines = 1;
612- else {
613- p_ptrn_lines = 0;
614- p_first = 1;
615- }
616- if (p_first >= LINENUM_MAX - p_ptrn_lines ||
617- p_ptrn_lines >= LINENUM_MAX - 6)
618- malformed();
619-
620- /* we need this much at least */
621- p_max = p_ptrn_lines + 6;
622- while (p_max >= hunkmax)
623- grow_hunkmax();
624- p_max = hunkmax;
625- break;
626- case '-':
627- if (buf[1] == '-') {
628- if (repl_beginning ||
629- (p_end != p_ptrn_lines + 1 +
630- (p_char[p_end - 1] == '\n'))) {
631- if (p_end == 1) {
632- /*
633- * `old' lines were omitted;
634- * set up to fill them in
635- * from 'new' context lines.
636- */
637- p_end = p_ptrn_lines + 1;
638- fillsrc = p_end + 1;
639- filldst = 1;
640- fillcnt = p_ptrn_lines;
641- } else {
642- if (repl_beginning) {
643- if (repl_could_be_missing) {
644- repl_missing = true;
645- goto hunk_done;
646- }
647- fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
648- p_input_line, p_hunk_beg + repl_beginning);
649- } else {
650- fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
651- (p_end <= p_ptrn_lines
652- ? "Premature"
653- : "Overdue"),
654- p_input_line, p_hunk_beg);
655- }
656- }
657- }
658- repl_beginning = p_end;
659- repl_backtrack_position = ftell(pfp);
660- repl_patch_line = p_input_line;
661- p_line[p_end] = savestr(buf);
662- if (out_of_mem) {
663- p_end--;
664- return false;
665- }
666- p_char[p_end] = '=';
667- for (s = buf; *s && !isdigit((unsigned char)*s); s++)
668- ;
669- if (!*s)
670- malformed();
671- p_newfirst = getskiplinenum(&s);
672- if (*s == ',') {
673- for (; *s && !isdigit((unsigned char)*s); s++)
674- ;
675- if (!*s)
676- malformed();
677- p_repl_lines = (getlinenum(s)) -
678- p_newfirst + 1;
679- if (p_repl_lines < 0)
680- malformed();
681- } else if (p_newfirst)
682- p_repl_lines = 1;
683- else {
684- p_repl_lines = 0;
685- p_newfirst = 1;
686- }
687- if (p_newfirst >= LINENUM_MAX - p_repl_lines ||
688- p_repl_lines >= LINENUM_MAX - p_end)
689- malformed();
690- p_max = p_repl_lines + p_end;
691- if (p_max > MAXHUNKSIZE)
692- fatal("hunk too large (%ld lines) at line %ld: %s",
693- p_max, p_input_line, buf);
694- while (p_max >= hunkmax)
695- grow_hunkmax();
696- if (p_repl_lines != ptrn_copiable &&
697- (p_context != 0 || p_repl_lines != 1))
698- repl_could_be_missing = false;
699- break;
700- }
701- goto change_line;
702- case '+':
703- case '!':
704- repl_could_be_missing = false;
705- change_line:
706- if (buf[1] == '\n' && canonicalize)
707- strlcpy(buf + 1, " \n", bufsz - 1);
708- if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
709- buf[1] != '<' &&
710- repl_beginning && repl_could_be_missing) {
711- repl_missing = true;
712- goto hunk_done;
713- }
714- if (context >= 0) {
715- if (context < p_context)
716- p_context = context;
717- context = -1000;
718- }
719- p_line[p_end] = savestr(buf + 2);
720- if (out_of_mem) {
721- p_end--;
722- return false;
723- }
724- if (p_end == p_ptrn_lines) {
725- if (remove_special_line()) {
726- int len;
727-
728- len = strlen(p_line[p_end]) - 1;
729- (p_line[p_end])[len] = 0;
730- }
731- }
732- break;
733- case '\t':
734- case '\n': /* assume the 2 spaces got eaten */
735- if (repl_beginning && repl_could_be_missing &&
736- (!ptrn_spaces_eaten ||
737- diff_type == NEW_CONTEXT_DIFF)) {
738- repl_missing = true;
739- goto hunk_done;
740- }
741- p_line[p_end] = savestr(buf);
742- if (out_of_mem) {
743- p_end--;
744- return false;
745- }
746- if (p_end != p_ptrn_lines + 1) {
747- ptrn_spaces_eaten |= (repl_beginning != 0);
748- context++;
749- if (!repl_beginning)
750- ptrn_copiable++;
751- p_char[p_end] = ' ';
752- }
753- break;
754- case ' ':
755- if (!isspace((unsigned char)buf[1]) &&
756- repl_beginning && repl_could_be_missing) {
757- repl_missing = true;
758- goto hunk_done;
759- }
760- context++;
761- if (!repl_beginning)
762- ptrn_copiable++;
763- p_line[p_end] = savestr(buf + 2);
764- if (out_of_mem) {
765- p_end--;
766- return false;
767- }
768- break;
769- default:
770- if (repl_beginning && repl_could_be_missing) {
771- repl_missing = true;
772- goto hunk_done;
773- }
774- malformed();
775- }
776- /* set up p_len for strncmp() so we don't have to */
777- /* assume null termination */
778- if (p_line[p_end])
779- p_len[p_end] = strlen(p_line[p_end]);
780- else
781- p_len[p_end] = 0;
782- }
783-
784-hunk_done:
785- if (p_end >= 0 && !repl_beginning)
786- fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
787-
788- if (repl_missing) {
789-
790- /* reset state back to just after --- */
791- p_input_line = repl_patch_line;
792- for (p_end--; p_end > repl_beginning; p_end--)
793- free(p_line[p_end]);
794- fseek(pfp, repl_backtrack_position, SEEK_SET);
795-
796- /* redundant 'new' context lines were omitted - set */
797- /* up to fill them in from the old file context */
798- if (!p_context && p_repl_lines == 1) {
799- p_repl_lines = 0;
800- p_max--;
801- }
802- fillsrc = 1;
803- filldst = repl_beginning + 1;
804- fillcnt = p_repl_lines;
805- p_end = p_max;
806- } else if (!p_context && fillcnt == 1) {
807- /* the first hunk was a null hunk with no context */
808- /* and we were expecting one line -- fix it up. */
809- while (filldst < p_end) {
810- p_line[filldst] = p_line[filldst + 1];
811- p_char[filldst] = p_char[filldst + 1];
812- p_len[filldst] = p_len[filldst + 1];
813- filldst++;
814- }
815-#if 0
816- repl_beginning--; /* this doesn't need to be fixed */
817-#endif
818- p_end--;
819- p_first++; /* do append rather than insert */
820- fillcnt = 0;
821- p_ptrn_lines = 0;
822- }
823- if (diff_type == CONTEXT_DIFF &&
824- (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
825- if (verbose)
826- say("%s\n%s\n%s\n",
827- "(Fascinating--this is really a new-style context diff but without",
828- "the telltale extra asterisks on the *** line that usually indicate",
829- "the new style...)");
830- diff_type = NEW_CONTEXT_DIFF;
831- }
832- /* if there were omitted context lines, fill them in now */
833- if (fillcnt) {
834- p_bfake = filldst; /* remember where not to free() */
835- p_efake = filldst + fillcnt - 1;
836- while (fillcnt-- > 0) {
837- while (fillsrc <= p_end && p_char[fillsrc] != ' ')
838- fillsrc++;
839- if (fillsrc > p_end)
840- fatal("replacement text or line numbers mangled in hunk at line %ld\n",
841- p_hunk_beg);
842- p_line[filldst] = p_line[fillsrc];
843- p_char[filldst] = p_char[fillsrc];
844- p_len[filldst] = p_len[fillsrc];
845- fillsrc++;
846- filldst++;
847- }
848- while (fillsrc <= p_end && fillsrc != repl_beginning &&
849- p_char[fillsrc] != ' ')
850- fillsrc++;
851-#ifdef DEBUGGING
852- if (debug & 64)
853- printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
854- fillsrc, filldst, repl_beginning, p_end + 1);
855-#endif
856- if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
857- malformed();
858- if (filldst != p_end + 1 && filldst != repl_beginning)
859- malformed();
860- }
861- if (p_line[p_end] != NULL) {
862- if (remove_special_line()) {
863- p_len[p_end] -= 1;
864- (p_line[p_end])[p_len[p_end]] = 0;
865- }
866- }
867- } else if (diff_type == UNI_DIFF) {
868- LINENUM fillold; /* index of old lines */
869- LINENUM fillnew; /* index of new lines */
870- char ch;
871-
872- line_beginning = ftell(pfp); /* file pos of the current line */
873- ret = pgetline(&buf, &bufsz, pfp);
874- p_input_line++;
875- if (ret == -1 || strnNE(buf, "@@ -", 4)) {
876- next_intuit_at(line_beginning, p_input_line);
877- return false;
878- }
879- s = buf + 4;
880- if (!*s)
881- malformed();
882- p_first = getskiplinenum(&s);
883- if (*s == ',') {
884- s++;
885- p_ptrn_lines = getskiplinenum(&s);
886- } else
887- p_ptrn_lines = 1;
888- if (p_first >= LINENUM_MAX - p_ptrn_lines)
889- malformed();
890- if (*s == ' ')
891- s++;
892- if (*s != '+' || !*++s)
893- malformed();
894- p_newfirst = getskiplinenum(&s);
895- if (*s == ',') {
896- s++;
897- p_repl_lines = getskiplinenum(&s);
898- } else
899- p_repl_lines = 1;
900- if (*s == ' ')
901- s++;
902- if (*s != '@')
903- malformed();
904- if (p_first >= LINENUM_MAX - p_ptrn_lines ||
905- p_newfirst > LINENUM_MAX - p_repl_lines ||
906- p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
907- malformed();
908- if (!p_ptrn_lines)
909- p_first++; /* do append rather than insert */
910- p_max = p_ptrn_lines + p_repl_lines + 1;
911- while (p_max >= hunkmax)
912- grow_hunkmax();
913- fillold = 1;
914- fillnew = fillold + p_ptrn_lines;
915- p_end = fillnew + p_repl_lines;
916- snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first,
917- p_first + p_ptrn_lines - 1);
918- p_line[0] = savestr(buf);
919- if (out_of_mem) {
920- p_end = -1;
921- return false;
922- }
923- p_char[0] = '*';
924- snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst,
925- p_newfirst + p_repl_lines - 1);
926- p_line[fillnew] = savestr(buf);
927- if (out_of_mem) {
928- p_end = 0;
929- return false;
930- }
931- p_char[fillnew++] = '=';
932- p_context = 100;
933- context = 0;
934- p_hunk_beg = p_input_line + 1;
935- while (fillold <= p_ptrn_lines || fillnew <= p_end) {
936- // line_beginning = ftell(pfp);
937- ret = pgetline(&buf, &bufsz, pfp);
938- p_input_line++;
939- if (ret == -1) {
940- if (p_max - fillnew < 3) {
941- /* assume blank lines got chopped */
942- strlcpy(buf, " \n", bufsz);
943- } else {
944- fatal("unexpected end of file in patch\n");
945- }
946- }
947- if (*buf == '\t' || *buf == '\n') {
948- ch = ' '; /* assume the space got eaten */
949- s = savestr(buf);
950- } else {
951- ch = *buf;
952- s = savestr(buf + 1);
953- }
954- if (out_of_mem) {
955- while (--fillnew > p_ptrn_lines)
956- free(p_line[fillnew]);
957- p_end = fillold - 1;
958- return false;
959- }
960- switch (ch) {
961- case '-':
962- if (fillold > p_ptrn_lines) {
963- free(s);
964- p_end = fillnew - 1;
965- malformed();
966- }
967- p_char[fillold] = ch;
968- p_line[fillold] = s;
969- p_len[fillold++] = strlen(s);
970- if (fillold > p_ptrn_lines) {
971- if (remove_special_line()) {
972- p_len[fillold - 1] -= 1;
973- s[p_len[fillold - 1]] = 0;
974- }
975- }
976- break;
977- case '=':
978- ch = ' ';
979- /* FALL THROUGH */
980- case ' ':
981- if (fillold > p_ptrn_lines) {
982- free(s);
983- while (--fillnew > p_ptrn_lines)
984- free(p_line[fillnew]);
985- p_end = fillold - 1;
986- malformed();
987- }
988- context++;
989- p_char[fillold] = ch;
990- p_line[fillold] = s;
991- p_len[fillold++] = strlen(s);
992- s = savestr(s);
993- if (out_of_mem) {
994- while (--fillnew > p_ptrn_lines)
995- free(p_line[fillnew]);
996- p_end = fillold - 1;
997- return false;
998- }
999- if (fillold > p_ptrn_lines) {
1000- if (remove_special_line()) {
1001- p_len[fillold - 1] -= 1;
1002- s[p_len[fillold - 1]] = 0;
1003- }
1004- }
1005- /* FALL THROUGH */
1006- case '+':
1007- if (fillnew > p_end) {
1008- free(s);
1009- while (--fillnew > p_ptrn_lines)
1010- free(p_line[fillnew]);
1011- p_end = fillold - 1;
1012- malformed();
1013- }
1014- p_char[fillnew] = ch;
1015- p_line[fillnew] = s;
1016- p_len[fillnew++] = strlen(s);
1017- if (fillold > p_ptrn_lines) {
1018- if (remove_special_line()) {
1019- p_len[fillnew - 1] -= 1;
1020- s[p_len[fillnew - 1]] = 0;
1021- }
1022- }
1023- break;
1024- default:
1025- p_end = fillnew;
1026- malformed();
1027- }
1028- if (ch != ' ' && context > 0) {
1029- if (context < p_context)
1030- p_context = context;
1031- context = -1000;
1032- }
1033- } /* while */
1034- } else { /* normal diff--fake it up */
1035- char hunk_type;
1036- int i;
1037- LINENUM min, max;
1038-
1039- line_beginning = ftell(pfp);
1040- p_context = 0;
1041- ret = pgetline(&buf, &bufsz, pfp);
1042- p_input_line++;
1043- if (ret == -1 || !isdigit((unsigned char)*buf)) {
1044- next_intuit_at(line_beginning, p_input_line);
1045- return false;
1046- }
1047- s = buf;
1048- p_first = getskiplinenum(&s);
1049- if (*s == ',') {
1050- s++;
1051- p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
1052- } else
1053- p_ptrn_lines = (*s != 'a');
1054- if (p_first >= LINENUM_MAX - p_ptrn_lines)
1055- malformed();
1056- hunk_type = *s++;
1057- if (hunk_type == 'a')
1058- p_first++; /* do append rather than insert */
1059- min = getskiplinenum(&s);
1060- if (*s == ',')
1061- max = getlinenum(++s);
1062- else
1063- max = min;
1064- if (min < 0 || min > max || max - min == LINENUM_MAX)
1065- malformed();
1066- if (hunk_type == 'd')
1067- min++;
1068- p_end = p_ptrn_lines + 1 + max - min + 1;
1069- p_newfirst = min;
1070- p_repl_lines = max - min + 1;
1071- if (p_newfirst > LINENUM_MAX - p_repl_lines ||
1072- p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
1073- malformed();
1074- p_end = p_ptrn_lines + p_repl_lines + 1;
1075- if (p_end > MAXHUNKSIZE)
1076- fatal("hunk too large (%ld lines) at line %ld: %s",
1077- p_end, p_input_line, buf);
1078- while (p_end >= hunkmax)
1079- grow_hunkmax();
1080- snprintf(buf, bufsz, "*** %ld,%ld\n", p_first,
1081- p_first + p_ptrn_lines - 1);
1082- p_line[0] = savestr(buf);
1083- if (out_of_mem) {
1084- p_end = -1;
1085- return false;
1086- }
1087- p_char[0] = '*';
1088- for (i = 1; i <= p_ptrn_lines; i++) {
1089- ret = pgetline(&buf, &bufsz, pfp);
1090- p_input_line++;
1091- if (ret == -1)
1092- fatal("unexpected end of file in patch at line %ld\n",
1093- p_input_line);
1094- if (*buf != '<')
1095- fatal("< expected at line %ld of patch\n",
1096- p_input_line);
1097- p_line[i] = savestr(buf + 2);
1098- if (out_of_mem) {
1099- p_end = i - 1;
1100- return false;
1101- }
1102- p_len[i] = strlen(p_line[i]);
1103- p_char[i] = '-';
1104- }
1105-
1106- if (remove_special_line()) {
1107- p_len[i - 1] -= 1;
1108- (p_line[i - 1])[p_len[i - 1]] = 0;
1109- }
1110- if (hunk_type == 'c') {
1111- ret = pgetline(&buf, &bufsz, pfp);
1112- p_input_line++;
1113- if (ret == -1)
1114- fatal("unexpected end of file in patch at line %ld\n",
1115- p_input_line);
1116- if (*buf != '-')
1117- fatal("--- expected at line %ld of patch\n",
1118- p_input_line);
1119- }
1120- snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
1121- p_line[i] = savestr(buf);
1122- if (out_of_mem) {
1123- p_end = i - 1;
1124- return false;
1125- }
1126- p_char[i] = '=';
1127- for (i++; i <= p_end; i++) {
1128- ret = pgetline(&buf, &bufsz, pfp);
1129- p_input_line++;
1130- if (ret == -1)
1131- fatal("unexpected end of file in patch at line %ld\n",
1132- p_input_line);
1133- if (*buf != '>')
1134- fatal("> expected at line %ld of patch\n",
1135- p_input_line);
1136- p_line[i] = savestr(buf + 2);
1137- if (out_of_mem) {
1138- p_end = i - 1;
1139- return false;
1140- }
1141- p_len[i] = strlen(p_line[i]);
1142- p_char[i] = '+';
1143- }
1144-
1145- if (remove_special_line()) {
1146- p_len[i - 1] -= 1;
1147- (p_line[i - 1])[p_len[i - 1]] = 0;
1148- }
1149- }
1150- if (reverse) /* backwards patch? */
1151- if (!pch_swap())
1152- say("Not enough memory to swap next hunk!\n");
1153-#ifdef DEBUGGING
1154- if (debug & 2) {
1155- int i;
1156- char special;
1157-
1158- for (i = 0; i <= p_end; i++) {
1159- if (i == p_ptrn_lines)
1160- special = '^';
1161- else
1162- special = ' ';
1163- fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1164- special, p_line[i]);
1165- fflush(stderr);
1166- }
1167- }
1168-#endif
1169- if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1170- p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */
1171- return true;
1172-}
1173-
1174-/*
1175- * Input a line from the patch file, worrying about indentation.
1176- */
1177-int
1178-pgetline(char **bf, size_t *sz, FILE *fp)
1179-{
1180- char *s;
1181- int indent = 0;
1182- int ret;
1183-
1184- ret = getline(bf, sz, fp);
1185-
1186- if (p_indent && ret != -1) {
1187- for (s = buf;
1188- indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1189- s++) {
1190- if (*s == '\t')
1191- indent += 8 - (indent % 7);
1192- else
1193- indent++;
1194- }
1195- if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
1196- fatal("buffer too small in pgetline()\n");
1197- }
1198- return ret;
1199-}
1200-
1201-/*
1202- * Reverse the old and new portions of the current hunk.
1203- */
1204-bool
1205-pch_swap(void)
1206-{
1207- char **tp_line; /* the text of the hunk */
1208- ssize_t *tp_len; /* length of each line */
1209- char *tp_char; /* +, -, and ! */
1210- LINENUM i;
1211- LINENUM n;
1212- bool blankline = false;
1213- char *s;
1214-
1215- i = p_first;
1216- p_first = p_newfirst;
1217- p_newfirst = i;
1218-
1219- /* make a scratch copy */
1220-
1221- tp_line = p_line;
1222- tp_len = p_len;
1223- tp_char = p_char;
1224- p_line = NULL; /* force set_hunkmax to allocate again */
1225- p_len = NULL;
1226- p_char = NULL;
1227- set_hunkmax();
1228- if (p_line == NULL || p_len == NULL || p_char == NULL) {
1229-
1230- free(p_line);
1231- p_line = tp_line;
1232- free(p_len);
1233- p_len = tp_len;
1234- free(p_char);
1235- p_char = tp_char;
1236- return false; /* not enough memory to swap hunk! */
1237- }
1238- /* now turn the new into the old */
1239-
1240- i = p_ptrn_lines + 1;
1241- if (tp_char[i] == '\n') { /* account for possible blank line */
1242- blankline = true;
1243- i++;
1244- }
1245- if (p_efake >= 0) { /* fix non-freeable ptr range */
1246- if (p_efake <= i)
1247- n = p_end - i + 1;
1248- else
1249- n = -i;
1250- p_efake += n;
1251- p_bfake += n;
1252- }
1253- for (n = 0; i <= p_end; i++, n++) {
1254- p_line[n] = tp_line[i];
1255- p_char[n] = tp_char[i];
1256- if (p_char[n] == '+')
1257- p_char[n] = '-';
1258- p_len[n] = tp_len[i];
1259- }
1260- if (blankline) {
1261- i = p_ptrn_lines + 1;
1262- p_line[n] = tp_line[i];
1263- p_char[n] = tp_char[i];
1264- p_len[n] = tp_len[i];
1265- n++;
1266- }
1267- if (p_char[0] != '=')
1268- fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1269- p_input_line, p_char[0]);
1270- p_char[0] = '*';
1271- for (s = p_line[0]; *s; s++)
1272- if (*s == '-')
1273- *s = '*';
1274-
1275- /* now turn the old into the new */
1276-
1277- if (p_char[0] != '*')
1278- fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1279- p_input_line, p_char[0]);
1280- tp_char[0] = '=';
1281- for (s = tp_line[0]; *s; s++)
1282- if (*s == '*')
1283- *s = '-';
1284- for (i = 0; n <= p_end; i++, n++) {
1285- p_line[n] = tp_line[i];
1286- p_char[n] = tp_char[i];
1287- if (p_char[n] == '-')
1288- p_char[n] = '+';
1289- p_len[n] = tp_len[i];
1290- }
1291-
1292- if (i != p_ptrn_lines + 1)
1293- fatal("Malformed patch at line %ld: expected %ld lines, "
1294- "got %ld\n",
1295- p_input_line, p_ptrn_lines + 1, i);
1296-
1297- i = p_ptrn_lines;
1298- p_ptrn_lines = p_repl_lines;
1299- p_repl_lines = i;
1300-
1301- free(tp_line);
1302- free(tp_len);
1303- free(tp_char);
1304-
1305- return true;
1306-}
1307-
1308-/*
1309- * Return the specified line position in the old file of the old context.
1310- */
1311-LINENUM
1312-pch_first(void)
1313-{
1314- return p_first;
1315-}
1316-
1317-/*
1318- * Return the number of lines of old context.
1319- */
1320-LINENUM
1321-pch_ptrn_lines(void)
1322-{
1323- return p_ptrn_lines;
1324-}
1325-
1326-/*
1327- * Return the probable line position in the new file of the first line.
1328- */
1329-LINENUM
1330-pch_newfirst(void)
1331-{
1332- return p_newfirst;
1333-}
1334-
1335-/*
1336- * Return the number of lines in the replacement text including context.
1337- */
1338-LINENUM
1339-pch_repl_lines(void)
1340-{
1341- return p_repl_lines;
1342-}
1343-
1344-/*
1345- * Return the number of lines in the whole hunk.
1346- */
1347-LINENUM
1348-pch_end(void)
1349-{
1350- return p_end;
1351-}
1352-
1353-/*
1354- * Return the number of context lines before the first changed line.
1355- */
1356-LINENUM
1357-pch_context(void)
1358-{
1359- return p_context;
1360-}
1361-
1362-/*
1363- * Return the length of a particular patch line.
1364- */
1365-ssize_t
1366-pch_line_len(LINENUM line)
1367-{
1368- return p_len[line];
1369-}
1370-
1371-/*
1372- * Return the control character (+, -, *, !, etc) for a patch line.
1373- */
1374-char
1375-pch_char(LINENUM line)
1376-{
1377- return p_char[line];
1378-}
1379-
1380-/*
1381- * Return a pointer to a particular patch line.
1382- */
1383-char *
1384-pfetch(LINENUM line)
1385-{
1386- return p_line[line];
1387-}
1388-
1389-/*
1390- * Return where in the patch file this hunk began, for error messages.
1391- */
1392-LINENUM
1393-pch_hunk_beg(void)
1394-{
1395- return p_hunk_beg;
1396-}
1397-
1398-/*
1399- * Apply an ed script by feeding ed itself.
1400- */
1401-void
1402-do_ed_script(void)
1403-{
1404- char *t;
1405- long beginning_of_this_line;
1406- FILE *pipefp = NULL;
1407- int continuation;
1408-
1409- if (!skip_rest_of_patch) {
1410- if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1411- unlink(TMPOUTNAME);
1412- fatal("can't create temp file %s", TMPOUTNAME);
1413- }
1414- snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED,
1415- verbose ? "" : "s", TMPOUTNAME);
1416- pipefp = popen(buf, "w");
1417- }
1418- for (;;) {
1419- beginning_of_this_line = ftell(pfp);
1420- if (pgetline(&buf, &bufsz, pfp) == -1) {
1421- next_intuit_at(beginning_of_this_line, p_input_line);
1422- break;
1423- }
1424- p_input_line++;
1425- for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1426- ;
1427- /* POSIX defines allowed commands as {a,c,d,i,s} */
1428- if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
1429- *t == 'd' || *t == 'i' || *t == 's')) {
1430- if (pipefp != NULL)
1431- fprintf(pipefp, "%s", buf);
1432- if (*t == 's') {
1433- for (;;) {
1434- continuation = 0;
1435- t = strchr(buf, '\0') - 1;
1436- while (--t >= buf && *t == '\\')
1437- continuation = !continuation;
1438- if (!continuation ||
1439- pgetline(&buf, &bufsz, pfp) == -1)
1440- break;
1441- if (pipefp != NULL)
1442- fprintf(pipefp, "%s", buf);
1443- }
1444- } else if (*t != 'd') {
1445- while (pgetline(&buf, &bufsz, pfp) != -1) {
1446- p_input_line++;
1447- if (pipefp != NULL)
1448- fprintf(pipefp, "%s", buf);
1449- if (strEQ(buf, ".\n"))
1450- break;
1451- }
1452- }
1453- } else {
1454- next_intuit_at(beginning_of_this_line, p_input_line);
1455- break;
1456- }
1457- }
1458- if (pipefp == NULL)
1459- return;
1460- fprintf(pipefp, "w\n");
1461- fprintf(pipefp, "q\n");
1462- fflush(pipefp);
1463- pclose(pipefp);
1464- ignore_signals();
1465- if (!check_only) {
1466- if (move_file(TMPOUTNAME, outname) < 0) {
1467- toutkeep = true;
1468- chmod(TMPOUTNAME, filemode);
1469- } else
1470- chmod(outname, filemode);
1471- }
1472- set_signals(1);
1473-}
1474-
1475-/*
1476- * Choose the name of the file to be patched based on POSIX rules.
1477- * NOTE: the POSIX rules are amazingly stupid and we only follow them
1478- * if the user specified --posix or set POSIXLY_CORRECT.
1479- */
1480-static char *
1481-posix_name(const struct file_name *names, bool assume_exists)
1482-{
1483- char *path = NULL;
1484- int i;
1485-
1486- /*
1487- * POSIX states that the filename will be chosen from one
1488- * of the old, new and index names (in that order) if
1489- * the file exists relative to CWD after -p stripping.
1490- */
1491- for (i = 0; i < MAX_FILE; i++) {
1492- if (names[i].path != NULL && names[i].exists) {
1493- path = names[i].path;
1494- break;
1495- }
1496- }
1497- if (path == NULL && !assume_exists) {
1498- /*
1499- * No files found, look for something we can checkout from
1500- * RCS/SCCS dirs. Same order as above.
1501- */
1502- for (i = 0; i < MAX_FILE; i++) {
1503- if (names[i].path != NULL &&
1504- (path = checked_in(names[i].path)) != NULL)
1505- break;
1506- }
1507- /*
1508- * Still no match? Check to see if the diff could be creating
1509- * a new file.
1510- */
1511- if (path == NULL && ok_to_create_file &&
1512- names[NEW_FILE].path != NULL)
1513- path = names[NEW_FILE].path;
1514- }
1515-
1516- return path ? savestr(path) : NULL;
1517-}
1518-
1519-/*
1520- * Choose the name of the file to be patched based the "best" one
1521- * available.
1522- */
1523-static char *
1524-best_name(const struct file_name *names, bool assume_exists)
1525-{
1526- size_t min_components, min_baselen, min_len, tmp;
1527- char *best = NULL;
1528- int i;
1529-
1530- /*
1531- * The "best" name is the one with the fewest number of path
1532- * components, the shortest basename length, and the shortest
1533- * overall length (in that order). We only use the Index: file
1534- * if neither of the old or new files could be intuited from
1535- * the diff header.
1536- */
1537- min_components = min_baselen = min_len = SIZE_MAX;
1538- for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1539- if (names[i].path == NULL ||
1540- (!names[i].exists && !assume_exists))
1541- continue;
1542- if ((tmp = num_components(names[i].path)) > min_components)
1543- continue;
1544- min_components = tmp;
1545- if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1546- continue;
1547- min_baselen = tmp;
1548- if ((tmp = strlen(names[i].path)) > min_len)
1549- continue;
1550- min_len = tmp;
1551- best = names[i].path;
1552- }
1553- if (best == NULL) {
1554- /*
1555- * No files found, look for something we can checkout from
1556- * RCS/SCCS dirs. Logic is identical to that above...
1557- */
1558- min_components = min_baselen = min_len = SIZE_MAX;
1559- for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1560- if (names[i].path == NULL ||
1561- checked_in(names[i].path) == NULL)
1562- continue;
1563- if ((tmp = num_components(names[i].path)) > min_components)
1564- continue;
1565- min_components = tmp;
1566- if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1567- continue;
1568- min_baselen = tmp;
1569- if ((tmp = strlen(names[i].path)) > min_len)
1570- continue;
1571- min_len = tmp;
1572- best = names[i].path;
1573- }
1574- /*
1575- * Still no match? Check to see if the diff could be creating
1576- * a new file.
1577- */
1578- if (best == NULL && ok_to_create_file &&
1579- names[NEW_FILE].path != NULL)
1580- best = names[NEW_FILE].path;
1581- }
1582-
1583- return best ? savestr(best) : NULL;
1584-}
1585-
1586-static size_t
1587-num_components(const char *path)
1588-{
1589- size_t n;
1590- const char *cp;
1591-
1592- for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1593- while (*cp == '/')
1594- cp++; /* skip consecutive slashes */
1595- }
1596- return n;
1597-}
+0,
-59
1@@ -1,59 +0,0 @@
2-/*
3- * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
4- * $DragonFly: src/usr.bin/patch/pch.h,v 1.1 2004/09/24 18:44:28 joerg Exp $
5- * $NetBSD: pch.h,v 1.11 2024/07/12 15:48:39 manu Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#define OLD_FILE 0
35-#define NEW_FILE 1
36-#define INDEX_FILE 2
37-#define MAX_FILE 3
38-
39-struct file_name {
40- char *path;
41- bool exists;
42-};
43-
44-void re_patch(void);
45-void open_patch_file(const char *);
46-void set_hunkmax(void);
47-bool there_is_another_patch(void);
48-bool another_hunk(void);
49-bool pch_swap(void);
50-char *pfetch(LINENUM);
51-ssize_t pch_line_len(LINENUM);
52-LINENUM pch_first(void);
53-LINENUM pch_ptrn_lines(void);
54-LINENUM pch_newfirst(void);
55-LINENUM pch_repl_lines(void);
56-LINENUM pch_end(void);
57-LINENUM pch_context(void);
58-LINENUM pch_hunk_beg(void);
59-char pch_char(LINENUM);
60-void do_ed_script(void);
+0,
-444
1@@ -1,444 +0,0 @@
2-/*
3- * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
4- * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
5- * $NetBSD: util.c,v 1.30 2021/05/25 11:25:59 cjep Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#include <sys/param.h>
35-#include <sys/stat.h>
36-
37-#include <ctype.h>
38-#include <errno.h>
39-#include <fcntl.h>
40-#include <libgen.h>
41-#include <signal.h>
42-#include <stdarg.h>
43-#include <stdlib.h>
44-#include <stdio.h>
45-#include <string.h>
46-#include <unistd.h>
47-
48-#include "common.h"
49-#include "util.h"
50-#include "backupfile.h"
51-#include "pathnames.h"
52-
53-/* Rename a file, copying it if necessary. */
54-
55-int
56-move_file(const char *from, const char *to)
57-{
58- int fromfd;
59- ssize_t i;
60-
61- /* to stdout? */
62-
63- if (strEQ(to, "-")) {
64-#ifdef DEBUGGING
65- if (debug & 4)
66- say("Moving %s to stdout.\n", from);
67-#endif
68- fromfd = open(from, O_RDONLY);
69- if (fromfd < 0)
70- pfatal("internal error, can't reopen %s", from);
71- while ((i = read(fromfd, buf, bufsz)) > 0)
72- if (write(STDOUT_FILENO, buf, i) != i)
73- pfatal("write failed");
74- close(fromfd);
75- return 0;
76- }
77- if (backup_file(to) < 0) {
78- say("Can't backup %s, output is in %s: %s\n", to, from,
79- strerror(errno));
80- return -1;
81- }
82-#ifdef DEBUGGING
83- if (debug & 4)
84- say("Moving %s to %s.\n", from, to);
85-#endif
86- if (rename(from, to) < 0) {
87- if (errno != EXDEV || copy_file(from, to) < 0) {
88- say("Can't create %s, output is in %s: %s\n",
89- to, from, strerror(errno));
90- return -1;
91- }
92- }
93- return 0;
94-}
95-
96-/* Backup the original file. */
97-
98-int
99-backup_file(const char *orig)
100-{
101- struct stat filestat;
102- char bakname[PATH_MAX], *s, *simplename;
103- dev_t orig_device;
104- ino_t orig_inode;
105-
106- if (backup_type == none || stat(orig, &filestat) != 0)
107- return 0; /* nothing to do */
108- /*
109- * If the user used zero prefixes or suffixes, then
110- * he doesn't want backups. Yet we have to remove
111- * orig to break possible hardlinks.
112- */
113- if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
114- unlink(orig);
115- return 0;
116- }
117- orig_device = filestat.st_dev;
118- orig_inode = filestat.st_ino;
119-
120- if (origprae) {
121- if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
122- strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
123- fatal("filename %s too long for buffer\n", origprae);
124- } else {
125- if ((s = find_backup_file_name(orig)) == NULL)
126- fatal("out of memory\n");
127- if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
128- fatal("filename %s too long for buffer\n", s);
129- free(s);
130- }
131-
132- if ((simplename = strrchr(bakname, '/')) != NULL)
133- simplename = simplename + 1;
134- else
135- simplename = bakname;
136-
137- /*
138- * Find a backup name that is not the same file. Change the
139- * first lowercase char into uppercase; if that isn't
140- * sufficient, chop off the first char and try again.
141- */
142- while (stat(bakname, &filestat) == 0 &&
143- orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
144- /* Skip initial non-lowercase chars. */
145- for (s = simplename; *s && !islower((unsigned char)*s); s++)
146- ;
147- if (*s)
148- *s = toupper((unsigned char)*s);
149- else
150- memmove(simplename, simplename + 1,
151- strlen(simplename + 1) + 1);
152- }
153-#ifdef DEBUGGING
154- if (debug & 4)
155- say("Moving %s to %s.\n", orig, bakname);
156-#endif
157- if (rename(orig, bakname) < 0) {
158- if (errno != EXDEV || copy_file(orig, bakname) < 0)
159- return -1;
160- }
161- return 0;
162-}
163-
164-/*
165- * Copy a file.
166- */
167-int
168-copy_file(const char *from, const char *to)
169-{
170- int tofd, fromfd;
171- ssize_t i;
172-
173- tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
174- if (tofd < 0)
175- return -1;
176- fromfd = open(from, O_RDONLY, 0);
177- if (fromfd < 0)
178- pfatal("internal error, can't reopen %s", from);
179- while ((i = read(fromfd, buf, bufsz)) > 0)
180- if (write(tofd, buf, i) != i)
181- pfatal("write to %s failed", to);
182- close(fromfd);
183- close(tofd);
184- return 0;
185-}
186-
187-/*
188- * Allocate a unique area for a string.
189- */
190-char *
191-savestr(const char *s)
192-{
193- char *rv;
194-
195- if (!s)
196- s = "Oops";
197- rv = strdup(s);
198- if (rv == NULL) {
199- if (using_plan_a)
200- out_of_mem = true;
201- else
202- fatal("out of memory\n");
203- }
204- return rv;
205-}
206-
207-/*
208- * Vanilla terminal output (buffered).
209- */
210-void
211-say(const char *fmt, ...)
212-{
213- va_list ap;
214-
215- va_start(ap, fmt);
216- vfprintf(stderr, fmt, ap);
217- va_end(ap);
218- fflush(stderr);
219-}
220-
221-/*
222- * Terminal output, pun intended.
223- */
224-void
225-fatal(const char *fmt, ...)
226-{
227- va_list ap;
228-
229- va_start(ap, fmt);
230- fprintf(stderr, "patch: **** ");
231- vfprintf(stderr, fmt, ap);
232- va_end(ap);
233- my_exit(2);
234-}
235-
236-/*
237- * Say something from patch, something from the system, then silence . . .
238- */
239-void
240-pfatal(const char *fmt, ...)
241-{
242- va_list ap;
243- int errnum = errno;
244-
245- fprintf(stderr, "patch: **** ");
246- va_start(ap, fmt);
247- vfprintf(stderr, fmt, ap);
248- va_end(ap);
249- fprintf(stderr, ": %s\n", strerror(errnum));
250- my_exit(2);
251-}
252-
253-/*
254- * Get a response from the user via /dev/tty
255- */
256-void
257-ask(const char *fmt, ...)
258-{
259- va_list ap;
260- ssize_t nr = 0;
261- static int ttyfd = -1;
262-
263- va_start(ap, fmt);
264- vfprintf(stdout, fmt, ap);
265- va_end(ap);
266- fflush(stdout);
267- if (ttyfd < 0)
268- ttyfd = open(_PATH_TTY, O_RDONLY);
269- if (ttyfd >= 0) {
270- if ((nr = read(ttyfd, buf, bufsz)) > 0 &&
271- buf[nr - 1] == '\n')
272- buf[nr - 1] = '\0';
273- }
274- if (ttyfd < 0 || nr <= 0) {
275- /* no tty or error reading, pretend user entered 'return' */
276- putchar('\n');
277- buf[0] = '\0';
278- }
279-}
280-
281-/*
282- * How to handle certain events when not in a critical region.
283- */
284-void
285-set_signals(int reset)
286-{
287- static sig_t hupval, intval;
288-
289- if (!reset) {
290- hupval = signal(SIGHUP, SIG_IGN);
291- if (hupval != SIG_IGN)
292- hupval = my_exit;
293- intval = signal(SIGINT, SIG_IGN);
294- if (intval != SIG_IGN)
295- intval = my_exit;
296- }
297- signal(SIGHUP, hupval);
298- signal(SIGINT, intval);
299-}
300-
301-/*
302- * How to handle certain events when in a critical region.
303- */
304-void
305-ignore_signals(void)
306-{
307- signal(SIGHUP, SIG_IGN);
308- signal(SIGINT, SIG_IGN);
309-}
310-
311-/*
312- * Make sure we'll have the directories to create a file. If `striplast' is
313- * true, ignore the last element of `filename'.
314- */
315-
316-void
317-makedirs(const char *filename, bool striplast)
318-{
319- char *tmpbuf;
320-
321- if ((tmpbuf = strdup(filename)) == NULL)
322- fatal("out of memory\n");
323-
324- if (striplast) {
325- char *s = strrchr(tmpbuf, '/');
326- if (s == NULL) {
327- free(tmpbuf);
328- return; /* nothing to be done */
329- }
330- *s = '\0';
331- }
332- if (mkpath(tmpbuf) != 0)
333- pfatal("creation of %s failed", tmpbuf);
334- free(tmpbuf);
335-}
336-
337-/*
338- * Make filenames more reasonable.
339- */
340-char *
341-fetchname(const char *at, bool *exists, int strip_leading)
342-{
343- char *fullname, *name, *t;
344- int sleading, tab;
345- struct stat filestat;
346-
347- if (at == NULL || *at == '\0')
348- return NULL;
349- while (isspace((unsigned char)*at))
350- at++;
351-#ifdef DEBUGGING
352- if (debug & 128)
353- say("fetchname %s %d\n", at, strip_leading);
354-#endif
355- /* So files can be created by diffing against /dev/null. */
356- if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
357- return NULL;
358- name = fullname = t = savestr(at);
359-
360- tab = strchr(t, '\t') != NULL;
361- /* Strip off up to `strip_leading' path components and NUL terminate. */
362- for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
363- !isspace((unsigned char)*t)); t++) {
364- if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
365- if (--sleading >= 0)
366- name = t + 1;
367- }
368- *t = '\0';
369-
370- /*
371- * If no -p option was given (957 is the default value!), we were
372- * given a relative pathname, and the leading directories that we
373- * just stripped off all exist, put them back on.
374- */
375- if (strip_leading == 957 && name != fullname && *fullname != '/') {
376- name[-1] = '\0';
377- if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
378- name[-1] = '/';
379- name = fullname;
380- }
381- }
382- name = savestr(name);
383- free(fullname);
384-
385- *exists = stat(name, &filestat) == 0;
386- return name;
387-}
388-
389-/*
390- * Takes the name returned by fetchname and looks in RCS/SCCS directories
391- * for a checked in version.
392- */
393-char *
394-checked_in(char *file)
395-{
396- char *filebase, *filedir, tmpbuf[PATH_MAX];
397- struct stat filestat;
398-
399- filebase = basename(file);
400- filedir = dirname(file);
401-
402-#define try(f, a1, a2, a3) \
403-(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
404-
405- if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
406- try("%s/RCS/%s%s", filedir, filebase, "") ||
407- try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
408- try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
409- try("%s/%s%s", filedir, SCCSPREFIX, filebase))
410- return file;
411-
412- return NULL;
413-}
414-
415-void
416-version(void)
417-{
418- printf("Patch version 2.0-12u9-NetBSD\n");
419- my_exit(EXIT_SUCCESS);
420-}
421-
422-/*
423- * Exit with cleanup.
424- */
425-void
426-my_exit(int status)
427-{
428- unlink(TMPINNAME);
429- if (!toutkeep)
430- unlink(TMPOUTNAME);
431- if (!trejkeep)
432- unlink(TMPREJNAME);
433- unlink(TMPPATNAME);
434- exit(status);
435-}
436-
437-void *
438-pch_realloc(void *ptr, size_t number, size_t size)
439-{
440- if (number > SIZE_MAX / size) {
441- errno = EOVERFLOW;
442- return NULL;
443- }
444- return realloc(ptr, number * size);
445-}
+0,
-52
1@@ -1,52 +0,0 @@
2-/*
3- * $OpenBSD: util.h,v 1.15 2005/06/20 07:14:06 otto Exp $
4- * $DragonFly: src/usr.bin/patch/util.h,v 1.2 2007/09/29 23:11:10 swildner Exp $
5- * $NetBSD: util.h,v 1.13 2018/06/18 18:33:31 christos Exp $
6- */
7-
8-/*
9- * patch - a program to apply diffs to original files
10- *
11- * Copyright 1986, Larry Wall
12- *
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- *
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- *
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-char *fetchname(const char *, bool *, int);
35-char *checked_in(char *);
36-LINENUM strtolinenum(char *, char **);
37-int backup_file(const char *);
38-int move_file(const char *, const char *);
39-int copy_file(const char *, const char *);
40-void say(const char *, ...) __printflike(1, 2);
41-void fatal(const char *, ...) __printflike(1, 2) __dead;
42-void pfatal(const char *, ...) __printflike(1, 2) __dead;
43-void ask(const char *, ...) __printflike(1, 2);
44-char *savestr(const char *);
45-void set_signals(int);
46-void ignore_signals(void);
47-void makedirs(const char *, bool);
48-void version(void) __dead;
49-void my_exit(int) __dead;
50-void *pch_realloc(void *, size_t, size_t);
51-
52-/* in mkpath.c */
53-extern int mkpath(char *);
+164,
-0
1@@ -0,0 +1,164 @@
2+/* See LICENSE file for copyright and license details. */
3+#include "util.h"
4+
5+#include <ctype.h>
6+#include <fcntl.h>
7+#include <stdio.h>
8+#include <stdlib.h>
9+#include <string.h>
10+#include <unistd.h>
11+
12+static void
13+usage(void)
14+{
15+ eprintf("usage: %s [-P fd] [-m type] [-S salt] [password] [salt]\n", argv0);
16+}
17+
18+// ?man mkpasswd: encrypt the given password using salt
19+// ?man arguments: [password] [salt]
20+// ?man encrypt password using crypt(3) with random or provided salt
21+int
22+main(int argc, char *argv[])
23+{
24+ char *mflag;
25+ char *sflag;
26+ char *parg;
27+ char *password;
28+ char *cryptpass;
29+ int pfd;
30+ int fd;
31+ int r;
32+ int i;
33+ int len;
34+ int id;
35+ char salt[128];
36+ char rand_bytes[32];
37+ char passbuf[1024];
38+ static const char saltchars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
39+
40+ mflag = NULL;
41+ sflag = NULL;
42+ parg = NULL;
43+ password = NULL;
44+ cryptpass = NULL;
45+ pfd = -1;
46+
47+ ARGBEGIN {
48+ // ?man -P:fd: read password from file descriptor
49+ case 'P':
50+ parg = EARGF(usage());
51+ pfd = estrtonum(parg, 0, INT_MAX);
52+ break;
53+ // ?man -m:type: encryption method (des, md5, sha256, sha512)
54+ case 'm':
55+ mflag = EARGF(usage());
56+ break;
57+ // ?man -S:salt: salt to use
58+ case 'S':
59+ sflag = EARGF(usage());
60+ break;
61+ default:
62+ usage();
63+ } ARGEND
64+
65+ if (argc > 2)
66+ usage();
67+
68+ if (argc == 2) {
69+ if (sflag)
70+ eprintf("duplicate salt\n");
71+ sflag = argv[1];
72+ }
73+
74+ if (argc >= 1)
75+ password = argv[0];
76+
77+ /* default type is des */
78+ if (!mflag)
79+ mflag = "des";
80+
81+ if (strcasecmp(mflag, "des") == 0) {
82+ id = 0;
83+ len = 2;
84+ } else if (strcasecmp(mflag, "md5") == 0) {
85+ id = 1;
86+ len = 8;
87+ } else if (strcasecmp(mflag, "sha256") == 0) {
88+ id = 5;
89+ len = 16;
90+ } else if (strcasecmp(mflag, "sha512") == 0) {
91+ id = 6;
92+ len = 16;
93+ } else {
94+ eprintf("bad method: %s\n", mflag);
95+ }
96+
97+ if (sflag) {
98+ /* validate provided salt */
99+ char *s = sflag;
100+ while (*s) {
101+ if (!isalnum((unsigned char)*s) && *s != '.' && *s != '/')
102+ eprintf("bad SALT (need [a-zA-Z0-9./])\n");
103+ s++;
104+ }
105+ if (id > 0)
106+ snprintf(salt, sizeof(salt), "$%d$%s", id, sflag);
107+ else
108+ estrlcpy(salt, sflag, sizeof(salt));
109+ } else {
110+ /* generate random salt */
111+ fd = open("/dev/urandom", O_RDONLY);
112+ if (fd < 0)
113+ eprintf("open /dev/urandom:");
114+ r = read(fd, rand_bytes, len);
115+ if (r < len)
116+ eprintf("read /dev/urandom:");
117+ close(fd);
118+
119+ if (id > 0) {
120+ snprintf(salt, sizeof(salt), "$%d$", id);
121+ for (i = 0; i < len; i++)
122+ salt[3 + i] = saltchars[(unsigned char)rand_bytes[i] % 64];
123+ salt[3 + len] = '\0';
124+ } else {
125+ for (i = 0; i < len; i++)
126+ salt[i] = saltchars[(unsigned char)rand_bytes[i] % 64];
127+ salt[len] = '\0';
128+ }
129+ }
130+
131+ if (pfd >= 0) {
132+ if (dup2(pfd, 0) == -1)
133+ eprintf("dup2:");
134+ close(pfd);
135+ }
136+
137+ if (!password) {
138+ if (isatty(0)) {
139+ char *p = getpass("Password: ");
140+ if (!p)
141+ eprintf("getpass failed\n");
142+ estrlcpy(passbuf, p, sizeof(passbuf));
143+ password = passbuf;
144+ } else {
145+ /* read from stdin */
146+ int bytes = read(0, passbuf, sizeof(passbuf) - 1);
147+ if (bytes < 0)
148+ eprintf("read stdin:");
149+ /* strip trailing newline or cr */
150+ while (bytes > 0 && (passbuf[bytes - 1] == '\n' || passbuf[bytes - 1] == '\r'))
151+ bytes--;
152+ passbuf[bytes] = '\0';
153+ password = passbuf;
154+ }
155+ }
156+
157+ cryptpass = crypt(password, salt);
158+ if (!cryptpass)
159+ eprintf("crypt:");
160+ printf("%s\n", cryptpass);
161+
162+ if (fshut(stdout, "<stdout>"))
163+ return 2;
164+ return 0;
165+}
M
deps.mk
+4,
-72
1@@ -20,11 +20,6 @@ cmd/extra/lsblk.o: shared/util.h
2 cmd/extra/lsblk.o: shared/arg.h
3 cmd/extra/lsblk.o: shared/compat.h
4 cmd/extra/lsblk.o: shared/diskutil.h
5-cmd/extra/diff3/diff3.o: cmd/extra/diff3/diff3.c
6-cmd/extra/diff3/diff3.o: shared/util.h
7-cmd/extra/diff3/diff3.o: shared/arg.h
8-cmd/extra/diff3/diff3.o: shared/compat.h
9-cmd/extra/diff3/diff3.o: cmd/extra/diff3/../../posix/diff/xmalloc.h
10 cmd/extra/sync.o: cmd/extra/sync.c
11 cmd/extra/sync.o: shared/arg.h
12 cmd/extra/sync.o: shared/util.h
13@@ -858,36 +853,7 @@ cmd/posix/date.o: cmd/posix/date.c
14 cmd/posix/date.o: shared/util.h
15 cmd/posix/date.o: shared/arg.h
16 cmd/posix/date.o: shared/compat.h
17-cmd/posix/diff/diff.o: cmd/posix/diff/diff.c
18-cmd/posix/diff/diff.o: shared/util.h
19-cmd/posix/diff/diff.o: shared/arg.h
20-cmd/posix/diff/diff.o: shared/compat.h
21-cmd/posix/diff/diff.o: cmd/posix/diff/diff.h
22-cmd/posix/diff/diff.o: cmd/posix/diff/xmalloc.h
23-cmd/posix/diff/diffdir.o: cmd/posix/diff/diffdir.c
24-cmd/posix/diff/diffdir.o: shared/util.h
25-cmd/posix/diff/diffdir.o: shared/arg.h
26-cmd/posix/diff/diffdir.o: shared/compat.h
27-cmd/posix/diff/diffdir.o: cmd/posix/diff/diff.h
28-cmd/posix/diff/diffreg.o: cmd/posix/diff/diffreg.c
29-cmd/posix/diff/diffreg.o: shared/util.h
30-cmd/posix/diff/diffreg.o: shared/arg.h
31-cmd/posix/diff/diffreg.o: shared/compat.h
32-cmd/posix/diff/diffreg.o: cmd/posix/diff/pr.h
33-cmd/posix/diff/diffreg.o: cmd/posix/diff/diff.h
34-cmd/posix/diff/diffreg.o: cmd/posix/diff/xmalloc.h
35-cmd/posix/diff/pr.o: cmd/posix/diff/pr.c
36-cmd/posix/diff/pr.o: shared/util.h
37-cmd/posix/diff/pr.o: shared/arg.h
38-cmd/posix/diff/pr.o: shared/compat.h
39-cmd/posix/diff/pr.o: cmd/posix/diff/pr.h
40-cmd/posix/diff/pr.o: cmd/posix/diff/diff.h
41-cmd/posix/diff/pr.o: cmd/posix/diff/xmalloc.h
42-cmd/posix/diff/xmalloc.o: cmd/posix/diff/xmalloc.c
43-cmd/posix/diff/xmalloc.o: shared/util.h
44-cmd/posix/diff/xmalloc.o: shared/arg.h
45-cmd/posix/diff/xmalloc.o: shared/compat.h
46-cmd/posix/diff/xmalloc.o: cmd/posix/diff/xmalloc.h
47+cmd/posix/diff.o: cmd/posix/diff.c shared/util.h shared/arg.h shared/compat.h
48 cmd/posix/env.o: cmd/posix/env.c
49 cmd/posix/env.o: shared/util.h
50 cmd/posix/env.o: shared/arg.h
51@@ -949,43 +915,9 @@ cmd/posix/uuencode.o: cmd/posix/uuencode.c
52 cmd/posix/uuencode.o: shared/util.h
53 cmd/posix/uuencode.o: shared/arg.h
54 cmd/posix/uuencode.o: shared/compat.h
55-cmd/posix/patch/backupfile.o: cmd/posix/patch/backupfile.c
56-cmd/posix/patch/backupfile.o: cmd/posix/patch/backupfile.h
57-cmd/posix/patch/inp.o: cmd/posix/patch/inp.c
58-cmd/posix/patch/inp.o: cmd/posix/patch/common.h
59-cmd/posix/patch/inp.o: shared/util.h
60-cmd/posix/patch/inp.o: shared/arg.h
61-cmd/posix/patch/inp.o: shared/compat.h
62-cmd/posix/patch/inp.o: cmd/posix/patch/util.h
63-cmd/posix/patch/inp.o: cmd/posix/patch/pch.h
64-cmd/posix/patch/inp.o: cmd/posix/patch/inp.h
65-cmd/posix/patch/mkpath.o: cmd/posix/patch/mkpath.c
66-cmd/posix/patch/patch.o: cmd/posix/patch/patch.c
67-cmd/posix/patch/patch.o: cmd/posix/patch/common.h
68-cmd/posix/patch/patch.o: shared/util.h
69-cmd/posix/patch/patch.o: shared/arg.h
70-cmd/posix/patch/patch.o: shared/compat.h
71-cmd/posix/patch/patch.o: cmd/posix/patch/util.h
72-cmd/posix/patch/patch.o: cmd/posix/patch/pch.h
73-cmd/posix/patch/patch.o: cmd/posix/patch/inp.h
74-cmd/posix/patch/patch.o: cmd/posix/patch/backupfile.h
75-cmd/posix/patch/patch.o: cmd/posix/patch/pathnames.h
76-cmd/posix/patch/pch.o: cmd/posix/patch/pch.c
77-cmd/posix/patch/pch.o: cmd/posix/patch/common.h
78-cmd/posix/patch/pch.o: shared/util.h
79-cmd/posix/patch/pch.o: shared/arg.h
80-cmd/posix/patch/pch.o: shared/compat.h
81-cmd/posix/patch/pch.o: cmd/posix/patch/util.h
82-cmd/posix/patch/pch.o: cmd/posix/patch/pch.h
83-cmd/posix/patch/pch.o: cmd/posix/patch/pathnames.h
84-cmd/posix/patch/util.o: cmd/posix/patch/util.c
85-cmd/posix/patch/util.o: cmd/posix/patch/common.h
86-cmd/posix/patch/util.o: shared/util.h
87-cmd/posix/patch/util.o: shared/arg.h
88-cmd/posix/patch/util.o: shared/compat.h
89-cmd/posix/patch/util.o: cmd/posix/patch/util.h
90-cmd/posix/patch/util.o: cmd/posix/patch/backupfile.h
91-cmd/posix/patch/util.o: cmd/posix/patch/pathnames.h
92+cmd/posix/patch.o: cmd/posix/patch.c shared/util.h shared/arg.h shared/compat.h
93+cmd/posix/diff3.o: cmd/posix/diff3.c shared/util.h shared/arg.h shared/compat.h
94+cmd/pseudo/mkpasswd.o: cmd/pseudo/mkpasswd.c shared/util.h shared/arg.h shared/compat.h
95 cmd/pseudo/base64.o: cmd/pseudo/base64.c
96 cmd/pseudo/base64.o: shared/util.h
97 cmd/pseudo/base64.o: shared/arg.h
1@@ -2,18 +2,30 @@
2 #ifndef ARUU_DISKUTIL_H
3 #define ARUU_DISKUTIL_H
4
5-#include <stdint.h>
6 #include <stddef.h>
7+#include <stdint.h>
8+
9+#ifndef PATH_DEV
10+#define PATH_DEV "/dev"
11+#endif
12+
13+#ifndef PATH_PROC
14+#define PATH_PROC "/proc"
15+#endif
16+
17+#define PATH_PARTITIONS PATH_PROC "/partitions"
18+#define PATH_MOUNTS PATH_PROC "/mounts"
19
20 struct BlockDev {
21 int fd;
22- uint64_t size; /* size in bytes */
23+ uint64_t size; /* size in bytes */
24 size_t sec_size; /* sector size */
25 };
26
27 struct BlockDevInfo {
28 char name[32];
29- char type[16]; /* disk, part, loop */
30+ char path[128]; /* absolute path to device node */
31+ char type[16]; /* disk, part, loop */
32 uint64_t size;
33 int major;
34 int minor;
35@@ -24,10 +36,16 @@ struct BlockDevInfo {
36 };
37
38 int blockdev_open(struct BlockDev *dev, const char *path, int writable);
39-int blockdev_read(struct BlockDev *dev, uint64_t sector, void *buf, size_t sector_count);
40-int blockdev_write(struct BlockDev *dev, uint64_t sector, const void *buf, size_t sector_count);
41+int blockdev_read(struct BlockDev *dev, uint64_t sector, void *buf,
42+ size_t sector_count);
43+int blockdev_write(struct BlockDev *dev, uint64_t sector, const void *buf,
44+ size_t sector_count);
45 void blockdev_close(struct BlockDev *dev);
46
47+int blockdev_detect_fs(struct BlockDev *dev, char *type, size_t type_sz,
48+ char *label, size_t label_sz, char *uuid,
49+ size_t uuid_sz);
50+
51 struct BlockDevInfo *blockdev_get_list(void);
52 void blockdev_free_list(struct BlockDevInfo *list);
53
1@@ -2,14 +2,14 @@
2 #include "../diskutil.h"
3 #include "../util.h"
4
5-#include <sys/types.h>
6-#include <sys/stat.h>
7+#include <errno.h>
8 #include <fcntl.h>
9-#include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13-#include <errno.h>
14+#include <sys/stat.h>
15+#include <sys/types.h>
16+#include <unistd.h>
17
18 #if defined(__linux__)
19 #include <sys/ioctl.h>
20@@ -123,13 +123,22 @@ blockdev_get_list(void)
21 {
22 #if defined(__linux__)
23 FILE *fp;
24+ FILE *mfile;
25 char line[256];
26- struct BlockDevInfo *head = NULL, *tail = NULL;
27+ char mline[512];
28+ char devpath[128];
29+ char mnt[256];
30+ char fs[64];
31+ struct BlockDevInfo *head = NULL;
32+ struct BlockDevInfo *tail = NULL;
33+ struct BlockDevInfo *info;
34+ struct BlockDevInfo *curr;
35 unsigned int major, minor;
36 unsigned long long blocks;
37 char name[64];
38+ int is_part;
39
40- fp = fopen("/proc/partitions", "r");
41+ fp = fopen(PATH_PARTITIONS, "r");
42 if (!fp)
43 return NULL;
44
45@@ -137,24 +146,28 @@ blockdev_get_list(void)
46 if (sscanf(line, " %u %u %llu %63s", &major, &minor, &blocks, name) != 4)
47 continue;
48
49- struct BlockDevInfo *info = calloc(1, sizeof(*info));
50- if (!info)
51- break;
52+ info = ecalloc(1, sizeof(*info));
53
54 strlcpy(info->name, name, sizeof(info->name));
55+ snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, name);
56 info->size = blocks * 1024;
57 info->major = major;
58 info->minor = minor;
59
60- /* check if this is a partition of an existing disk */
61- struct BlockDevInfo *curr;
62- int is_part = 0;
63+ /* search tree topology to link partition node under proper target parent disk */
64+ is_part = 0;
65 for (curr = head; curr; curr = curr->next) {
66 size_t len = strlen(curr->name);
67 if (strncmp(name, curr->name, len) == 0) {
68 char c = name[len];
69- if ((c >= '0' && c <= '9') || (c == 'p' && name[len+1] >= '0' && name[len+1] <= '9')) {
70- is_part = 1;
71+ if (curr->name[len - 1] >= '0' && curr->name[len - 1] <= '9') {
72+ if (c == 'p' && name[len + 1] >= '0' && name[len + 1] <= '9')
73+ is_part = 1;
74+ } else {
75+ if (c >= '0' && c <= '9')
76+ is_part = 1;
77+ }
78+ if (is_part) {
79 strlcpy(info->type, "part", sizeof(info->type));
80 struct BlockDevInfo *p = curr->parts;
81 if (!p) {
82@@ -181,16 +194,14 @@ blockdev_get_list(void)
83 }
84 fclose(fp);
85
86- /* parse mounts to populate mountpoint and fstype */
87- FILE *mfile = fopen("/proc/mounts", "r");
88+ mfile = fopen(PATH_MOUNTS, "r");
89 if (mfile) {
90- char mline[512];
91- char devpath[128], mnt[256], fs[64];
92 while (fgets(mline, sizeof(mline), mfile)) {
93 if (sscanf(mline, "%127s %255s %63s", devpath, mnt, fs) == 3) {
94 char *devname = devpath;
95- if (strncmp(devpath, "/dev/", 5) == 0)
96- devname = devpath + 5;
97+ size_t dev_len = strlen(PATH_DEV);
98+ if (strncmp(devpath, PATH_DEV, dev_len) == 0 && devpath[dev_len] == '/')
99+ devname = devpath + dev_len + 1;
100 struct BlockDevInfo *curr;
101 for (curr = head; curr; curr = curr->next) {
102 if (strcmp(curr->name, devname) == 0) {
103@@ -214,57 +225,67 @@ blockdev_get_list(void)
104
105 #elif defined(__FreeBSD__)
106 char disks[1024];
107- size_t len = sizeof(disks);
108- struct BlockDevInfo *head = NULL, *tail = NULL;
109+ char devpath[128];
110+ char *tok;
111+ char *ptr;
112+ struct BlockDevInfo *head = NULL;
113+ struct BlockDevInfo *tail = NULL;
114+ struct BlockDevInfo *info;
115+ struct BlockDevInfo *part;
116+ struct BlockDevInfo *p;
117+ struct BlockDev dev;
118+ DIR *dir;
119+ struct dirent *de;
120+ size_t disk_len;
121+ size_t len;
122+ struct statfs *mntbuf;
123+ int mntsize;
124+ int i;
125
126+ len = sizeof(disks);
127 if (sysctlbyname("kern.disks", disks, &len, NULL, 0) < 0)
128 return NULL;
129
130- char *tok, *ptr = disks;
131+ ptr = disks;
132 while ((tok = strsep(&ptr, " \t")) != NULL) {
133 if (!*tok)
134 continue;
135
136- struct BlockDevInfo *info = calloc(1, sizeof(*info));
137- if (!info)
138- break;
139+ info = ecalloc(1, sizeof(*info));
140
141 strlcpy(info->name, tok, sizeof(info->name));
142+ snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, tok);
143 strlcpy(info->type, "disk", sizeof(info->type));
144
145- char devpath[64];
146- snprintf(devpath, sizeof(devpath), "/dev/%s", tok);
147- struct BlockDev dev;
148+ snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, tok);
149 if (blockdev_open(&dev, devpath, 0) == 0) {
150 info->size = dev.size;
151 blockdev_close(&dev);
152 }
153
154- DIR *dir = opendir("/dev");
155+ dir = opendir(PATH_DEV);
156 if (dir) {
157- struct dirent *de;
158- size_t disk_len = strlen(tok);
159+ disk_len = strlen(tok);
160 while ((de = readdir(dir)) != NULL) {
161 if (strncmp(de->d_name, tok, disk_len) == 0) {
162 char c = de->d_name[disk_len];
163 if (c == 's' || c == 'p') {
164- struct BlockDevInfo *part = calloc(1, sizeof(*part));
165- if (part) {
166- strlcpy(part->name, de->d_name, sizeof(part->name));
167- strlcpy(part->type, "part", sizeof(part->type));
168- snprintf(devpath, sizeof(devpath), "/dev/%s", de->d_name);
169- if (blockdev_open(&dev, devpath, 0) == 0) {
170- part->size = dev.size;
171- blockdev_close(&dev);
172- }
173- struct BlockDevInfo *p = info->parts;
174- if (!p) {
175- info->parts = part;
176- } else {
177- while (p->next)
178- p = p->next;
179- p->next = part;
180- }
181+ part = ecalloc(1, sizeof(*part));
182+ strlcpy(part->name, de->d_name, sizeof(part->name));
183+ snprintf(part->path, sizeof(part->path), "%s/%s", PATH_DEV, de->d_name);
184+ strlcpy(part->type, "part", sizeof(part->type));
185+ snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, de->d_name);
186+ if (blockdev_open(&dev, devpath, 0) == 0) {
187+ part->size = dev.size;
188+ blockdev_close(&dev);
189+ }
190+ p = info->parts;
191+ if (!p) {
192+ info->parts = part;
193+ } else {
194+ while (p->next)
195+ p = p->next;
196+ p->next = part;
197 }
198 }
199 }
200@@ -280,13 +301,13 @@ blockdev_get_list(void)
201 tail = info;
202 }
203
204- struct statfs *mntbuf;
205- int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
206- for (int i = 0; i < mntsize; i++) {
207- char *devpath = mntbuf[i].f_mntfromname;
208- char *devname = devpath;
209- if (strncmp(devpath, "/dev/", 5) == 0)
210- devname = devpath + 5;
211+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
212+ for (i = 0; i < mntsize; i++) {
213+ char *frompath = mntbuf[i].f_mntfromname;
214+ char *devname = frompath;
215+ size_t dev_len = strlen(PATH_DEV);
216+ if (strncmp(frompath, PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
217+ devname = frompath + dev_len + 1;
218 struct BlockDevInfo *curr;
219 for (curr = head; curr; curr = curr->next) {
220 if (strcmp(curr->name, devname) == 0) {
221@@ -307,54 +328,66 @@ blockdev_get_list(void)
222
223 #elif defined(__OpenBSD__)
224 char disknames[1024];
225- size_t len = sizeof(disknames);
226- int mib[2] = { CTL_HW, HW_DISKNAMES };
227- struct BlockDevInfo *head = NULL, *tail = NULL;
228+ char devpath[128];
229+ char *tok;
230+ char *ptr;
231+ char *colon;
232+ struct BlockDevInfo *head = NULL;
233+ struct BlockDevInfo *tail = NULL;
234+ struct BlockDevInfo *info;
235+ struct BlockDevInfo *part;
236+ struct BlockDevInfo *p;
237+ struct BlockDev dev;
238+ size_t len;
239+ int mib[2];
240+ char c;
241+ struct statfs *mntbuf;
242+ int mntsize;
243+ int i;
244
245+ mib[0] = CTL_HW;
246+ mib[1] = HW_DISKNAMES;
247+ len = sizeof(disknames);
248 if (sysctl(mib, 2, disknames, &len, NULL, 0) < 0)
249 return NULL;
250
251- char *tok, *ptr = disknames;
252+ ptr = disknames;
253 while ((tok = strsep(&ptr, ",")) != NULL) {
254- char *colon = strchr(tok, ':');
255+ colon = strchr(tok, ':');
256 if (colon)
257 *colon = '\0';
258 if (!*tok)
259 continue;
260
261- struct BlockDevInfo *info = calloc(1, sizeof(*info));
262- if (!info)
263- break;
264+ info = ecalloc(1, sizeof(*info));
265
266 strlcpy(info->name, tok, sizeof(info->name));
267+ snprintf(info->path, sizeof(info->path), "%s/r%sc", PATH_DEV, tok);
268 strlcpy(info->type, "disk", sizeof(info->type));
269
270- char devpath[64];
271- snprintf(devpath, sizeof(devpath), "/dev/r%sc", tok);
272- struct BlockDev dev;
273+ snprintf(devpath, sizeof(devpath), "%s/r%sc", PATH_DEV, tok);
274 if (blockdev_open(&dev, devpath, 0) == 0) {
275 info->size = dev.size;
276 blockdev_close(&dev);
277 }
278
279- for (char c = 'a'; c <= 'p'; c++) {
280+ for (c = 'a'; c <= 'p'; c++) {
281 if (c == 'c')
282 continue;
283- snprintf(devpath, sizeof(devpath), "/dev/r%s%c", tok, c);
284+ snprintf(devpath, sizeof(devpath), "%s/r%s%c", PATH_DEV, tok, c);
285 if (blockdev_open(&dev, devpath, 0) == 0) {
286- struct BlockDevInfo *part = calloc(1, sizeof(*part));
287- if (part) {
288- snprintf(part->name, sizeof(part->name), "%s%c", tok, c);
289- strlcpy(part->type, "part", sizeof(part->type));
290- part->size = dev.size;
291- struct BlockDevInfo *p = info->parts;
292- if (!p) {
293- info->parts = part;
294- } else {
295- while (p->next)
296- p = p->next;
297- p->next = part;
298- }
299+ part = ecalloc(1, sizeof(*part));
300+ snprintf(part->name, sizeof(part->name), "%s%c", tok, c);
301+ snprintf(part->path, sizeof(part->path), "%s/r%s%c", PATH_DEV, tok, c);
302+ strlcpy(part->type, "part", sizeof(part->type));
303+ part->size = dev.size;
304+ p = info->parts;
305+ if (!p) {
306+ info->parts = part;
307+ } else {
308+ while (p->next)
309+ p = p->next;
310+ p->next = part;
311 }
312 blockdev_close(&dev);
313 }
314@@ -368,13 +401,13 @@ blockdev_get_list(void)
315 tail = info;
316 }
317
318- struct statfs *mntbuf;
319- int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
320- for (int i = 0; i < mntsize; i++) {
321- char *devpath = mntbuf[i].f_mntfromname;
322- char *devname = devpath;
323- if (strncmp(devpath, "/dev/", 5) == 0)
324- devname = devpath + 5;
325+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
326+ for (i = 0; i < mntsize; i++) {
327+ char *frompath = mntbuf[i].f_mntfromname;
328+ char *devname = frompath;
329+ size_t dev_len = strlen(PATH_DEV);
330+ if (strncmp(frompath, PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
331+ devname = frompath + dev_len + 1;
332 struct BlockDevInfo *curr;
333 for (curr = head; curr; curr = curr->next) {
334 if (strcmp(curr->name, devname) == 0) {
335@@ -394,26 +427,29 @@ blockdev_get_list(void)
336 return head;
337
338 #else
339- struct BlockDevInfo *head = NULL, *tail = NULL;
340+ struct BlockDevInfo *head = NULL;
341+ struct BlockDevInfo *tail = NULL;
342+ struct BlockDevInfo *info;
343+ struct BlockDev dev;
344 char devname[16];
345- for (char c = 'a'; c <= 'z'; c++) {
346+ char devpath[128];
347+ char c;
348+
349+ for (c = 'a'; c <= 'z'; c++) {
350 snprintf(devname, sizeof(devname), "sd%c", c);
351- char devpath[32];
352- snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
353- struct BlockDev dev;
354+ snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, devname);
355 if (blockdev_open(&dev, devpath, 0) == 0) {
356- struct BlockDevInfo *info = calloc(1, sizeof(*info));
357- if (info) {
358- strlcpy(info->name, devname, sizeof(info->name));
359- strlcpy(info->type, "disk", sizeof(info->type));
360- info->size = dev.size;
361- if (!head) {
362- head = info;
363- } else {
364- tail->next = info;
365- }
366- tail = info;
367+ info = ecalloc(1, sizeof(*info));
368+ strlcpy(info->name, devname, sizeof(info->name));
369+ snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, devname);
370+ strlcpy(info->type, "disk", sizeof(info->type));
371+ info->size = dev.size;
372+ if (!head) {
373+ head = info;
374+ } else {
375+ tail->next = info;
376 }
377+ tail = info;
378 blockdev_close(&dev);
379 }
380 }
381@@ -437,3 +473,269 @@ blockdev_free_list(struct BlockDevInfo *list)
382 curr = next;
383 }
384 }
385+
386+struct FsType {
387+ const char *name;
388+ size_t magic_offset;
389+ size_t magic_len;
390+ uint64_t magic1;
391+ uint64_t magic2;
392+ size_t uuid_offset;
393+ size_t uuid_len;
394+ size_t label_offset;
395+ size_t label_len;
396+};
397+
398+static const struct FsType fstypes[] = {
399+ /* ext2/3/4 */
400+ { "ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16 },
401+ /* vfat / fat32 */
402+ { "vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11 },
403+ /* vfat / fat12/16 */
404+ { "vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11 },
405+ /* ntfs */
406+ { "ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0 },
407+ /* xfs */
408+ { "xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12 },
409+ /* btrfs */
410+ { "btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256 },
411+ /* f2fs */
412+ { "f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512 },
413+ /* ufs1 */
414+ { "ufs1", 9564, 4, 0x011954, 0x54190100, 8428, 8, 0, 0 },
415+ /* ufs2 */
416+ { "ufs2", 66908, 4, 0x19540119, 0x19015419, 65772, 8, 66216, 32 },
417+ /* fossil */
418+ { "fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127 },
419+};
420+
421+static void
422+format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
423+{
424+ size_t i;
425+ char *p;
426+
427+ p = out;
428+ if (strcmp(type, "vfat") == 0) {
429+ snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
430+ return;
431+ }
432+
433+ if (strcmp(type, "ntfs") == 0) {
434+ for (i = 8; i > 0; i--) {
435+ snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
436+ p += 2;
437+ }
438+ return;
439+ }
440+
441+ for (i = 0; i < len; i++) {
442+ if (i == 4 || i == 6 || i == 8 || i == 10) {
443+ *p++ = '-';
444+ }
445+ snprintf(p, out_len - (p - out), "%02x", buf[i]);
446+ p += 2;
447+ }
448+ *p = '\0';
449+}
450+
451+static int
452+zfs_detect(const unsigned char *buf, size_t len)
453+{
454+ size_t offset;
455+ uint64_t magic;
456+
457+ for (offset = 131072; offset + 8 <= len; offset += 1024) {
458+ magic = ((uint64_t)buf[offset + 7] << 56) |
459+ ((uint64_t)buf[offset + 6] << 48) |
460+ ((uint64_t)buf[offset + 5] << 40) |
461+ ((uint64_t)buf[offset + 4] << 32) |
462+ ((uint64_t)buf[offset + 3] << 24) |
463+ ((uint64_t)buf[offset + 2] << 16) |
464+ ((uint64_t)buf[offset + 1] << 8) |
465+ (uint64_t)buf[offset];
466+
467+ if (magic == 0x00bab10c00000000ULL || magic == 0x0cb1ba0000000000ULL ||
468+ magic == 0x00bab10cULL || magic == 0x0cb1ba00ULL)
469+ return 1;
470+ }
471+ return 0;
472+}
473+
474+struct Plan9FsDef {
475+ const char *name;
476+ int blocksize;
477+ int namelen;
478+ int dirblks;
479+ int indirblks;
480+ int daddrbits;
481+};
482+
483+static const struct Plan9FsDef p9_fs_defs[] = {
484+ { "cwfs64x", 16384, 144, 6, 4, 64 },
485+ { "cwfs64", 16384, 56, 6, 4, 64 },
486+ { "cwfs", 16384, 28, 6, 2, 32 },
487+ { "fs64", 8192, 56, 6, 4, 64 },
488+ { "fs", 4096, 28, 6, 2, 32 },
489+};
490+
491+static int
492+plan9_magic_check(const unsigned char *p, const char **name)
493+{
494+ if (memcmp(p, "\x01\x1c\xe5\x0d", 4) == 0 || memcmp(p, "\x0d\xe5\x1c\x01", 4) == 0) {
495+ *name = "hjfs";
496+ return 1;
497+ }
498+ if (memcmp(p, "gefs", 4) == 0 || memcmp(p, "sfeg", 4) == 0) {
499+ *name = "gefs";
500+ return 1;
501+ }
502+ return 0;
503+}
504+
505+static int
506+plan9_detect_other(const unsigned char *buf, size_t len, const char **name)
507+{
508+ if (len >= 4 && plan9_magic_check(buf, name))
509+ return 1;
510+ if (len >= 516 && plan9_magic_check(buf + 512, name))
511+ return 1;
512+ return 0;
513+}
514+
515+static int
516+plan9_detect_text(const unsigned char *buf, size_t len, const char **name)
517+{
518+ int blocksize = 0, namelen = 0, dirblks = 0, indirblks = 0, daddrbits = 0;
519+ char line[128];
520+ char key[64];
521+ int val;
522+ size_t i = 0, j;
523+
524+ if (len >= 256 + 15) {
525+ if (memcmp(buf + 256, "kfs wren device", 15) == 0) {
526+ *name = "kfs";
527+ return 1;
528+ }
529+ }
530+
531+ if (len < 512)
532+ return 0;
533+ i = 512;
534+ if (len > 1536)
535+ len = 1536;
536+
537+ while (i < len) {
538+ size_t k = 0;
539+ while (i < len && buf[i] != '\n' && k < sizeof(line) - 1) {
540+ line[k++] = buf[i++];
541+ }
542+ if (i < len && buf[i] == '\n')
543+ i++;
544+ line[k] = '\0';
545+
546+ if (sscanf(line, "%63s %d", key, &val) == 2) {
547+ if (strcmp(key, "blocksize") == 0) blocksize = val;
548+ else if (strcmp(key, "namelen") == 0) namelen = val;
549+ else if (strcmp(key, "dirblks") == 0) dirblks = val;
550+ else if (strcmp(key, "indirblks") == 0) indirblks = val;
551+ else if (strcmp(key, "daddrbits") == 0) daddrbits = val;
552+ }
553+ }
554+
555+ for (j = 0; j < LEN(p9_fs_defs); j++) {
556+ if (blocksize == p9_fs_defs[j].blocksize &&
557+ namelen == p9_fs_defs[j].namelen &&
558+ dirblks == p9_fs_defs[j].dirblks &&
559+ indirblks == p9_fs_defs[j].indirblks &&
560+ daddrbits == p9_fs_defs[j].daddrbits) {
561+ *name = p9_fs_defs[j].name;
562+ return 1;
563+ }
564+ }
565+
566+ return 0;
567+}
568+
569+int
570+blockdev_detect_fs(struct BlockDev *dev, char *type, size_t type_sz,
571+ char *label, size_t label_sz, char *uuid, size_t uuid_sz)
572+{
573+ unsigned char *buf;
574+ size_t read_size = 262144; /* 256kb */
575+ size_t sectors_to_read;
576+ size_t i, j;
577+ int found = 0;
578+
579+ if (dev->size < read_size)
580+ read_size = dev->size;
581+
582+ buf = emalloc(read_size);
583+ sectors_to_read = (read_size + dev->sec_size - 1) / dev->sec_size;
584+ if (blockdev_read(dev, 0, buf, sectors_to_read) < 0) {
585+ free(buf);
586+ return -2;
587+ }
588+
589+ for (i = 0; i < LEN(fstypes); i++) {
590+ const struct FsType *fs = &fstypes[i];
591+ if (fs->magic_offset + fs->magic_len > read_size)
592+ continue;
593+
594+ uint64_t val = 0;
595+ for (j = 0; j < fs->magic_len; j++) {
596+ val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
597+ }
598+
599+ if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
600+ const char *type_name = fs->name;
601+ if (strcmp(fs->name, "ext") == 0) {
602+ if (buf[1116] & 4)
603+ type_name = "ext3";
604+ else if (buf[1120] & 64)
605+ type_name = "ext4";
606+ else
607+ type_name = "ext2";
608+ }
609+
610+ if (type && type_sz)
611+ strlcpy(type, type_name, type_sz);
612+
613+ if (label && label_sz && fs->label_len && fs->label_offset + fs->label_len <= read_size) {
614+ snprintf(label, label_sz, "%.*s", (int)fs->label_len, buf + fs->label_offset);
615+ /* strip trailing spaces */
616+ char *end = label + strlen(label) - 1;
617+ while (end >= label && *end == ' ') {
618+ *end = '\0';
619+ end--;
620+ }
621+ }
622+
623+ if (uuid && uuid_sz && fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
624+ format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, uuid_sz);
625+ }
626+
627+ found = 1;
628+ break;
629+ }
630+ }
631+
632+ if (!found && zfs_detect(buf, read_size)) {
633+ if (type && type_sz)
634+ strlcpy(type, "zfs_member", type_sz);
635+ found = 1;
636+ }
637+
638+ if (!found) {
639+ const char *p9_name = NULL;
640+ if (plan9_detect_other(buf, read_size, &p9_name) ||
641+ plan9_detect_text(buf, read_size, &p9_name)) {
642+ if (type && type_sz)
643+ strlcpy(type, p9_name, type_sz);
644+ found = 1;
645+ }
646+ }
647+
648+ free(buf);
649+ return found ? 0 : -1;
650+}