mirror of
https://https.git.savannah.gnu.org/git/diffutils.git
synced 2026-01-28 02:14:40 +00:00
Compare commits
447 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbc1bdb771 | ||
|
|
301eca636b | ||
|
|
33ce4260b8 | ||
|
|
6ad4a619d4 | ||
|
|
916b50fe96 | ||
|
|
a1432a6216 | ||
|
|
a709a0a2bd | ||
|
|
b4524ba9a6 | ||
|
|
6b9c726076 | ||
|
|
3ccfcd8cd7 | ||
|
|
096a3b29b5 | ||
|
|
dc6dc9147f | ||
|
|
cf5648869a | ||
|
|
e124541148 | ||
|
|
42083759b6 | ||
|
|
c08704493d | ||
|
|
f3f02235de | ||
|
|
0cba18b965 | ||
|
|
2591992037 | ||
|
|
c191460adf | ||
|
|
686357a40c | ||
|
|
49760e9f50 | ||
|
|
33ee12e587 | ||
|
|
45a4762bf3 | ||
|
|
80053ab7b5 | ||
|
|
d05765a8f1 | ||
|
|
16681a3cbc | ||
|
|
6c92d5fb8f | ||
|
|
7e53977313 | ||
|
|
53ba74998b | ||
|
|
f180b5f4d4 | ||
|
|
362a759cfb | ||
|
|
e9f8e6a439 | ||
|
|
58e734dedd | ||
|
|
706116c651 | ||
|
|
6395d51a01 | ||
|
|
6ce0ebd033 | ||
|
|
82cbd61848 | ||
|
|
41034e8398 | ||
|
|
3f326ae3ea | ||
|
|
cdc6555e2c | ||
|
|
03e379ecc8 | ||
|
|
7a32628d4d | ||
|
|
ea7c073d2d | ||
|
|
cdc6f1490f | ||
|
|
142a264d9d | ||
|
|
dfaa846f09 | ||
|
|
733e2f6d7f | ||
|
|
088ba9f064 | ||
|
|
d65b41664a | ||
|
|
e1bb047793 | ||
|
|
4eb92b2a80 | ||
|
|
f81b0a7c1c | ||
|
|
d22d1f67ca | ||
|
|
7f9bf95640 | ||
|
|
5ec3ebf31c | ||
|
|
e7387b4ce2 | ||
|
|
4a6bc03a99 | ||
|
|
c31ff07758 | ||
|
|
1d2456f539 | ||
|
|
8da1f14d84 | ||
|
|
da0c15f381 | ||
|
|
54c8930ca5 | ||
|
|
d636f9c117 | ||
|
|
d7588ed774 | ||
|
|
8f8ee60502 | ||
|
|
d82aac15e6 | ||
|
|
a9cca43461 | ||
|
|
b8b3ac5c20 | ||
|
|
ba8e55270f | ||
|
|
8f1af93295 | ||
|
|
bf85e93a52 | ||
|
|
12dc6b3e6f | ||
|
|
62d075ad72 | ||
|
|
195016c60f | ||
|
|
cceca01aba | ||
|
|
a33c9b9e85 | ||
|
|
d6fc5a8ce1 | ||
|
|
561caf873f | ||
|
|
c9a237907b | ||
|
|
3d1a56b906 | ||
|
|
6c88db1f7b | ||
|
|
b477ddb1b8 | ||
|
|
f1a8c476b6 | ||
|
|
774b942f70 | ||
|
|
8bd0414495 | ||
|
|
89b59a4337 | ||
|
|
632deed390 | ||
|
|
5fdc79cc07 | ||
|
|
f2d94e2e67 | ||
|
|
c1e838aeff | ||
|
|
b82644eb22 | ||
|
|
a8c3040e6a | ||
|
|
2b82edcdf8 | ||
|
|
7c5ade561b | ||
|
|
8ef3eb9d98 | ||
|
|
924f297e3e | ||
|
|
30c5e9e592 | ||
|
|
7ec1b5db35 | ||
|
|
86d52a9a3e | ||
|
|
38da0e3813 | ||
|
|
1cce5a2bae | ||
|
|
77b21655e6 | ||
|
|
d58d584d3c | ||
|
|
5d73f793bd | ||
|
|
8a9765d3f7 | ||
|
|
ae1cdc7239 | ||
|
|
574e81bff2 | ||
|
|
61e01d05e3 | ||
|
|
1a24e490c6 | ||
|
|
14a167f8d4 | ||
|
|
12bcf0bd50 | ||
|
|
e016d12581 | ||
|
|
e1bd4d4094 | ||
|
|
bd03fa9e7b | ||
|
|
065a4648e2 | ||
|
|
77ac458934 | ||
|
|
5d30da7a5b | ||
|
|
1fa953a0c4 | ||
|
|
c1f6f48a08 | ||
|
|
69ae797392 | ||
|
|
23d506ac59 | ||
|
|
de0dbfc9b9 | ||
|
|
daaf819374 | ||
|
|
b684567824 | ||
|
|
6f357cfa9f | ||
|
|
b90d1aba74 | ||
|
|
71fd20c222 | ||
|
|
e85572e5d1 | ||
|
|
7bf02ff2cc | ||
|
|
b0215c89b8 | ||
|
|
8efc1a0ec7 | ||
|
|
634a0bd2ba | ||
|
|
c3d652956b | ||
|
|
ce65fcbeec | ||
|
|
1cb42b05d4 | ||
|
|
0c37ed8acb | ||
|
|
6984795f49 | ||
|
|
1963fdf558 | ||
|
|
c0bbbc2715 | ||
|
|
7953719a3e | ||
|
|
615502de3f | ||
|
|
05b21c3723 | ||
|
|
f7e94f96f1 | ||
|
|
72160d1428 | ||
|
|
b69d7d1661 | ||
|
|
15242bb897 | ||
|
|
5f3d9a4a32 | ||
|
|
65b6e08d1f | ||
|
|
25594ef0d9 | ||
|
|
8fa0e68226 | ||
|
|
5c74ccefc5 | ||
|
|
d0e9d67586 | ||
|
|
a30a8d4f4d | ||
|
|
8d476d03b5 | ||
|
|
6477dce501 | ||
|
|
00badee340 | ||
|
|
9c98864cb1 | ||
|
|
13799c56ed | ||
|
|
a1455251a9 | ||
|
|
70fd6c57ad | ||
|
|
c640bec4f3 | ||
|
|
753302982b | ||
|
|
6bf2c33ea4 | ||
|
|
1bb156e927 | ||
|
|
5aa5a1cc9b | ||
|
|
856f72409b | ||
|
|
e44fb6b802 | ||
|
|
d42a8f7587 | ||
|
|
42e4d008a6 | ||
|
|
0a72d5ab28 | ||
|
|
2e2731f520 | ||
|
|
4c83bb78fe | ||
|
|
fcb759b21a | ||
|
|
df12b2c41c | ||
|
|
3227528668 | ||
|
|
d95e016e37 | ||
|
|
5d0554f0a1 | ||
|
|
d28395ef8d | ||
|
|
4c722ca85e | ||
|
|
c15acfe2c5 | ||
|
|
e9a55a8edc | ||
|
|
2d48e4b13d | ||
|
|
ef7093b760 | ||
|
|
6f522c51ed | ||
|
|
a542ab269a | ||
|
|
2055c9cffd | ||
|
|
b79de8748b | ||
|
|
2c3f8a85aa | ||
|
|
572249e0fa | ||
|
|
6e091776f8 | ||
|
|
43b7a667e5 | ||
|
|
93a5a16852 | ||
|
|
8474b6e088 | ||
|
|
642e2397f2 | ||
|
|
1f5d520fa1 | ||
|
|
ffafa15eec | ||
|
|
05cdf3102e | ||
|
|
7aa48caa06 | ||
|
|
359b8c3ef2 | ||
|
|
bdc8608705 | ||
|
|
d4a63b11fc | ||
|
|
9eece02408 | ||
|
|
ef5e8e03a8 | ||
|
|
8ee5d34415 | ||
|
|
c780b2f4c6 | ||
|
|
c563537bad | ||
|
|
47cd159b0f | ||
|
|
9c5fcbdc29 | ||
|
|
b7d8245828 | ||
|
|
fbc7fc8074 | ||
|
|
b72079f2f9 | ||
|
|
84d4d49c6b | ||
|
|
2f053f15c0 | ||
|
|
ae55e99195 | ||
|
|
93c20ba3c0 | ||
|
|
4ee8300b46 | ||
|
|
584986ad18 | ||
|
|
69ea279bbb | ||
|
|
ff1096a0c2 | ||
|
|
77722b5c59 | ||
|
|
d45cee5d38 | ||
|
|
5ce9ac138c | ||
|
|
0d2ea921b3 | ||
|
|
608a613abd | ||
|
|
29f1874aea | ||
|
|
ec4a3c07ba | ||
|
|
f7432876c5 | ||
|
|
a2e301b52c | ||
|
|
c89fd071d5 | ||
|
|
418240fccc | ||
|
|
4c8554b702 | ||
|
|
9fdc4083f8 | ||
|
|
fddb3dbab1 | ||
|
|
ced621dd21 | ||
|
|
73b5c504f6 | ||
|
|
e5e4200fce | ||
|
|
a1a4c58d3d | ||
|
|
6341b346cd | ||
|
|
e8a7dab95c | ||
|
|
c41d3d921e | ||
|
|
c457027a98 | ||
|
|
8c13859cce | ||
|
|
74bc02c996 | ||
|
|
741a5659a0 | ||
|
|
fa802347c7 | ||
|
|
b56727716d | ||
|
|
c2e38d4b22 | ||
|
|
cb018ecff2 | ||
|
|
8587626636 | ||
|
|
71430dd736 | ||
|
|
61c1fffe7b | ||
|
|
fc62345ebc | ||
|
|
f54e901c32 | ||
|
|
82298afa03 | ||
|
|
b733c02072 | ||
|
|
a693d81b03 | ||
|
|
15fda47d8f | ||
|
|
225f1db8e1 | ||
|
|
6f879e4f12 | ||
|
|
a7eb06a39d | ||
|
|
48c1b55813 | ||
|
|
3357879cba | ||
|
|
e752ad65be | ||
|
|
e9e15097ff | ||
|
|
7d69341d91 | ||
|
|
1e20578506 | ||
|
|
917e4d34e0 | ||
|
|
1b9883fe56 | ||
|
|
2a354e0699 | ||
|
|
9c3bc5255f | ||
|
|
6ee3cf3e0b | ||
|
|
8918d10b0d | ||
|
|
9eae15709a | ||
|
|
b65d1622e2 | ||
|
|
5b6b5d9361 | ||
|
|
835063ee2a | ||
|
|
6ae4f12df0 | ||
|
|
7f7054dd5e | ||
|
|
15f09cb45e | ||
|
|
e8c2d02643 | ||
|
|
2bb977b7d9 | ||
|
|
da1697dcb6 | ||
|
|
35b74c5708 | ||
|
|
56dd675c35 | ||
|
|
d136413d1c | ||
|
|
9df7292724 | ||
|
|
efec6cce91 | ||
|
|
844c45fd72 | ||
|
|
f265160c26 | ||
|
|
12747f3138 | ||
|
|
b1a657b85a | ||
|
|
7abd180200 | ||
|
|
b079f08cc9 | ||
|
|
55260678d9 | ||
|
|
11a0695113 | ||
|
|
11238b27ed | ||
|
|
d3e3696ec7 | ||
|
|
9f7c4148b3 | ||
|
|
36244332b8 | ||
|
|
7d154dae52 | ||
|
|
d9c367b164 | ||
|
|
09f7779275 | ||
|
|
d08e0b47fc | ||
|
|
c427f9d4b4 | ||
|
|
cc36781560 | ||
|
|
877c120765 | ||
|
|
13a05d1e95 | ||
|
|
3fa7218567 | ||
|
|
6351be27c3 | ||
|
|
ba08fbbb0c | ||
|
|
7e7ef707d6 | ||
|
|
7e7a21b05b | ||
|
|
09f8e2b0a9 | ||
|
|
75a38113a7 | ||
|
|
0e9cb995ee | ||
|
|
3379bf4ef4 | ||
|
|
8acc04a7b5 | ||
|
|
27a9b27d00 | ||
|
|
86543b9630 | ||
|
|
89f3daeaaf | ||
|
|
aa13187f2a | ||
|
|
89b587f03f | ||
|
|
555a3d7d08 | ||
|
|
cc41598038 | ||
|
|
f5f2a237b2 | ||
|
|
bc732310db | ||
|
|
47a1f46f08 | ||
|
|
28e4de2c64 | ||
|
|
7379990845 | ||
|
|
2d50351377 | ||
|
|
eb3085a076 | ||
|
|
4e9063a17b | ||
|
|
7325c07a64 | ||
|
|
dd9deb7655 | ||
|
|
231f879462 | ||
|
|
25904a12fe | ||
|
|
438d6e1cf8 | ||
|
|
aec523d3a4 | ||
|
|
dabf2c747d | ||
|
|
5a4339a609 | ||
|
|
76f518677b | ||
|
|
3e83dac7ad | ||
|
|
227dd38d9f | ||
|
|
074cbe590b | ||
|
|
08b103ff4d | ||
|
|
e1458a3137 | ||
|
|
f1de694cbf | ||
|
|
a5e8102129 | ||
|
|
d04b6c1e10 | ||
|
|
37ff1120ad | ||
|
|
078321ebeb | ||
|
|
9b20182d48 | ||
|
|
b05f7f54e4 | ||
|
|
57e5c514df | ||
|
|
dadc7f231f | ||
|
|
31226b9bbe | ||
|
|
f2e2b4d3c3 | ||
|
|
24bd30d6d9 | ||
|
|
d0721fe43d | ||
|
|
20ab8fc308 | ||
|
|
6bb47c10f6 | ||
|
|
044f369952 | ||
|
|
4befe0a802 | ||
|
|
a3b63ab67d | ||
|
|
7ec5eb6c97 | ||
|
|
fc7e47f18b | ||
|
|
b53aba93b8 | ||
|
|
c21eddbfea | ||
|
|
fcf4e8e3f4 | ||
|
|
db1c761198 | ||
|
|
a3188d9e98 | ||
|
|
3065d7421c | ||
|
|
42868488c7 | ||
|
|
498f88de4d | ||
|
|
8d941fc5ad | ||
|
|
cae1a3d257 | ||
|
|
a9191e5588 | ||
|
|
1aa7ccfca4 | ||
|
|
747551b880 | ||
|
|
caf365bc32 | ||
|
|
4c42ca30c6 | ||
|
|
cc1477078d | ||
|
|
4c92a42eb8 | ||
|
|
6feddb95b4 | ||
|
|
db2fa35597 | ||
|
|
1399b225eb | ||
|
|
b48581daa8 | ||
|
|
f88b033174 | ||
|
|
22fb055cca | ||
|
|
9181dae4bf | ||
|
|
7c13bbbeca | ||
|
|
9c1447f661 | ||
|
|
2280c5b4a5 | ||
|
|
91eebc4b86 | ||
|
|
351f87d0c4 | ||
|
|
db5ff0eb0a | ||
|
|
a7808608fc | ||
|
|
f76d6a01dc | ||
|
|
b4d66bbc06 | ||
|
|
98f55d7bf2 | ||
|
|
3e0ece09ae | ||
|
|
ee869b17b1 | ||
|
|
1a9db80899 | ||
|
|
8d26b1403e | ||
|
|
c12acba737 | ||
|
|
c65f4d64b1 | ||
|
|
592e123468 | ||
|
|
1d19852975 | ||
|
|
3600e8d4f0 | ||
|
|
0c8f1ff314 | ||
|
|
db6d5f7240 | ||
|
|
86ece0ee21 | ||
|
|
badccffea4 | ||
|
|
601eceb57c | ||
|
|
2c9d956aac | ||
|
|
1104d02651 | ||
|
|
b09adbffd5 | ||
|
|
197d7d492f | ||
|
|
70d787ef44 | ||
|
|
2de623e7a1 | ||
|
|
04c64737c5 | ||
|
|
aaf0efa70d | ||
|
|
b9e2f4f3b3 | ||
|
|
b33a919934 | ||
|
|
c7a4eacac9 | ||
|
|
a28327bd77 | ||
|
|
f878003953 | ||
|
|
15668f2c54 | ||
|
|
b18c0155f5 | ||
|
|
d5bab3afd8 | ||
|
|
93a1e963e1 | ||
|
|
1505982238 | ||
|
|
6cd60762b2 | ||
|
|
ffd63aedad | ||
|
|
612db4defa | ||
|
|
576645cdf7 | ||
|
|
56225ecca4 | ||
|
|
9b87043d8d | ||
|
|
5dc851ad24 | ||
|
|
915003de19 | ||
|
|
4a38f715f1 | ||
|
|
81eea1db38 | ||
|
|
b50ab11f23 | ||
|
|
7ba026cae3 | ||
|
|
487e009ed7 | ||
|
|
592e503b48 |
148
.gitignore
vendored
148
.gitignore
vendored
@ -1,94 +1,110 @@
|
||||
*.a
|
||||
*.exe
|
||||
*.o
|
||||
*.orig
|
||||
*.patch
|
||||
*.rej
|
||||
*~
|
||||
.deps
|
||||
.Tpo
|
||||
.deps/
|
||||
.dirstamp
|
||||
.gdb-history
|
||||
/*.diff
|
||||
/ABOUT-NLS
|
||||
/GNUmakefile
|
||||
/INSTALL
|
||||
/README-release
|
||||
/aclocal.m4
|
||||
/build-aux
|
||||
/autom4te.cache/
|
||||
/build-aux/
|
||||
/confdefs*
|
||||
/config.cache
|
||||
/config.log
|
||||
/config.status
|
||||
/configure
|
||||
/conftest*
|
||||
/diffutils-*.tar.xz
|
||||
/doc/.gitignore
|
||||
/doc/diffutils.aux
|
||||
/doc/diffutils.cp
|
||||
/doc/diffutils.cps
|
||||
/doc/diffutils.info
|
||||
/doc/diffutils.log
|
||||
/doc/diffutils.toc
|
||||
/doc/stamp-vti
|
||||
/doc/version.texi
|
||||
/gnulib-tests
|
||||
/gnulib-tests/.gitignore
|
||||
/gnulib-tests/locale/
|
||||
/gnulib-tests/uniwidth/
|
||||
/lib/.gitignore
|
||||
/lib/alloca.h
|
||||
/lib/c++defs.h
|
||||
/lib/charset.alias
|
||||
/lib/config.h
|
||||
/lib/config.hin
|
||||
/lib/configmake.h
|
||||
/lib/fcntl.h
|
||||
/lib/iconv*.h
|
||||
/lib/inttypes.h
|
||||
/lib/langinfo.h
|
||||
/lib/locale.h
|
||||
/lib/ref-add.sed
|
||||
/lib/ref-del.sed
|
||||
/lib/signal.h
|
||||
/lib/stamp-h1
|
||||
/lib/stdio.h
|
||||
/lib/stdlib.h
|
||||
/lib/string.h
|
||||
/lib/strings.h
|
||||
/lib/sys/
|
||||
/lib/time.h
|
||||
/lib/unistd.h
|
||||
/lib/unistr
|
||||
/lib/unistr.h
|
||||
/lib/unitypes.h
|
||||
/lib/uniwidth
|
||||
/lib/uniwidth.h
|
||||
/lib/unused-parameter.h
|
||||
/lib/warn-on-use.h
|
||||
/lib/wchar.h
|
||||
/lib/wctype.h
|
||||
/m4/.gitignore
|
||||
/m4/gnulib-cache.m4
|
||||
/gnulib-tests/
|
||||
/m4/
|
||||
/maint.mk
|
||||
/po/*.pot
|
||||
/po/*.sed
|
||||
/po/.gitignore
|
||||
/po/.reference
|
||||
/po/LINGUAS
|
||||
/po/Makefile.in.in
|
||||
/po/Makevars
|
||||
/po/Makevars.template
|
||||
/po/POTFILES
|
||||
/po/Rules-quot
|
||||
/po/stamp-po
|
||||
ABOUT-NLS
|
||||
ChangeLog
|
||||
ID
|
||||
Makefile
|
||||
Makefile.in
|
||||
TAGS
|
||||
autom4te.cache
|
||||
build-aux
|
||||
gnulib-tests/*.log
|
||||
doc/.gitignore
|
||||
doc/diffutils.aux
|
||||
doc/diffutils.cp
|
||||
doc/diffutils.cps
|
||||
doc/diffutils.info
|
||||
doc/diffutils.log
|
||||
doc/diffutils.toc
|
||||
doc/stamp-vti
|
||||
doc/version.texi
|
||||
lib/*/
|
||||
lib/.gitignore
|
||||
lib/alloca.h
|
||||
lib/arg-nonnull.h
|
||||
lib/link-warning.h
|
||||
lib/c++defs.h
|
||||
lib/c-file-type.c
|
||||
lib/config.h
|
||||
lib/config.hin
|
||||
lib/ctype.h
|
||||
lib/dirent.h
|
||||
lib/errno.h
|
||||
lib/error.h
|
||||
lib/fcntl.h
|
||||
lib/file-type.h
|
||||
lib/fnmatch.h
|
||||
lib/getopt-cdefs.h
|
||||
lib/getopt.h
|
||||
lib/iconv*.h
|
||||
lib/inttypes.h
|
||||
lib/langinfo.h
|
||||
lib/limits.h
|
||||
lib/locale.h
|
||||
lib/signal.h
|
||||
lib/sigsegv.h
|
||||
lib/stamp-h1
|
||||
lib/stdarg.h
|
||||
lib/stdbit.h
|
||||
lib/stdckdint.h
|
||||
lib/stddef.h
|
||||
lib/stdint.h
|
||||
lib/stdio.h
|
||||
lib/stdlib.h
|
||||
# For some reason 'bootstrap' doesn't add /stdopen.[ch] to lib/.gitignore.
|
||||
lib/stdopen.[ch]
|
||||
lib/string.h
|
||||
lib/strings.h
|
||||
lib/time.h
|
||||
lib/uchar.h
|
||||
lib/unicase.h
|
||||
lib/unictype.h
|
||||
lib/uninorm.h
|
||||
lib/unistd.h
|
||||
lib/unistr.h
|
||||
lib/unistr/
|
||||
lib/unitypes.h
|
||||
lib/uniwidth.h
|
||||
lib/uniwidth/
|
||||
lib/warn-on-use.h
|
||||
lib/wchar.h
|
||||
lib/wctype.h
|
||||
man/*.1
|
||||
po/*.gmo
|
||||
po/*.po
|
||||
po/*.pot
|
||||
po/*.sed
|
||||
po/.gitignore
|
||||
po/.reference
|
||||
po/LINGUAS
|
||||
po/Makefile.in.in
|
||||
po/Makevars
|
||||
po/Makevars.template
|
||||
po/POTFILES
|
||||
po/Rules-quot
|
||||
po/stamp-po
|
||||
src/cmp
|
||||
src/diff
|
||||
src/diff3
|
||||
@ -97,3 +113,5 @@ src/sdiff
|
||||
src/version.[ch]
|
||||
tests/*.log
|
||||
tests/*.trs
|
||||
/tests/init.sh
|
||||
vc-dwim-log-*
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "gnulib"]
|
||||
path = gnulib
|
||||
url = git://git.sv.gnu.org/gnulib.git
|
||||
url = https://git.savannah.gnu.org/git/gnulib
|
||||
|
||||
@ -1 +1 @@
|
||||
3.5
|
||||
3.12
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@ -33,7 +33,7 @@ Patrick D'Cruze
|
||||
Eli Zaretskii
|
||||
|
||||
|
||||
Copyright (C) 2001, 2006, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
Copyright (C) 2001, 2006, 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU diffutils.
|
||||
|
||||
|
||||
@ -4265,7 +4265,7 @@ Thu Nov 3 16:30:24 1988 Randall Smith (randy at gluteus.ai.mit.edu)
|
||||
|
||||
-----
|
||||
|
||||
Copyright (C) 1988-1994, 1997-2002, 2004, 2006, 2009-2013, 2015-2017
|
||||
Copyright (C) 1988-1994, 1997-2002, 2004, 2006, 2009-2013, 2015-2026
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without
|
||||
|
||||
39
HACKING
39
HACKING
@ -3,14 +3,6 @@ Diffutils Contribution Guidelines
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
You will need the "git" version control tools.
|
||||
On Fedora-based systems, do "yum install git".
|
||||
On Debian-based ones install the "git-core" package.
|
||||
Then run "git --version". If that says it's older than
|
||||
version 1.4.4, then you'd do well to get a newer version.
|
||||
At worst, just download the latest stable release from
|
||||
http://git.or.cz/ and build from source.
|
||||
|
||||
For details on building the programs in this package, see
|
||||
the file, README-hacking.
|
||||
|
||||
@ -20,14 +12,11 @@ Use the latest upstream sources
|
||||
Base any changes you make on the latest upstream sources.
|
||||
You can get a copy of the latest with this command:
|
||||
|
||||
git clone git://git.sv.gnu.org/diffutils
|
||||
git clone https://git.savannah.gnu.org/git/diffutils
|
||||
|
||||
That downloads the entire repository, including revision control history
|
||||
dating back to 1991. The repository (the part you download, and which
|
||||
resides in diffutils/.git) currently weighs in at about 9MB. So you don't
|
||||
want to download it more often than necessary. Once downloaded, you
|
||||
can get incremental updates by running one of these commands from
|
||||
inside your new diffutils/ directory:
|
||||
That downloads the entire repository, including revision control history.
|
||||
Once downloaded, you can get incremental updates by running one of
|
||||
these commands from inside your new diffutils/ directory:
|
||||
|
||||
If you have made *no* changes:
|
||||
git pull
|
||||
@ -94,7 +83,7 @@ Make your changes on a private "topic" branch
|
||||
=============================================
|
||||
So you checked out diffutils like this:
|
||||
|
||||
git clone git://git.sv.gnu.org/diffutils
|
||||
git clone https://git.savannah.gnu.org/git/diffutils
|
||||
|
||||
Now, cd into the diffutils/ directory and run:
|
||||
|
||||
@ -221,7 +210,7 @@ keep the maximum line length at 72 or smaller, so that the generated
|
||||
ChangeLog lines, each with its leading TAB, will not exceed 80 columns.
|
||||
As for the ChangeLog-style content, please follow these guidelines:
|
||||
|
||||
http://www.gnu.org/software/guile/changelogs/guile-changelogs_3.html
|
||||
https://www.gnu.org/prep/standards/html_node/Change-Logs.html
|
||||
|
||||
Try to make the summary line fit one of the following forms:
|
||||
|
||||
@ -438,18 +427,18 @@ The forms to choose from are in gnulib's doc/Copyright/ directory.
|
||||
If you want to assign a single change, you should use the file,
|
||||
doc/Copyright/request-assign.changes:
|
||||
|
||||
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=doc/Copyright/request-assign.changes;hb=HEAD
|
||||
https://www.gnu.org/software/gnulib/Copyright/request-assign.changes
|
||||
|
||||
If you would like to assign past and future contributions to a project,
|
||||
you'd use doc/Copyright/request-assign.future:
|
||||
|
||||
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=doc/Copyright/request-assign.future;hb=HEAD
|
||||
https://www.gnu.org/software/gnulib/Copyright/request-assign.future
|
||||
|
||||
You may make assignments for up to four projects at a time.
|
||||
|
||||
In case you're wondering why we bother with all of this, read this:
|
||||
|
||||
http://www.gnu.org/licenses/why-assign.html
|
||||
https://www.gnu.org/licenses/why-assign.html
|
||||
|
||||
|
||||
Run "make syntax-check", or even "make distcheck"
|
||||
@ -483,13 +472,11 @@ Do not add any more trailing blanks anywhere. While "make syntax-check"
|
||||
will alert you if you slip up, it's better to nip any problem in the
|
||||
bud, as you're typing. A good way to help you adapt to this rule is
|
||||
to configure your editor to highlight any offending characters in the
|
||||
files you edit. If you use Emacs, customize its font-lock mode (FIXME:
|
||||
provide more detail) or try one of its whitespace packages. This appears
|
||||
to be the one that will end up in emacs 23:
|
||||
files you edit. If you use Emacs, customize its font-lock mode
|
||||
or use its WhiteSpace mode:
|
||||
|
||||
http://www.emacswiki.org/emacs/WhiteSpace
|
||||
https://www.emacswiki.org/emacs/WhiteSpace
|
||||
|
||||
[that page says its version also works with emacs 21 and 22]
|
||||
If you use vim, add this to ~/.vimrc:
|
||||
|
||||
let c_space_errors=1
|
||||
@ -581,7 +568,7 @@ Then just open the index.html file (in the generated lcov-html directory)
|
||||
in your favorite web browser.
|
||||
|
||||
========================================================================
|
||||
Copyright (C) 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
Copyright (C) 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Main Automakefile for GNU diffutils.
|
||||
|
||||
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2017 Free Software
|
||||
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free Software
|
||||
# Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
158
NEWS
158
NEWS
@ -1,5 +1,149 @@
|
||||
GNU diffutils NEWS -*- outline -*-
|
||||
|
||||
* Noteworthy changes in release ?.? (????-??-??) [?]
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.12 (2025-04-08) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
diff -r no longer merely summarizes when comparing an empty regular
|
||||
file to a nonempty regular file.
|
||||
[bug#76452 introduced in 3.11]
|
||||
|
||||
diff -y no longer crashes when given nontrivial differences.
|
||||
[bug#76613 introduced in 3.11]
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.11 (2025-02-02) [stable]
|
||||
|
||||
** Improvements
|
||||
|
||||
Programs now quote file names more consistently in diagnostics.
|
||||
For example; "cmp 'none of' /etc/passwd" now might output
|
||||
"cmp: EOF on ‘none of’ which is empty" instead of outputting
|
||||
"cmp: EOF on none of which is empty". In diagnostic messages
|
||||
that traditionally omit quotes and where backward compatibility
|
||||
seems to be important, programs continue to omit quotes unless
|
||||
a file name contains shell metacharacters, in which case programs
|
||||
use shell quoting. For example, although diff continues to output
|
||||
"Only in a: b" as before for most file names, it now outputs
|
||||
"Only in 'a: b': 'c: d'" instead of "Only in a: b: c: d" because the
|
||||
file names 'a: b' and 'c: d' contain spaces. For compatibility
|
||||
with previous practice, diff -c and -u headers continue to quote for
|
||||
C rather than for the shell.
|
||||
|
||||
diff now outputs more information when symbolic links differ, e.g.,
|
||||
"Symbolic links ‘d/f’ -> ‘a’ and ‘e/f’ -> ‘b’ differ", not just
|
||||
"Symbolic links d/f and e/f differ". Special files too, e.g.,
|
||||
"Character special files ‘d/f’ (1, 3) and ‘e/f’ (5, 0) differ", not
|
||||
"File d/f is a character special file while file e/f is a character
|
||||
special file".
|
||||
|
||||
diff's --ignore-case (-i) and --ignore-file-name-case options now
|
||||
support multi-byte characters. For example, they treat Greek
|
||||
capital Δ like small δ when input uses UTF-8.
|
||||
|
||||
diff now supports multi-byte characters when treating white space.
|
||||
In options like --expand-tabs (-t), --ignore-space-change (-b) and
|
||||
--ignore-tab-expansion (-E), diff now recognizes non-ASCII space
|
||||
characters and counts columns for non-ASCII characters.
|
||||
|
||||
** Bug fixes
|
||||
|
||||
cmp -bl no longer omits "M-" from bytes with the high bit set in
|
||||
single-byte locales like en_US.iso8859-1. This fix causes the
|
||||
behavior to be locale independent, and to be the same as the
|
||||
longstanding behavior in the C locale and in locales using UTF-8.
|
||||
[bug introduced in 2.9]
|
||||
|
||||
cmp -i N and -n N no longer fail merely because N is enormous.
|
||||
[bug present since "the beginning"]
|
||||
|
||||
cmp -s no longer mishandles /proc files, for which the Linux kernel
|
||||
reports a zero size even when nonempty. For example, the following
|
||||
shell command now outputs nothing, as it should:
|
||||
cp /proc/cmdline t; cmp -s /proc/cmdline t || echo files differ
|
||||
[bug present since "the beginning"]
|
||||
|
||||
diff -E no longer mishandles some input lines containing '\a', '\b',
|
||||
'\f', '\r', '\v', or '\0'.
|
||||
[bug present since 2.8]
|
||||
|
||||
diff -ly no longer mishandles non-ASCII input.
|
||||
[bug#64461 introduced in 2.9]
|
||||
|
||||
diff - A/B now works correctly when standard input is a directory,
|
||||
by reading a file named B in that directory.
|
||||
[bug present since "the beginning"]
|
||||
|
||||
diff no longer suffers from race conditions in some cases
|
||||
when comparing files in a mutating file system.
|
||||
[bug present since "the beginning"]
|
||||
|
||||
** Release
|
||||
|
||||
distribute gzip-compressed tarballs once again
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.10 (2023-05-21) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
cmp/diff can again work with file dates past Y2K38
|
||||
[bug introduced in 3.9]
|
||||
|
||||
diff -D no longer fails to output #ifndef lines.
|
||||
[bug#61193 introduced in 3.9]
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.9 (2023-01-15) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
diff -c and -u no longer output incorrect timezones in headers
|
||||
on platforms like Solaris where struct tm lacks tm_gmtoff.
|
||||
[bug#51228 introduced in 3.4]
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.8 (2021-08-01) [stable]
|
||||
|
||||
** Incompatible changes
|
||||
|
||||
diff no longer treats a closed stdin as representing an absent file
|
||||
in usage like 'diff --new-file - foo <&-'. This feature was rarely
|
||||
if ever used and was not portable to POSIX platforms that reopen
|
||||
stdin on exec, such as SELinux if the process underwent an AT_SECURE
|
||||
transition, or HP-UX even if not setuid.
|
||||
[bug#33965 introduced in 2.8]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
diff and related programs no longer get confused if stdin, stdout,
|
||||
or stderr are closed. Previously, they sometimes opened files into
|
||||
file descriptors 0, 1, or 2 and then mistakenly did I/O with them
|
||||
that was intended for stdin, stdout, or stderr.
|
||||
[bug#33965 present since "the beginning"]
|
||||
|
||||
cmp, diff and sdiff no longer treat negative command-line
|
||||
option-arguments as if they were large positive numbers.
|
||||
[bug#35256 introduced in 2.8]
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.7 (2018-12-31) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
diff --strip-trailing-cr with a single CR byte in one input file
|
||||
would provoke an uninitialized memory read, e.g.,
|
||||
diff -a --strip-trailing-cr <(printf '\r') <(echo a)
|
||||
[bug introduced in 2.8 with addition of the --strip-trailing-cr option]
|
||||
|
||||
** Improvements
|
||||
|
||||
diff --color now produces output compatible with less -R.
|
||||
|
||||
|
||||
* Noteworthy changes in release 3.6 (2017-05-21) [stable]
|
||||
|
||||
** New features
|
||||
@ -309,12 +453,12 @@ User-visible changes in version 2.4:
|
||||
printed according to the printf specification.
|
||||
E.g. '%5df' prints the number of the first line in the
|
||||
group in the old file using the "%5d" format.
|
||||
e: line number just before the group in old file; equals f - 1
|
||||
f: first line number in group in the old file
|
||||
l: last line number in group in the old file
|
||||
m: line number just after the group in old file; equals l + 1
|
||||
n: number of lines in group in the old file; equals l - f + 1
|
||||
E, F, L, M, N: likewise, for lines in the new file
|
||||
e: line number just before the group in old file; equals f - 1
|
||||
f: first line number in group in the old file
|
||||
l: last line number in group in the old file
|
||||
m: line number just after the group in old file; equals l + 1
|
||||
n: number of lines in group in the old file; equals l - f + 1
|
||||
E, F, L, M, N: likewise, for lines in the new file
|
||||
%(A=B?T:E)
|
||||
If A equals B then T else E. A and B are each either a decimal
|
||||
constant or a single letter interpreted as above. T and E are
|
||||
@ -404,7 +548,7 @@ User-visible changes in version 2.0:
|
||||
|
||||
|
||||
|
||||
Copyright (C) 1993-1994, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2017 Free
|
||||
Copyright (C) 1993-1994, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free
|
||||
Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Diffutils.
|
||||
|
||||
15
README
15
README
@ -30,19 +30,12 @@ installation. If you have an older version of libiconv, please
|
||||
upgrade to the latest one; see <ftp://ftp.gnu.org/gnu/libiconv/>. If
|
||||
the problem seems isolated to diffutils, though, please report a bug.
|
||||
|
||||
This program requires a Standard C compiler (C89 or later). If you
|
||||
This program requires a Standard C compiler (C99 or later). If you
|
||||
have a nonstandard compiler, please install GCC first.
|
||||
|
||||
If you make changes to the source code, you may need appropriate
|
||||
versions of GNU build tools to regenerate the intermediate files. The
|
||||
following versions were used to generate the intermediate files in
|
||||
this distribution:
|
||||
|
||||
* Autoconf 2.59 <ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz>
|
||||
* Automake 1.8.3 <ftp://ftp.gnu.org/gnu/automake/automake-1.8.3.tar.gz>
|
||||
* gettext 0.14.1 <ftp://ftp.gnu.org/gnu/gettext/gettext-0.14.1.tar.gz>
|
||||
* help2man 1.33 <ftp://ftp.gnu.org/gnu/help2man/help2man-1.33.1.tar.gz>
|
||||
* Texinfo 4.7 <ftp://ftp.gnu.org/gnu/texinfo/texinfo-4.7.tar.gz>
|
||||
versions of GNU build tools to regenerate the intermediate files.
|
||||
See README-prereq for details.
|
||||
|
||||
For any copyright year range specified as YYYY-ZZZZ in this package
|
||||
note that the range specifies every single year in that closed interval.
|
||||
@ -51,7 +44,7 @@ Please report bugs to <bug-diffutils@gnu.org>.
|
||||
|
||||
-----
|
||||
|
||||
Copyright (C) 1992, 1998, 2001-2002, 2004, 2009-2013, 2015-2017 Free Software
|
||||
Copyright (C) 1992, 1998, 2001-2002, 2004, 2009-2013, 2015-2026 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is part of GNU Diffutils.
|
||||
|
||||
101
README-hacking
101
README-hacking
@ -1,58 +1,63 @@
|
||||
-*- outline -*-
|
||||
Building from a Git repository -*- outline -*-
|
||||
|
||||
These notes intend to help people working on the checked-out sources.
|
||||
These requirements do not apply when building from a distribution tarball.
|
||||
See also HACKING for more detailed contribution guidelines.
|
||||
If this package has a file HACKING, please also read that file for
|
||||
more detailed contribution guidelines.
|
||||
|
||||
* Requirements
|
||||
|
||||
We've opted to keep only the highest-level sources in the GIT repository.
|
||||
This eases our maintenance burden, (fewer merges etc.), but imposes more
|
||||
We've opted to keep only the highest-level sources in the Git repository.
|
||||
This eases our maintenance burden (fewer merges etc.), but imposes more
|
||||
requirements on anyone wishing to build from the just-checked-out sources.
|
||||
Note the requirements to build the released archive are much less and
|
||||
are just the requirements of the standard ./configure && make procedure.
|
||||
(The requirements to build from a release are much less and are just
|
||||
the requirements of the standard './configure && make' procedure.)
|
||||
Specific development tools and versions will be checked for and listed by
|
||||
the bootstrap script. See README-prereq for specific notes on obtaining
|
||||
these prerequisite tools.
|
||||
|
||||
Valgrind <http://valgrind.org/> is also highly recommended, if
|
||||
Valgrind supports your architecture.
|
||||
Valgrind <https://valgrind.org/> is also highly recommended, if
|
||||
Valgrind supports your architecture. See also README-valgrind
|
||||
(if present).
|
||||
|
||||
While building from a just-cloned source tree may require installing a
|
||||
few prerequisites, later, a plain 'git pull && make' should be sufficient.
|
||||
few prerequisites, later, a plain 'git pull && make' typically suffices.
|
||||
|
||||
* First GIT checkout
|
||||
* First Git checkout
|
||||
|
||||
You can get a copy of the source repository like this:
|
||||
|
||||
$ git clone git://git.sv.gnu.org/diffutils
|
||||
$ cd diffutils
|
||||
$ git clone https://git.savannah.gnu.org/git/<packagename>
|
||||
$ cd <packagename>
|
||||
|
||||
As an optional step, if you already have a copy of the gnulib git
|
||||
repository on your hard drive, then you can use it as a reference to
|
||||
reduce download time and disk space requirements:
|
||||
where '<packagename>' stands for 'coreutils' or whatever other package
|
||||
you are building.
|
||||
|
||||
$ export GNULIB_SRCDIR=/path/to/gnulib
|
||||
|
||||
The next step is to get and check other files needed to build,
|
||||
which are extracted from other source packages:
|
||||
|
||||
$ ./bootstrap
|
||||
|
||||
To use the most-recent gnulib (as opposed to the gnulib version that
|
||||
To use the most-recent Gnulib (as opposed to the Gnulib version that
|
||||
the package last synchronized to), do this next:
|
||||
|
||||
$ git submodule foreach git pull origin master
|
||||
$ git commit -m 'build: update gnulib submodule to latest' gnulib
|
||||
|
||||
As an optional step, if you already have a copy of the Gnulib Git
|
||||
repository, then you can use it as a reference to reduce download
|
||||
time and file system space requirements:
|
||||
|
||||
$ export GNULIB_SRCDIR=/path/to/gnulib
|
||||
|
||||
The next step is to get and check other files needed to build,
|
||||
which are extracted from other source packages [1]:
|
||||
|
||||
$ ./bootstrap
|
||||
|
||||
And there you are! Just
|
||||
|
||||
$ ./configure --quiet #[--enable-gcc-warnings] [*]
|
||||
$ ./configure --quiet #[--disable-gcc-warnings] [2]
|
||||
$ make
|
||||
$ make check
|
||||
|
||||
At this point, there should be no difference between your local copy,
|
||||
and the GIT master copy:
|
||||
and the Git master copy:
|
||||
|
||||
$ git diff
|
||||
|
||||
@ -60,27 +65,35 @@ should output no difference.
|
||||
|
||||
Enjoy!
|
||||
|
||||
[*] The --enable-gcc-warnings option is useful only with glibc
|
||||
and with a very recent version of gcc. You'll probably also have
|
||||
to use recent system headers. If you configure with this option,
|
||||
and spot a problem, please be sure to send the report to the bug
|
||||
reporting address of this package, and not to that of gnulib, even
|
||||
if the problem seems to originate in a gnulib-provided file.
|
||||
[1] You can also bootstrap in two phases: first, './bootstrap --pull'
|
||||
fetches auxiliary files from the network, and then './bootstrap --gen'
|
||||
generates files by using local tools without accessing the net.
|
||||
Although './bootstrap' by default does both phases, auditable builds
|
||||
can do them separately.
|
||||
|
||||
[2] By default GCC warnings are enabled when building from Git.
|
||||
If you get warnings with recent GCC and Glibc with default
|
||||
configure-time options, please report the warnings to the bug
|
||||
reporting address of this package instead of to bug-gnulib,
|
||||
even if the problem seems to originate in a Gnulib-provided file.
|
||||
If you get warnings with other configurations, you can run
|
||||
'./configure --disable-gcc-warnings' or 'make WERROR_CFLAGS='
|
||||
to build quietly or verbosely, respectively.
|
||||
-----
|
||||
|
||||
* Submitting patches
|
||||
|
||||
If you develop a fix or a new feature, please send it to the
|
||||
appropriate bug-reporting address as reported by the --help option of
|
||||
each program. One way to do this is to use vc-dwim
|
||||
<http://www.gnu.org/software/vc-dwim/>), as follows.
|
||||
<https://www.gnu.org/software/vc-dwim/>), as follows.
|
||||
|
||||
Run the command "vc-dwim --help", copy its definition of the
|
||||
"git-changelog-symlink-init" function into your shell, and then run
|
||||
this function at the top-level directory of the package.
|
||||
Run the command "vc-dwim --initialize" from the top-level directory
|
||||
of this package's git-cloned hierarchy.
|
||||
|
||||
Edit the (empty) ChangeLog file that this command creates, creating a
|
||||
properly-formatted entry according to the GNU coding standards
|
||||
<http://www.gnu.org/prep/standards/html_node/Change-Logs.html>.
|
||||
<https://www.gnu.org/prep/standards/html_node/Change-Logs.html>.
|
||||
|
||||
Make your changes.
|
||||
|
||||
@ -92,9 +105,21 @@ each program. One way to do this is to use vc-dwim
|
||||
Run the command "git format-patch --stdout -1", and email its output
|
||||
in, using the output's subject line.
|
||||
|
||||
* Continuous integration
|
||||
|
||||
There are two continuous integrations that regularly build and test
|
||||
diffutils:
|
||||
- On a Linux/glibc system only:
|
||||
https://gitlab.com/gnu-diffutils/ci-distcheck/pipelines
|
||||
https://gitlab.com/gnu-diffutils/ci-distcheck/-/jobs?scope=finished
|
||||
This one will catch only the most blatant mistakes.
|
||||
- On many platforms:
|
||||
https://github.com/gnu-diffutils/ci-check/actions
|
||||
This one catches platform-specific bugs.
|
||||
|
||||
-----
|
||||
|
||||
Copyright (C) 2002-2007, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
Copyright (C) 2002-2026 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -107,4 +132,4 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
39
README-prereq
Normal file
39
README-prereq
Normal file
@ -0,0 +1,39 @@
|
||||
This gives some notes on obtaining the tools required for development.
|
||||
These tools can be used by the 'bootstrap' and 'configure' scripts,
|
||||
as well as by 'make'. They include:
|
||||
|
||||
- Autoconf <https://www.gnu.org/software/autoconf/>
|
||||
- Automake <https://www.gnu.org/software/automake/>
|
||||
- Gettext <https://www.gnu.org/software/gettext/>
|
||||
- Git <https://git-scm.com/>
|
||||
- Gzip <https://www.gnu.org/software/gzip/>
|
||||
- Help2man <https://www.gnu.org/software/help2man/>
|
||||
- M4 <https://www.gnu.org/software/m4/>
|
||||
- Make <https://www.gnu.org/software/make/>
|
||||
- Perl <https://www.cpan.org/>
|
||||
- Tar <https://www.gnu.org/software/tar/>
|
||||
- Texinfo <https://www.gnu.org/software/texinfo/>
|
||||
- Wget <http://www.gnu.org/software/wget/>
|
||||
- XZ Utils <https://tukaani.org/xz/>
|
||||
|
||||
It is generally better to use official packages for your system.
|
||||
If a package is not officially available you can build it from source
|
||||
and install it into a directory that you can then use to build this
|
||||
package. If some packages are available but are too old, install the
|
||||
too-old versions first as they may be needed to build newer versions.
|
||||
|
||||
Here is an example of how to build a program from source. This
|
||||
example is for Autoconf; a similar approach should work for the other
|
||||
developer prerequisites. This example assumes Autoconf 2.72; it
|
||||
should be OK to use a later version of Autoconf, if available.
|
||||
|
||||
prefix=$HOME/prefix # (or wherever else you choose)
|
||||
export PATH=$prefix/bin:$PATH
|
||||
wget https://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.72.tar.gz
|
||||
gzip -d <autoconf-2.72.tar.gz | tar xf -
|
||||
cd autoconf-2.72
|
||||
./configure --prefix=$prefix
|
||||
make install
|
||||
|
||||
Once the prerequisites are installed, you can build this package as
|
||||
described in README-hacking.
|
||||
10
TODO
10
TODO
@ -1,7 +1,15 @@
|
||||
Add --git option to generate output compatible with 'git diff -p'.
|
||||
This would behave like 'diff -p', except that it would also generate
|
||||
the extended headers 'old mode', 'new mode', 'deleted file mode', and
|
||||
'new file mode', and it would quote file names with unusual characters.
|
||||
GNU patch already parses this format.
|
||||
|
||||
Add --include option (opposite of --exclude).
|
||||
|
||||
Add SEEK_DATA/SEEK_HOLE sparse file optimization to cmp, diff -q, etc.
|
||||
|
||||
Look into sdiff improvement here:
|
||||
http://www.pkix.net/~chuck/sdiff2.diff
|
||||
https://web.archive.org/web/20090106164131/http://www.pkix.net/~chuck/sdiff2.diff
|
||||
|
||||
Propagate stderr from subprocess so that diff3 does
|
||||
a better job of explaining _why_:
|
||||
|
||||
160
bootstrap.conf
160
bootstrap.conf
@ -1,6 +1,6 @@
|
||||
# Bootstrap configuration.
|
||||
|
||||
# Copyright (C) 2006-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2006-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -15,14 +15,52 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
avoided_gnulib_modules='
|
||||
--avoid=hard-locale
|
||||
--avoid=localename-unsafe
|
||||
--avoid=localename-unsafe-limited
|
||||
--avoid=lock-tests
|
||||
--avoid=mbuiter
|
||||
--avoid=mbuiterf
|
||||
--avoid=setlocale
|
||||
'
|
||||
# These tests depend on localcharset, which we avoid.
|
||||
avoided_gnulib_tests='
|
||||
--avoid=c32width-tests
|
||||
--avoid=quotearg-simple-tests
|
||||
--avoid=regex-tests
|
||||
--avoid=wcwidth-tests
|
||||
'
|
||||
# These test whether all bytes are valid in the C locale,
|
||||
# which we don't care about.
|
||||
avoided_gnulib_tests=$avoided_gnulib_tests'
|
||||
--avoid=btoc32-tests
|
||||
--avoid=btowc-tests
|
||||
--avoid=mbrtoc32-tests
|
||||
--avoid=mbrtowc-tests
|
||||
--avoid=mbsrtoc32s-tests
|
||||
--avoid=mbsrtowcs-tests
|
||||
'
|
||||
|
||||
# gnulib modules used by this package.
|
||||
gnulib_modules='
|
||||
announce-gen
|
||||
argmatch
|
||||
assert-h
|
||||
attribute
|
||||
binary-io
|
||||
bool
|
||||
builtin-expect
|
||||
c-ctype
|
||||
c-file-type
|
||||
c-stack
|
||||
c32isprint
|
||||
c32isspace
|
||||
c32tolower
|
||||
c32width
|
||||
careadlinkat
|
||||
config-h
|
||||
d-type
|
||||
diffseq
|
||||
dirname
|
||||
do-release-commit-and-tag
|
||||
@ -31,99 +69,151 @@ error
|
||||
exclude
|
||||
exitfail
|
||||
extensions
|
||||
extern-inline
|
||||
fcntl
|
||||
fdl
|
||||
file-type
|
||||
fdopendir
|
||||
filenamecat
|
||||
flexmember
|
||||
fnmatch-gnu
|
||||
getopt
|
||||
fopen-gnu
|
||||
fstatat
|
||||
getopt-gnu
|
||||
gettext-h
|
||||
gettime
|
||||
git-version-gen
|
||||
gitlog-to-changelog
|
||||
gnu-make
|
||||
gnu-web-doc-update
|
||||
gnumakefile
|
||||
gnupload
|
||||
hard-locale
|
||||
inttostr
|
||||
inttypes
|
||||
isblank
|
||||
ialloc
|
||||
idx
|
||||
intprops
|
||||
inttypes-h
|
||||
largefile
|
||||
lstat
|
||||
maintainer-makefile
|
||||
manywarnings
|
||||
mbrtowc
|
||||
mbscasecmp
|
||||
mcel-prefer
|
||||
mempcpy
|
||||
minmax
|
||||
mkstemp
|
||||
mktime
|
||||
nstrftime-limited
|
||||
nullptr
|
||||
openat
|
||||
pclose
|
||||
perl
|
||||
popen
|
||||
progname
|
||||
propername
|
||||
propername-lite
|
||||
quote
|
||||
raise
|
||||
rawmemchr
|
||||
readdir
|
||||
readlinkat
|
||||
readme-release
|
||||
regex
|
||||
same-inode
|
||||
sh-quote
|
||||
signal
|
||||
sigaction
|
||||
signal-h
|
||||
sigprocmask
|
||||
stat
|
||||
stat-macros
|
||||
stat-size
|
||||
stat-time
|
||||
stdint
|
||||
stdc_bit_width
|
||||
stdckdint-h
|
||||
stdcountof-h
|
||||
stdint-h
|
||||
stpcpy
|
||||
strcase
|
||||
strftime
|
||||
strptime
|
||||
strtoumax
|
||||
sys_wait
|
||||
strtoimax
|
||||
system-quote
|
||||
unistd
|
||||
sys_types-h
|
||||
sys_wait-h
|
||||
timespec
|
||||
timespec_get
|
||||
time_rz
|
||||
unistd-h
|
||||
unlocked-io
|
||||
update-copyright
|
||||
vararrays
|
||||
verify
|
||||
version-etc
|
||||
version-etc-fsf
|
||||
wcwidth
|
||||
xalloc
|
||||
xfreopen
|
||||
xreadlink
|
||||
xstrtoumax
|
||||
xvasprintf
|
||||
xmalloca
|
||||
xstdopen
|
||||
xstrtoimax
|
||||
year2038
|
||||
'
|
||||
|
||||
# Additional xgettext options to use. Use "\\\newline" to break lines.
|
||||
XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\
|
||||
--flag=asnprintf:3:c-format\\\
|
||||
--flag=asprintf:2:c-format\\\
|
||||
--from-code=UTF-8\\\
|
||||
--flag=asprintf:2:c-format --flag=vasprintf:2:c-format\\\
|
||||
--flag=asnprintf:3:c-format --flag=vasnprintf:3:c-format\\\
|
||||
--flag=message:1:c-format --flag=message5:1:c-format\\\
|
||||
--flag=message:1:c-format\\\
|
||||
--flag=try_help:1:c-format\\\
|
||||
--flag=vasprintf:2:c-format\\\
|
||||
--flag=vasnprintf:3:c-format\\\
|
||||
'
|
||||
|
||||
gnulib_tool_option_extras="--tests-base=gnulib-tests --with-tests
|
||||
--avoid=localename
|
||||
--avoid=lock
|
||||
--symlink
|
||||
--makefile-name=gnulib.mk
|
||||
gnulib_tool_option_extras="--tests-base=gnulib-tests
|
||||
--local-dir=gl
|
||||
--with-tests
|
||||
--symlink
|
||||
--makefile-name=gnulib.mk
|
||||
$avoided_gnulib_modules
|
||||
$avoided_gnulib_tests
|
||||
"
|
||||
|
||||
# Build prerequisites
|
||||
buildreq="\
|
||||
autoconf 2.61
|
||||
automake 1.12.2
|
||||
autopoint -
|
||||
autoconf 2.64
|
||||
automake 1.14
|
||||
autopoint 0.19.2
|
||||
gettext 0.19.2
|
||||
git 1.4.4
|
||||
gperf -
|
||||
gzip -
|
||||
help2man -
|
||||
m4 -
|
||||
makeinfo -
|
||||
perl 5.5
|
||||
rsync -
|
||||
texi2pdf 6.1
|
||||
tar -
|
||||
wget -
|
||||
xz -
|
||||
"
|
||||
|
||||
bootstrap_post_import_hook()
|
||||
bootstrap_post_import_hook ()
|
||||
{
|
||||
# Automake requires that ChangeLog exist.
|
||||
touch ChangeLog || exit 1
|
||||
|
||||
# Copy tests/init.sh from Gnulib.
|
||||
$gnulib_tool --copy-file tests/init.sh
|
||||
|
||||
# Copy pkg-config's pkg.m4 so that our downstream users don't need to.
|
||||
local ac_dir=`aclocal --print-ac-dir`
|
||||
test -s "$ac_dir/dirlist" && ac_dir=$ac_dir:`tr '\n' : < "$ac_dir/dirlist"`
|
||||
oIFS=$IFS
|
||||
IFS=:
|
||||
for dir in \
|
||||
$ACLOCAL_PATH $ac_dir /usr/share/aclocal ''
|
||||
do
|
||||
IFS=$oIFS
|
||||
if test -n "$dir" && test -r "$dir/pkg.m4"; then
|
||||
cp "$dir/pkg.m4" m4/pkg.m4
|
||||
return
|
||||
fi
|
||||
done
|
||||
IFS=$oIFS
|
||||
die 'Cannot find pkg.m4; perhaps you need to install pkg-config'
|
||||
}
|
||||
|
||||
bootstrap_epilogue()
|
||||
|
||||
31
cfg.mk
31
cfg.mk
@ -1,5 +1,5 @@
|
||||
# Customize maint.mk -*- makefile -*-
|
||||
# Copyright (C) 2003-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
# Use the direct link. This is guaranteed to work immediately, while
|
||||
# it can take a while for the faster mirror links to become usable.
|
||||
url_dir_list = http://ftp.gnu.org/gnu/$(PACKAGE)
|
||||
url_dir_list = https://ftp.gnu.org/gnu/$(PACKAGE)
|
||||
|
||||
# Used in maint.mk's web-manual rule
|
||||
manual_title = Comparing and Merging Files
|
||||
@ -24,20 +24,30 @@ manual_title = Comparing and Merging Files
|
||||
# Tests not to run as part of "make distcheck".
|
||||
local-checks-to-skip = \
|
||||
sc_error_message_period \
|
||||
sc_error_message_uppercase
|
||||
sc_error_message_uppercase \
|
||||
sc_indent
|
||||
|
||||
# Tools used to bootstrap this package, used for "announcement".
|
||||
bootstrap-tools = autoconf,automake,gnulib
|
||||
|
||||
# Override the default Cc: used in generating an announcement.
|
||||
announcement_Cc_ = $(translation_project_), $(PACKAGE)-devel@gnu.org
|
||||
|
||||
# Now that we have better tests, make this the default.
|
||||
export VERBOSE = yes
|
||||
|
||||
old_NEWS_hash = 0216ec3bf3e3322f33afd4e949a9a29b
|
||||
old_NEWS_hash = cfdcec044a361bf0e7b4e860bacc3d53
|
||||
|
||||
# Tell maint.mk's syntax-check rules that diff gets config.h directly or
|
||||
# via diff.h or system.h.
|
||||
config_h_header = (<config\.h>|"(diff|system)\.h")
|
||||
|
||||
# Write base64-encoded (not hex) checksums into the announcement.
|
||||
announce_gen_args = --cksum-checksums
|
||||
|
||||
# Add an exemption for sc_makefile_at_at_check.
|
||||
_makefile_at_at_check_exceptions = ' && !/MAKEINFO/'
|
||||
|
||||
update-copyright-env = \
|
||||
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
|
||||
UPDATE_COPYRIGHT_MAX_LINE_LENGTH=79
|
||||
@ -66,7 +76,14 @@ config-save:
|
||||
ln -nsf $(_date_time) $(_cf_state_dir)/latest
|
||||
cp lib/config.h config.status $(_cf_state_dir)/latest
|
||||
|
||||
exclude_file_name_regexp--sc_space_tab = ^gl/lib/.*\.c\.diff$$
|
||||
exclude_file_name_regexp--sc_GPL_version = ^gl/lib/
|
||||
exclude_file_name_regexp--sc_bindtextdomain = ^gl/tests/
|
||||
exclude_file_name_regexp--sc_doubled_words = ^gl/lib/mcel\.h$$
|
||||
exclude_file_name_regexp--sc_prohibit_doubled_word = ^(gl/lib/mcel\.h|tests/y2038-vs-32bit)$$
|
||||
exclude_file_name_regexp--sc_prohibit_strcmp = ^gl/lib/
|
||||
|
||||
# Tell gnulib's tight_scope rule that we mark externs with XTERN
|
||||
export _gl_TS_extern = extern|XTERN
|
||||
# Tell gnulib's tight_scope rule that we mark extern inlines with
|
||||
# DIFF_INLINE and SYSTEM_INLINE.
|
||||
export _gl_TS_extern = extern|DIFF_INLINE|SYSTEM_INLINE
|
||||
|
||||
codespell_ignore_words_list = FO,ND,debbugs
|
||||
|
||||
228
configure.ac
228
configure.ac
@ -1,6 +1,6 @@
|
||||
# Configure template for GNU Diffutils.
|
||||
|
||||
# Copyright (C) 1994-1995, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2017
|
||||
# Copyright (C) 1994-1995, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2026
|
||||
# Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@ -16,145 +16,193 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_PREREQ([2.64])
|
||||
|
||||
AC_INIT([GNU diffutils],
|
||||
m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
[bug-diffutils@gnu.org])
|
||||
[bug-diffutils@gnu.org],
|
||||
[diffutils],
|
||||
[https://www.gnu.org/software/diffutils/])
|
||||
|
||||
AC_CONFIG_SRCDIR([src/diff.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.11 dist-xz no-dist-gzip subdir-objects
|
||||
color-tests parallel-tests])
|
||||
AM_INIT_AUTOMAKE([1.11 dist-xz subdir-objects color-tests parallel-tests])
|
||||
AM_SILENT_RULES([yes]) # make --enable-silent-rules the default.
|
||||
|
||||
AC_CONFIG_HEADER([lib/config.h:lib/config.hin])
|
||||
AC_CONFIG_HEADERS([lib/config.h:lib/config.hin])
|
||||
|
||||
AC_PROG_AWK
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
AM_MISSING_PROG([HELP2MAN], [help2man])
|
||||
AC_PROG_RANLIB
|
||||
|
||||
# Do not worry about multibyte C locales.
|
||||
# This removes dependencies on hard-locale etc.
|
||||
gl_cv_func_mbrtowc_C_locale_sans_EILSEQ="guessing yes"
|
||||
gl_cv_func_mbrtoc32_C_locale_sans_EILSEQ="guessing yes"
|
||||
|
||||
# Default Gnulib configuration to no multithreading,
|
||||
# since diffutils is single-threaded.
|
||||
# Although the builder can override this with the --enable-threads option
|
||||
# of 'configure', diffutils does not support that on all platforms.
|
||||
m4_define([gl_THREADLIB_DEFAULT_NO])
|
||||
|
||||
gl_EARLY
|
||||
gl_USE_SYSTEM_EXTENSIONS
|
||||
gl_INIT
|
||||
|
||||
# Ensure VLAs are not used.
|
||||
# Note -Wvla is implicitly added by gl_MANYWARN_ALL_GCC
|
||||
AC_DEFINE([GNULIB_NO_VLA], [1], [Define to 1 to disable use of VLAs])
|
||||
|
||||
# diffutils is single-threaded; optimize for this.
|
||||
AC_DEFINE([GNULIB_EXCLUDE_SINGLE_THREAD], [1],
|
||||
['exclude' code is called only from 1 thread.])
|
||||
AC_DEFINE([GNULIB_MBRTOWC_SINGLE_THREAD], [1],
|
||||
['mbrtowc', 'mbrtoc32', 'regex' code is called only from 1 thread.])
|
||||
AC_DEFINE([GNULIB_REGEX_SINGLE_THREAD], [1],
|
||||
['regex' code is called only from 1 thread.])
|
||||
AC_DEFINE([GNULIB_WCHAR_SINGLE_LOCALE], [1],
|
||||
[Locale-sensitive functions are called only after locale is set.])
|
||||
|
||||
AC_DEFINE([GNULIB_MBRTOC32_REGULAR], [1],
|
||||
[Do not worry about rare encodings like CP864, EBCDIC, Johab, and Shift JIS
|
||||
that glibc does not support.])
|
||||
|
||||
AC_DEFINE([REQUIRE_GNUISH_STRFTIME_AM_PM], [0],
|
||||
[Do not worry about GNU strftime behavior for AM and PM indicators.])
|
||||
AC_DEFINE([SUPPORT_NON_GREG_CALENDARS_IN_STRFTIME], [false],
|
||||
[Do not worry about GNU strftime behavior for non-Gregorian calendars.])
|
||||
|
||||
# Diffutils translates Gnulib's msgids too.
|
||||
AC_DEFINE([GNULIB_TEXT_DOMAIN], [PACKAGE],
|
||||
[Textdomain to use when translating Gnulib's msgids.])
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_TYPE_PID_T
|
||||
|
||||
AC_FUNC_FORK
|
||||
|
||||
dnl O_PATH exists since Linux 2.6.39, but is supported with fstat() only since
|
||||
dnl Linux 3.6. Use a configure test rather than testing `uname -sr`.
|
||||
AC_CACHE_CHECK([whether O_PATH supports fstat],
|
||||
[du_cv_O_PATH_fstat],
|
||||
[AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([[
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
]], [[
|
||||
struct stat statbuf;
|
||||
int fd = openat (AT_FDCWD, "conftest.c", O_RDONLY|O_PATH);
|
||||
return !(fd >= 0 && fstat (fd, &statbuf) == 0);
|
||||
]])
|
||||
],
|
||||
[du_cv_O_PATH_fstat=yes],
|
||||
[du_cv_O_PATH_fstat=no],
|
||||
[du_cv_O_PATH_fstat="guessing no"])
|
||||
])
|
||||
case "$du_cv_O_PATH_fstat" in
|
||||
yes)
|
||||
AC_DEFINE([O_PATH_SUPPORTS_FSTAT], [1],
|
||||
[Define to 1 if <fcntl.h> defines O_PATH and file descriptors
|
||||
created with this flag are supported by fstat().])
|
||||
;;
|
||||
esac
|
||||
|
||||
# gl_GCC_VERSION_IFELSE([major], [minor], [run-if-found], [run-if-not-found])
|
||||
# ------------------------------------------------
|
||||
# If $CPP is gcc-MAJOR.MINOR or newer, then run RUN-IF-FOUND.
|
||||
# Otherwise, run RUN-IF-NOT-FOUND.
|
||||
AC_DEFUN([gl_GCC_VERSION_IFELSE],
|
||||
[AC_PREPROC_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[
|
||||
#if ($1) < __GNUC__ || (($1) == __GNUC__ && ($2) <= __GNUC_MINOR__)
|
||||
/* ok */
|
||||
#else
|
||||
# error "your version of gcc is older than $1.$2"
|
||||
#endif
|
||||
]]),
|
||||
], [$3], [$4])
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([gcc-warnings],
|
||||
[AS_HELP_STRING([--enable-gcc-warnings],
|
||||
[turn on lots of GCC warnings (for developers)])],
|
||||
[AS_HELP_STRING([[--enable-gcc-warnings[=TYPE]]],
|
||||
[control generation of GCC warnings. The TYPE 'no' disables
|
||||
warnings (default for non-developer builds); 'yes' generates
|
||||
cheap warnings if available (default for developer builds);
|
||||
'expensive' in addition generates expensive-to-compute warnings
|
||||
if available.])],
|
||||
[case $enableval in
|
||||
yes|no) ;;
|
||||
no|yes|expensive) ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;;
|
||||
esac
|
||||
gl_gcc_warnings=$enableval],
|
||||
[if test -d "$srcdir"/.git; then
|
||||
gl_gcc_warnings=yes
|
||||
else
|
||||
gl_gcc_warnings=no
|
||||
fi]
|
||||
[
|
||||
# GCC provides fine-grained control over diagnostics which
|
||||
# is used in gnulib for example to suppress warnings from
|
||||
# certain sections of code. So if this is available and
|
||||
# we're running from a git repo, then auto enable the warnings.
|
||||
gl_gcc_warnings=no
|
||||
gl_GCC_VERSION_IFELSE([4], [6],
|
||||
[test -d "$srcdir"/.git \
|
||||
&& ! test -f "$srcdir"/.tarball-version \
|
||||
&& gl_gcc_warnings=yes])]
|
||||
)
|
||||
|
||||
if test "$gl_gcc_warnings" = yes; then
|
||||
if test $gl_gcc_warnings != no; then
|
||||
gl_WARN_ADD([-Werror], [WERROR_CFLAGS])
|
||||
AC_SUBST([WERROR_CFLAGS])
|
||||
|
||||
nw=
|
||||
ew=
|
||||
AS_IF([test $gl_gcc_warnings != expensive],
|
||||
[# -fanalyzer and related options slow GCC considerably.
|
||||
ew="$ew -fanalyzer -Wno-analyzer-malloc-leak"])
|
||||
|
||||
# This, $nw, is the list of warnings we disable.
|
||||
nw="$nw -Wdeclaration-after-statement" # too useful to forbid
|
||||
nw="$nw -Waggregate-return" # anachronistic
|
||||
nw="$nw -Wlong-long" # C90 is anachronistic (lib/gethrxtime.h)
|
||||
nw="$nw -Wc++-compat" # We don't care about C++ compilers
|
||||
nw="$nw -Wundef" # Warns on '#if GNULIB_FOO' etc in gnulib
|
||||
nw="$nw -Wtraditional" # Warns on #elif which we use often
|
||||
nw="$nw -Wcast-qual" # Too many warnings for now
|
||||
nw="$nw -Wconversion" # Too many warnings for now
|
||||
nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings
|
||||
nw="$nw -Wsign-conversion" # Too many warnings for now
|
||||
nw="$nw -Wtraditional-conversion" # Too many warnings for now
|
||||
nw="$nw -Wunreachable-code" # Too many warnings for now
|
||||
nw="$nw -Wpadded" # Our structs are not padded
|
||||
nw="$nw -Wredundant-decls" # openat.h declares e.g., mkdirat
|
||||
nw="$nw -Wlogical-op" # any use of fwrite provokes this
|
||||
nw="$nw -Wformat-nonliteral" # who.c and pinky.c strftime uses
|
||||
nw="$nw -Wvla" # warnings in gettext.h
|
||||
nw="$nw -Wnested-externs" # use of XARGMATCH/verify_function__
|
||||
nw="$nw -Wswitch-enum" # Too many warnings for now
|
||||
nw="$nw -Wswitch-default" # Too many warnings for now
|
||||
nw="$nw -Wstack-protector" # not worth working around
|
||||
# things I might fix soon:
|
||||
nw="$nw -Wfloat-equal" # sort.c, seq.c
|
||||
nw="$nw -Wmissing-format-attribute" # copy.c
|
||||
nw="$nw -Wunsafe-loop-optimizations" # a few src/*.c
|
||||
nw="$nw -Winline" # system.h's readdir_ignoring_dot_and_dotdot
|
||||
nw="$nw -Wstrict-overflow" # expr.c, pr.c, tr.c, factor.c
|
||||
nw="$nw -Wformat-extra-args" # sdiff.c
|
||||
# ?? -Wstrict-overflow
|
||||
nw=$ew
|
||||
nw="$nw -Winline" # not a correctness warning
|
||||
nw="$nw -Wstack-protector" # not a correctness warning
|
||||
|
||||
gl_MANYWARN_ALL_GCC([ws])
|
||||
gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
|
||||
for w in $ws; do
|
||||
gl_WARN_ADD([$w])
|
||||
done
|
||||
gl_WARN_ADD([-Wno-missing-field-initializers]) # We need this one
|
||||
gl_WARN_ADD([-Wno-sign-compare]) # Too many warnings for now
|
||||
gl_WARN_ADD([-Wno-pointer-sign]) # Too many warnings for now
|
||||
gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
|
||||
gl_WARN_ADD([-Wno-sign-compare]) # Too many false positives
|
||||
gl_WARN_ADD([-Wno-format-nonliteral])
|
||||
|
||||
# In spite of excluding -Wlogical-op above, it is enabled, as of
|
||||
# gcc 4.5.0 20090517, and it provokes warnings in cat.c, dd.c, truncate.c
|
||||
gl_WARN_ADD([-Wno-logical-op])
|
||||
|
||||
gl_WARN_ADD([-fdiagnostics-show-option])
|
||||
gl_WARN_ADD([-funit-at-a-time])
|
||||
gl_WARN_ADD([-fno-common])
|
||||
|
||||
AC_SUBST([WARN_CFLAGS])
|
||||
|
||||
AC_DEFINE([lint], [1], [Define to 1 if the compiler is checking for lint.])
|
||||
AC_DEFINE([_FORTIFY_SOURCE], [2],
|
||||
[enable compile-time and run-time bounds-checking, and some warnings])
|
||||
AH_VERBATIM([FORTIFY_SOURCE],
|
||||
[/* Enable compile-time and run-time bounds-checking, and some warnings,
|
||||
without upsetting glibc 2.15+. */
|
||||
#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ \
|
||||
&& !defined __MINGW32__
|
||||
# define _FORTIFY_SOURCE 2
|
||||
#endif
|
||||
])
|
||||
AC_DEFINE([GNULIB_PORTCHECK], [1], [enable some gnulib portability checks])
|
||||
|
||||
# We use a slightly smaller set of warning options for lib/.
|
||||
# Remove the following and save the result in GNULIB_WARN_CFLAGS.
|
||||
nw=
|
||||
nw="$nw -Wunused-macros"
|
||||
nw="$nw -Wmissing-prototypes"
|
||||
nw="$nw -Wold-style-definition"
|
||||
nw="$nw -Wsuggest-attribute=pure"
|
||||
gl_MANYWARN_COMPLEMENT([GNULIB_WARN_CFLAGS], [$WARN_CFLAGS], [$nw])
|
||||
AC_SUBST([GNULIB_WARN_CFLAGS])
|
||||
fi
|
||||
|
||||
AC_C_INLINE
|
||||
AC_C_VARARRAYS
|
||||
|
||||
AC_DEFINE([DEFAULT_EDITOR_PROGRAM], ["ed"],
|
||||
[Name of editor program, unless overridden.])
|
||||
|
||||
AC_PATH_PROG([PR_PROGRAM], [pr], [""])
|
||||
AC_DEFINE_UNQUOTED([PR_PROGRAM], ["$PR_PROGRAM"], [Name of "pr" program.])
|
||||
|
||||
AC_CHECK_MEMBERS([struct stat.st_blksize])
|
||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_TYPE_PID_T
|
||||
|
||||
AC_CHECK_FUNCS_ONCE([sigaction sigprocmask strcasecoll stricoll])
|
||||
if test $ac_cv_func_sigprocmask = no; then
|
||||
AC_CHECK_FUNCS([sigblock])
|
||||
fi
|
||||
AC_FUNC_CLOSEDIR_VOID
|
||||
AC_FUNC_FORK
|
||||
|
||||
# When .tarball-version exists, we're building from a tarball
|
||||
# and must not make man/*.1 files depend on the generated src/version.c,
|
||||
# because that would induce a requirement to run the help2man perl script.
|
||||
# We are not yet prepared to make perl a build-from-tarball requirement.
|
||||
# because that would induce a requirement to run help2man.
|
||||
# We are not yet prepared to make help2man a build-from-tarball requirement.
|
||||
# Hence, here we detect .tarball-version existence. When not present,
|
||||
# we define a variable to be used in man/Makefile.am to induce the
|
||||
# proper dependency (so that man/*.1 will be rebuilt upon any version change),
|
||||
@ -164,7 +212,7 @@ test -f $srcdir/.tarball-version \
|
||||
&& SRC_VERSION_C= \
|
||||
|| SRC_VERSION_C=../src/version.c
|
||||
|
||||
AM_GNU_GETTEXT([external], [need-ngettext])
|
||||
AM_GNU_GETTEXT([external], [need-formatstring-macros])
|
||||
AM_GNU_GETTEXT_VERSION([0.19.2])
|
||||
XGETTEXT="AWK='$AWK' \$(SHELL) \$(top_srcdir)/exgettext $XGETTEXT"
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Makefile for GNU diffutils documentation.
|
||||
|
||||
# Copyright (C) 2001-2002, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2002, 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -15,6 +15,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
# The customization variable CHECK_NORMAL_MENU_STRUCTURE is necessary with
|
||||
# makeinfo versions ≥ 6.8.
|
||||
MAKEINFO = @MAKEINFO@ -c CHECK_NORMAL_MENU_STRUCTURE=1
|
||||
|
||||
AM_MAKEINFOFLAGS = --no-split
|
||||
|
||||
info_TEXINFOS = diffutils.texi
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
@setfilename diffutils.info
|
||||
@include version.texi
|
||||
@settitle Comparing and Merging Files
|
||||
@documentencoding UTF-8
|
||||
@set txicodequoteundirected
|
||||
@set txicodequotebacktick
|
||||
@syncodeindex vr cp
|
||||
@setchapternewpage odd
|
||||
@comment %**end of header
|
||||
@ -14,8 +17,8 @@ and documents the GNU @command{diff}, @command{diff3},
|
||||
differences between files and the GNU @command{patch} command for
|
||||
using their output to update files.
|
||||
|
||||
Copyright @copyright{} 1992-1994, 1998, 2001-2002, 2004, 2006, 2009-2017 Free
|
||||
Software Foundation, Inc.
|
||||
Copyright @copyright{} 1992--1994, 1998, 2001--2002, 2004, 2006, 2009--2026
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
@quotation
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
@ -235,6 +238,7 @@ need not read them to do its job.
|
||||
* Case Folding:: Suppressing differences in alphabetic case.
|
||||
* Brief:: Summarizing which files are different.
|
||||
* Binary:: Comparing binary files or forcing text comparisons.
|
||||
* Mutating Files:: Comparing files that are changing while being read.
|
||||
@end menu
|
||||
|
||||
@node Hunks
|
||||
@ -388,12 +392,42 @@ line against each regular expression.
|
||||
@section Suppressing Case Differences
|
||||
@cindex case difference suppression
|
||||
|
||||
GNU @command{diff} can treat lower case letters as
|
||||
equivalent to their upper case counterparts, so that, for example, it
|
||||
GNU @command{diff} can treat capital letters as
|
||||
equivalent to their small counterparts, so that, for example, it
|
||||
considers @samp{Funky Stuff}, @samp{funky STUFF}, and @samp{fUNKy
|
||||
stuFf} to all be the same. To request this, use the @option{-i} or
|
||||
@option{--ignore-case} option.
|
||||
|
||||
When ignoring case @command{diff} downcases each character before comparing it.
|
||||
For example, @samp{diff -i} downcases Greek capital ``Δ'' to small
|
||||
``δ'' before comparison. Although this works for many cases, it may
|
||||
have problems in some. For example:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The German word ``Straße'' is not treated like ``STRASSE'' as
|
||||
many Germans might expect, because the two words downcase to
|
||||
``straße'' and ``strasse'' respectively.
|
||||
|
||||
@item
|
||||
The Greek name ``Κως'' is not treated like ``ΚΩΣ''
|
||||
even though they both end in sigma,
|
||||
because the latter's trailing ``Σ'' (U+03A3 GREEK CAPITAL LETTER SIGMA)
|
||||
downcases to ``σ'' (U+03C3 GREEK SMALL LETTER SIGMA) instead
|
||||
of to ``ς'' (U+03C2 GREEK SMALL LETTER FINAL SIGMA).
|
||||
|
||||
@c pdftex cannot handle combining characters, alas.
|
||||
@ifnottex
|
||||
@item
|
||||
The ancient Greek phrase ``Πάντα ῥεῖ'' is not treated like ``ΠΆΝΤΑ ῬΕΙ͂'',
|
||||
as the former's trailing ``ῖ'' is the single character U+1FD6 (GREEK
|
||||
SMALL LETTER IOTA WITH PERISPOMENI) whereas the latter's trailing
|
||||
``Ι͂'' has two characters, U+0399 (GREEK CAPITAL LETTER IOTA) and
|
||||
U+0342 (COMBINING GREEK PERISPOMENI).
|
||||
@end ifnottex
|
||||
|
||||
@end itemize
|
||||
|
||||
@node Brief
|
||||
@section Summarizing Which Files Differ
|
||||
@cindex summarizing which files differ
|
||||
@ -497,6 +531,28 @@ text files, you can force @command{diff3} to consider all files to be text
|
||||
files and compare them line by line by using the @option{-a} or
|
||||
@option{--text} option.
|
||||
|
||||
@node Mutating Files
|
||||
@section Comparing Files While They Are Changing
|
||||
@cindex mutating files
|
||||
|
||||
Commands like @command{diff} read files and directories by using a
|
||||
series of system calls, as operating systems typically do not give a
|
||||
program a way to snapshot all its input before processing it.
|
||||
If input files change while being read, @command{diff} and related
|
||||
commands can generate output that does not correspond to any state of
|
||||
the input file system.
|
||||
|
||||
For example, if some other process truncates a file to zero size when
|
||||
@command{diff} has read just half the file, @command{diff} will use
|
||||
the half that it read as its input data. As another example, if a
|
||||
recursive @command{diff} determines that a file is a directory, and
|
||||
then some other process replaces the directory with a regular file,
|
||||
and then @command{diff} attempts to descend into the directory,
|
||||
@command{diff} will issue a diagnostic and fail.
|
||||
|
||||
In these situations the output of @command{diff} and related programs
|
||||
should be interpreted with some care.
|
||||
|
||||
@node Output Formats
|
||||
@chapter @command{diff} Output Formats
|
||||
@cindex output formats
|
||||
@ -766,8 +822,8 @@ or @option{-u} option.
|
||||
The argument @var{lines} is the number of lines of context to show.
|
||||
When it is not given, it defaults to three.
|
||||
|
||||
At present, only GNU @command{diff} can produce this format and
|
||||
only GNU @command{patch} can automatically apply diffs in this
|
||||
In the early 1990s, only GNU @command{diff} could produce this format and
|
||||
only GNU @command{patch} could automatically apply diffs in this
|
||||
format. For proper operation, @command{patch} typically needs at
|
||||
least three lines of context.
|
||||
|
||||
@ -1737,7 +1793,7 @@ output formats (@pxref{Output Formats}) @command{diff} cannot
|
||||
represent an incomplete line, so it pretends there was a newline and
|
||||
reports an error.
|
||||
|
||||
For example, suppose @file{F} and @file{G} are one-byte files that
|
||||
For example, suppose @file{F} and @file{G} are one-character files that
|
||||
contain just @samp{f} and @samp{g}, respectively. Then @samp{diff F G}
|
||||
outputs
|
||||
|
||||
@ -1796,8 +1852,7 @@ second position.) To do this, use the @option{--new-file}
|
||||
(@option{-N}) option. This option affects command-line arguments as
|
||||
well as files found via directory traversal; for example, @samp{diff
|
||||
-N a b} treats @file{a} as empty if @file{a} does not exist but
|
||||
@file{b} does, and similarly @samp{diff -N - b} treats standard input
|
||||
as empty if it is closed but @file{b} exists.
|
||||
@file{b} does.
|
||||
|
||||
If the older directory contains large files that are not in
|
||||
the newer directory, you can make the patch smaller by using the
|
||||
@ -1872,6 +1927,10 @@ These adjustments can be applied to any output format.
|
||||
@cindex tab stop alignment
|
||||
@cindex aligning tab stops
|
||||
|
||||
The tab character moves the cursor to the next tab stop.
|
||||
Tab stops are normally every 8 display columns;
|
||||
this can be altered by the @option{--tabsize=@var{columns}} option.
|
||||
|
||||
The lines of text in some of the @command{diff} output formats are
|
||||
preceded by one or two characters that indicate whether the text is
|
||||
inserted, deleted, or changed. The addition of those characters can
|
||||
@ -1884,9 +1943,7 @@ number of spaces before outputting them; select this method with the
|
||||
@option{--expand-tabs} (@option{-t}) option. To use this form of output with
|
||||
@command{patch}, you must give @command{patch} the @option{-l} or
|
||||
@option{--ignore-white-space} option (@pxref{Changed White Space}, for more
|
||||
information). @command{diff} normally assumes that tab stops are set
|
||||
every 8 print columns, but this can be altered by the
|
||||
@option{--tabsize=@var{columns}} option.
|
||||
information).
|
||||
|
||||
The other method for making tabs line up correctly is to add a tab
|
||||
character instead of a space after the indicator character at the
|
||||
@ -1899,6 +1956,15 @@ output format, which does not have a space character after the change
|
||||
type indicator character. Select this method with the @option{-T} or
|
||||
@option{--initial-tab} option.
|
||||
|
||||
GNU @command{diff} currently assumes that the output device respects tab stops,
|
||||
displays each character with column width as given by the operating system,
|
||||
and displays each encoding error byte in a single column.
|
||||
Unfortunately these assumptions are often incorrect
|
||||
for encoding errors and non-ASCII characters,
|
||||
so complex input data may not line up properly on output,
|
||||
and analysis based on the @option{--ignore-tab-expansion} (@option{-E}) option
|
||||
may differ from the display device's behavior.
|
||||
|
||||
@node Trailing Blanks
|
||||
@section Omitting trailing blanks
|
||||
@cindex trailing blanks
|
||||
@ -3544,9 +3610,12 @@ line word: @option{-bl} is equivalent to @option{-b -l}.
|
||||
@table @option
|
||||
@item -b
|
||||
@itemx --print-bytes
|
||||
Print the differing bytes. Display control bytes as a
|
||||
@samp{^} followed by a letter of the alphabet and precede bytes
|
||||
that have the high bit set with @samp{M-} (which stands for ``meta'').
|
||||
Print the differing bytes. Display each control byte as a @samp{^}
|
||||
followed by an ASCII letter, @samp{@@}, @samp{[}, @samp{\}, @samp{]},
|
||||
@samp{^} or @samp{_}. Precede each byte with the high bit set with
|
||||
@samp{M-}, which stands for ``meta''. A control byte is any byte
|
||||
with the high bit clear that does not represent a printable ASCII
|
||||
character including space.
|
||||
|
||||
@item --help
|
||||
Output a summary of usage and then exit.
|
||||
@ -3588,20 +3657,16 @@ In the above table, operands that are byte counts are normally
|
||||
decimal, but may be preceded by @samp{0} for octal and @samp{0x} for
|
||||
hexadecimal.
|
||||
|
||||
A byte count can be followed by a suffix to specify a multiple of that
|
||||
count; in this case an omitted integer is understood to be 1. A bare
|
||||
size letter, or one followed by @samp{iB}, specifies a multiple using
|
||||
powers of 1024. A size letter followed by @samp{B} specifies powers
|
||||
of 1000 instead. For example, @option{-n 4M} and @option{-n 4MiB} are
|
||||
equivalent to @option{-n 4194304}, whereas @option{-n 4MB} is
|
||||
equivalent to @option{-n 4000000}. This notation is upward compatible
|
||||
with the @uref{http://www.bipm.fr/enus/3_SI/si-prefixes.html, SI
|
||||
prefixes} for decimal multiples and with the
|
||||
@uref{http://physics.nist.gov/cuu/Units/binary.html, IEC 60027-2
|
||||
prefixes for binary multiples}.
|
||||
A byte count can be followed by a suffix to specify a
|
||||
multiple of the count. A bare letter,
|
||||
or one followed by @samp{iB}, specifies
|
||||
a multiple using powers of 1024. A letter followed by @samp{B}
|
||||
specifies powers of 1000 instead. For example, @samp{-n 1M} and
|
||||
@samp{-n 1MiB} are equivalent to @samp{-n 1048576}, whereas @samp{-n 1MB} is
|
||||
equivalent to @samp{-n 1000000}.
|
||||
|
||||
The following suffixes are defined. Large sizes like @code{1Y} may be
|
||||
rejected by your computer due to limitations of its arithmetic.
|
||||
The following suffixes are defined. Large sizes like @code{1Q}
|
||||
may be rejected by your computer due to limitations of its arithmetic.
|
||||
|
||||
@table @samp
|
||||
@item kB
|
||||
@ -3611,8 +3676,8 @@ kilobyte: @math{10^3 = 1000}.
|
||||
@itemx K
|
||||
@itemx KiB
|
||||
@cindex kibibyte, definition of
|
||||
kibibyte: @math{2^10 = 1024}. @samp{K} is special: the SI prefix is
|
||||
@samp{k} and the IEC 60027-2 prefix is @samp{Ki}, but tradition and
|
||||
kibibyte: @math{2^{10} = 1024}. @samp{K} is special: the SI prefix is
|
||||
@samp{k} and the ISO/IEC 80000-13 prefix is @samp{Ki}, but tradition and
|
||||
POSIX use @samp{k} to mean @samp{KiB}.
|
||||
@item MB
|
||||
@cindex megabyte, definition of
|
||||
@ -3620,49 +3685,59 @@ megabyte: @math{10^6 = 1,000,000}.
|
||||
@item M
|
||||
@itemx MiB
|
||||
@cindex mebibyte, definition of
|
||||
mebibyte: @math{2^20 = 1,048,576}.
|
||||
mebibyte: @math{2^{20} = 1,048,576}.
|
||||
@item GB
|
||||
@cindex gigabyte, definition of
|
||||
gigabyte: @math{10^9 = 1,000,000,000}.
|
||||
@item G
|
||||
@itemx GiB
|
||||
@cindex gibibyte, definition of
|
||||
gibibyte: @math{2^30 = 1,073,741,824}.
|
||||
gibibyte: @math{2^{30} = 1,073,741,824}.
|
||||
@item TB
|
||||
@cindex terabyte, definition of
|
||||
terabyte: @math{10^12 = 1,000,000,000,000}.
|
||||
terabyte: @math{10^{12} = 1,000,000,000,000}.
|
||||
@item T
|
||||
@itemx TiB
|
||||
@cindex tebibyte, definition of
|
||||
tebibyte: @math{2^40 = 1,099,511,627,776}.
|
||||
tebibyte: @math{2^{40} = 1,099,511,627,776}.
|
||||
@item PB
|
||||
@cindex petabyte, definition of
|
||||
petabyte: @math{10^15 = 1,000,000,000,000,000}.
|
||||
petabyte: @math{10^{15} = 1,000,000,000,000,000}.
|
||||
@item P
|
||||
@itemx PiB
|
||||
@cindex pebibyte, definition of
|
||||
pebibyte: @math{2^50 = 1,125,899,906,842,624}.
|
||||
pebibyte: @math{2^{50} = 1,125,899,906,842,624}.
|
||||
@item EB
|
||||
@cindex exabyte, definition of
|
||||
exabyte: @math{10^18 = 1,000,000,000,000,000,000}.
|
||||
exabyte: @math{10^{18} = 1,000,000,000,000,000,000}.
|
||||
@item E
|
||||
@itemx EiB
|
||||
@cindex exbibyte, definition of
|
||||
exbibyte: @math{2^60 = 1,152,921,504,606,846,976}.
|
||||
exbibyte: @math{2^{60} = 1,152,921,504,606,846,976}.
|
||||
@item ZB
|
||||
@cindex zettabyte, definition of
|
||||
zettabyte: @math{10^21 = 1,000,000,000,000,000,000,000}
|
||||
zettabyte: @math{10^{21} = 1,000,000,000,000,000,000,000}
|
||||
@item Z
|
||||
@itemx ZiB
|
||||
@math{2^70 = 1,180,591,620,717,411,303,424}.
|
||||
(@samp{Zi} is a GNU extension to IEC 60027-2.)
|
||||
zebibyte: @math{2^{70} = 1,180,591,620,717,411,303,424}.
|
||||
@item YB
|
||||
@cindex yottabyte, definition of
|
||||
yottabyte: @math{10^24 = 1,000,000,000,000,000,000,000,000}.
|
||||
yottabyte: @math{10^{24} = 1,000,000,000,000,000,000,000,000}.
|
||||
@item Y
|
||||
@itemx YiB
|
||||
@math{2^80 = 1,208,925,819,614,629,174,706,176}.
|
||||
(@samp{Yi} is a GNU extension to IEC 60027-2.)
|
||||
yobibyte: @math{2^{80} = 1,208,925,819,614,629,174,706,176}.
|
||||
@item RB
|
||||
@cindex ronnabyte, definition of
|
||||
ronnabyte: @math{10^{27} = 1,000,000,000,000,000,000,000,000,000}.
|
||||
@item R
|
||||
@itemx RiB
|
||||
robibyte: @math{2^{90} = 1,237,940,039,285,380,274,899,124,224}.
|
||||
@item QB
|
||||
@cindex quettabyte, definition of
|
||||
quettabyte: @math{10^{30} = 1,000,000,000,000,000,000,000,000,000,000}.
|
||||
@item Q
|
||||
@itemx QiB
|
||||
quebibyte: @math{2^{100} = 1,267,650,600,228,229,401,496,703,205,376}.
|
||||
@end table
|
||||
|
||||
@node Invoking diff
|
||||
@ -3941,7 +4016,7 @@ found. @xref{Comparing Directories}.
|
||||
|
||||
@item -s
|
||||
@itemx --report-identical-files
|
||||
Report when two files are the same. @xref{Comparing Directories}.
|
||||
Also report when two files are the same. @xref{Comparing Directories}.
|
||||
|
||||
@item -S @var{file}
|
||||
@itemx --starting-file=@var{file}
|
||||
@ -4595,39 +4670,37 @@ handle some of the existing formats. These shortcomings motivate the
|
||||
following suggested projects.
|
||||
|
||||
@menu
|
||||
* Internationalization:: Handling multibyte and varying-width characters.
|
||||
* Internationalization:: Handling multi-byte and varying-width characters.
|
||||
* Changing Structure:: Handling changes to the directory structure.
|
||||
* Special Files:: Handling symbolic links, device special files, etc.
|
||||
* Unusual File Names:: Handling file names that contain unusual characters.
|
||||
* Timestamp Order:: Outputting diffs in timestamp order.
|
||||
* Timestamp Order:: Outputting diffs in timestamp order.
|
||||
* Ignoring Changes:: Ignoring certain changes while showing others.
|
||||
* Speedups:: Improving performance.
|
||||
@end menu
|
||||
|
||||
@node Internationalization
|
||||
@subsection Handling Multibyte and Varying-Width Characters
|
||||
@cindex multibyte characters
|
||||
@subsection Handling Multi-byte and Varying-Width Characters
|
||||
@cindex multi-byte characters
|
||||
@cindex varying-width characters
|
||||
|
||||
@command{diff}, @command{diff3} and @command{sdiff} treat each line of
|
||||
input as a string of unibyte characters. This can mishandle multibyte
|
||||
characters in some cases. For example, when asked to ignore spaces,
|
||||
@command{diff} does not properly ignore a multibyte space character.
|
||||
input as a string of characters and encoding errors, where an
|
||||
@dfn{encoding error} is an input byte that is not part of any
|
||||
character. Single-byte and multi-byte characters are supported, along
|
||||
with common character encoding systems like UTF-8. The operating
|
||||
system's locale specifies the character encoding, and can be specified
|
||||
with the @env{LC_ALL} environment variable. You can find which
|
||||
locales are supported on your system by running the shell command
|
||||
@samp{locale -a}.
|
||||
|
||||
Also, @command{diff} currently assumes that each byte is one column
|
||||
wide, and this assumption is incorrect in some locales, e.g., locales
|
||||
that use UTF-8 encoding. This causes problems with the @option{-y} or
|
||||
@option{--side-by-side} option of @command{diff}.
|
||||
When counting columns for options like @option{--expand-tabs} (@option{-t}),
|
||||
@command{diff} consults the locale for the column width of each character,
|
||||
and assumes that each encoding error occupies a single column.
|
||||
|
||||
These problems need to be fixed without unduly affecting the
|
||||
performance of the utilities in unibyte environments.
|
||||
|
||||
The IBM GNU/Linux Technology Center Internationalization Team has
|
||||
proposed
|
||||
@uref{http://oss.software.ibm.com/developer/opensource/linux/patches/i18n/diffutils-2.7.2-i18n-0.1.patch.gz,patches
|
||||
to support internationalized @command{diff}}.
|
||||
Unfortunately, these patches are incomplete and are to an older
|
||||
version of @command{diff}, so more work needs to be done in this area.
|
||||
When ignoring case for @option{--ignore-case} (@option{-i}),
|
||||
@command{diff} downcases each character before comparing it,
|
||||
regardless of whether it is multi-byte. @xref{Case Folding}.
|
||||
|
||||
@node Changing Structure
|
||||
@subsection Handling Changes to the Directory Structure
|
||||
@ -4677,13 +4750,14 @@ and @command{patch} should be extended to understand these extensions.
|
||||
@subsection File Names that Contain Unusual Characters
|
||||
@cindex file names with unusual characters
|
||||
|
||||
When a file name contains an unusual character like a newline or
|
||||
white space, @samp{diff -r} generates a patch that @command{patch} cannot
|
||||
parse. The problem is with format of @command{diff} output, not just with
|
||||
@command{patch}, because with odd enough file names one can cause
|
||||
@command{diff} to generate a patch that is syntactically correct but
|
||||
patches the wrong files. The format of @command{diff} output should be
|
||||
extended to handle all possible file names.
|
||||
Since diffutils-3.3, file names have been encoded to eliminate the ambiguity
|
||||
of unusual characters like newline and TAB.
|
||||
However, since any file name containing a newline may easily cause trouble,
|
||||
in so many contexts (not just diff and patch), a future version of
|
||||
diff may well reject attempts to operate on such names.
|
||||
@c FIXME-in-2026 consider rejecting NL-afflicted file names
|
||||
@c keep an eye on POSIX, e.g.,
|
||||
@c https://pubs.opengroup.org/onlinepubs/9799919799/utilities/diff.html
|
||||
|
||||
@node Timestamp Order
|
||||
@subsection Outputting Diffs in Timestamp Order
|
||||
|
||||
506
doc/fdl.texi
Normal file
506
doc/fdl.texi
Normal file
@ -0,0 +1,506 @@
|
||||
@c The GNU Free Documentation License.
|
||||
@center Version 1.3, 3 November 2008
|
||||
|
||||
@c This file is intended to be included within another document,
|
||||
@c hence no sectioning command or @node.
|
||||
|
||||
@display
|
||||
Copyright @copyright{} 2000--2002, 2007--2008, 2022--2026 Free Software
|
||||
Foundation, Inc.
|
||||
@uref{https://fsf.org/}
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
@end display
|
||||
|
||||
@enumerate 0
|
||||
@item
|
||||
PREAMBLE
|
||||
|
||||
The purpose of this License is to make a manual, textbook, or other
|
||||
functional and useful document @dfn{free} in the sense of freedom: to
|
||||
assure everyone the effective freedom to copy and redistribute it,
|
||||
with or without modifying it, either commercially or noncommercially.
|
||||
Secondarily, this License preserves for the author and publisher a way
|
||||
to get credit for their work, while not being considered responsible
|
||||
for modifications made by others.
|
||||
|
||||
This License is a kind of ``copyleft'', which means that derivative
|
||||
works of the document must themselves be free in the same sense. It
|
||||
complements the GNU General Public License, which is a copyleft
|
||||
license designed for free software.
|
||||
|
||||
We have designed this License in order to use it for manuals for free
|
||||
software, because free software needs free documentation: a free
|
||||
program should come with manuals providing the same freedoms that the
|
||||
software does. But this License is not limited to software manuals;
|
||||
it can be used for any textual work, regardless of subject matter or
|
||||
whether it is published as a printed book. We recommend this License
|
||||
principally for works whose purpose is instruction or reference.
|
||||
|
||||
@item
|
||||
APPLICABILITY AND DEFINITIONS
|
||||
|
||||
This License applies to any manual or other work, in any medium, that
|
||||
contains a notice placed by the copyright holder saying it can be
|
||||
distributed under the terms of this License. Such a notice grants a
|
||||
world-wide, royalty-free license, unlimited in duration, to use that
|
||||
work under the conditions stated herein. The ``Document'', below,
|
||||
refers to any such manual or work. Any member of the public is a
|
||||
licensee, and is addressed as ``you''. You accept the license if you
|
||||
copy, modify or distribute the work in a way requiring permission
|
||||
under copyright law.
|
||||
|
||||
A ``Modified Version'' of the Document means any work containing the
|
||||
Document or a portion of it, either copied verbatim, or with
|
||||
modifications and/or translated into another language.
|
||||
|
||||
A ``Secondary Section'' is a named appendix or a front-matter section
|
||||
of the Document that deals exclusively with the relationship of the
|
||||
publishers or authors of the Document to the Document's overall
|
||||
subject (or to related matters) and contains nothing that could fall
|
||||
directly within that overall subject. (Thus, if the Document is in
|
||||
part a textbook of mathematics, a Secondary Section may not explain
|
||||
any mathematics.) The relationship could be a matter of historical
|
||||
connection with the subject or with related matters, or of legal,
|
||||
commercial, philosophical, ethical or political position regarding
|
||||
them.
|
||||
|
||||
The ``Invariant Sections'' are certain Secondary Sections whose titles
|
||||
are designated, as being those of Invariant Sections, in the notice
|
||||
that says that the Document is released under this License. If a
|
||||
section does not fit the above definition of Secondary then it is not
|
||||
allowed to be designated as Invariant. The Document may contain zero
|
||||
Invariant Sections. If the Document does not identify any Invariant
|
||||
Sections then there are none.
|
||||
|
||||
The ``Cover Texts'' are certain short passages of text that are listed,
|
||||
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
|
||||
the Document is released under this License. A Front-Cover Text may
|
||||
be at most 5 words, and a Back-Cover Text may be at most 25 words.
|
||||
|
||||
A ``Transparent'' copy of the Document means a machine-readable copy,
|
||||
represented in a format whose specification is available to the
|
||||
general public, that is suitable for revising the document
|
||||
straightforwardly with generic text editors or (for images composed of
|
||||
pixels) generic paint programs or (for drawings) some widely available
|
||||
drawing editor, and that is suitable for input to text formatters or
|
||||
for automatic translation to a variety of formats suitable for input
|
||||
to text formatters. A copy made in an otherwise Transparent file
|
||||
format whose markup, or absence of markup, has been arranged to thwart
|
||||
or discourage subsequent modification by readers is not Transparent.
|
||||
An image format is not Transparent if used for any substantial amount
|
||||
of text. A copy that is not ``Transparent'' is called ``Opaque''.
|
||||
|
||||
Examples of suitable formats for Transparent copies include plain
|
||||
ASCII without markup, Texinfo input format, La@TeX{} input
|
||||
format, SGML or XML using a publicly available
|
||||
DTD, and standard-conforming simple HTML,
|
||||
PostScript or PDF designed for human modification. Examples
|
||||
of transparent image formats include PNG, XCF and
|
||||
JPG@. Opaque formats include proprietary formats that can be
|
||||
read and edited only by proprietary word processors, SGML or
|
||||
XML for which the DTD and/or processing tools are
|
||||
not generally available, and the machine-generated HTML,
|
||||
PostScript or PDF produced by some word processors for
|
||||
output purposes only.
|
||||
|
||||
The ``Title Page'' means, for a printed book, the title page itself,
|
||||
plus such following pages as are needed to hold, legibly, the material
|
||||
this License requires to appear in the title page. For works in
|
||||
formats which do not have any title page as such, ``Title Page'' means
|
||||
the text near the most prominent appearance of the work's title,
|
||||
preceding the beginning of the body of the text.
|
||||
|
||||
The ``publisher'' means any person or entity that distributes copies
|
||||
of the Document to the public.
|
||||
|
||||
A section ``Entitled XYZ'' means a named subunit of the Document whose
|
||||
title either is precisely XYZ or contains XYZ in parentheses following
|
||||
text that translates XYZ in another language. (Here XYZ stands for a
|
||||
specific section name mentioned below, such as ``Acknowledgements'',
|
||||
``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title''
|
||||
of such a section when you modify the Document means that it remains a
|
||||
section ``Entitled XYZ'' according to this definition.
|
||||
|
||||
The Document may include Warranty Disclaimers next to the notice which
|
||||
states that this License applies to the Document. These Warranty
|
||||
Disclaimers are considered to be included by reference in this
|
||||
License, but only as regards disclaiming warranties: any other
|
||||
implication that these Warranty Disclaimers may have is void and has
|
||||
no effect on the meaning of this License.
|
||||
|
||||
@item
|
||||
VERBATIM COPYING
|
||||
|
||||
You may copy and distribute the Document in any medium, either
|
||||
commercially or noncommercially, provided that this License, the
|
||||
copyright notices, and the license notice saying this License applies
|
||||
to the Document are reproduced in all copies, and that you add no other
|
||||
conditions whatsoever to those of this License. You may not use
|
||||
technical measures to obstruct or control the reading or further
|
||||
copying of the copies you make or distribute. However, you may accept
|
||||
compensation in exchange for copies. If you distribute a large enough
|
||||
number of copies you must also follow the conditions in section 3.
|
||||
|
||||
You may also lend copies, under the same conditions stated above, and
|
||||
you may publicly display copies.
|
||||
|
||||
@item
|
||||
COPYING IN QUANTITY
|
||||
|
||||
If you publish printed copies (or copies in media that commonly have
|
||||
printed covers) of the Document, numbering more than 100, and the
|
||||
Document's license notice requires Cover Texts, you must enclose the
|
||||
copies in covers that carry, clearly and legibly, all these Cover
|
||||
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
|
||||
the back cover. Both covers must also clearly and legibly identify
|
||||
you as the publisher of these copies. The front cover must present
|
||||
the full title with all words of the title equally prominent and
|
||||
visible. You may add other material on the covers in addition.
|
||||
Copying with changes limited to the covers, as long as they preserve
|
||||
the title of the Document and satisfy these conditions, can be treated
|
||||
as verbatim copying in other respects.
|
||||
|
||||
If the required texts for either cover are too voluminous to fit
|
||||
legibly, you should put the first ones listed (as many as fit
|
||||
reasonably) on the actual cover, and continue the rest onto adjacent
|
||||
pages.
|
||||
|
||||
If you publish or distribute Opaque copies of the Document numbering
|
||||
more than 100, you must either include a machine-readable Transparent
|
||||
copy along with each Opaque copy, or state in or with each Opaque copy
|
||||
a computer-network location from which the general network-using
|
||||
public has access to download using public-standard network protocols
|
||||
a complete Transparent copy of the Document, free of added material.
|
||||
If you use the latter option, you must take reasonably prudent steps,
|
||||
when you begin distribution of Opaque copies in quantity, to ensure
|
||||
that this Transparent copy will remain thus accessible at the stated
|
||||
location until at least one year after the last time you distribute an
|
||||
Opaque copy (directly or through your agents or retailers) of that
|
||||
edition to the public.
|
||||
|
||||
It is requested, but not required, that you contact the authors of the
|
||||
Document well before redistributing any large number of copies, to give
|
||||
them a chance to provide you with an updated version of the Document.
|
||||
|
||||
@item
|
||||
MODIFICATIONS
|
||||
|
||||
You may copy and distribute a Modified Version of the Document under
|
||||
the conditions of sections 2 and 3 above, provided that you release
|
||||
the Modified Version under precisely this License, with the Modified
|
||||
Version filling the role of the Document, thus licensing distribution
|
||||
and modification of the Modified Version to whoever possesses a copy
|
||||
of it. In addition, you must do these things in the Modified Version:
|
||||
|
||||
@enumerate A
|
||||
@item
|
||||
Use in the Title Page (and on the covers, if any) a title distinct
|
||||
from that of the Document, and from those of previous versions
|
||||
(which should, if there were any, be listed in the History section
|
||||
of the Document). You may use the same title as a previous version
|
||||
if the original publisher of that version gives permission.
|
||||
|
||||
@item
|
||||
List on the Title Page, as authors, one or more persons or entities
|
||||
responsible for authorship of the modifications in the Modified
|
||||
Version, together with at least five of the principal authors of the
|
||||
Document (all of its principal authors, if it has fewer than five),
|
||||
unless they release you from this requirement.
|
||||
|
||||
@item
|
||||
State on the Title page the name of the publisher of the
|
||||
Modified Version, as the publisher.
|
||||
|
||||
@item
|
||||
Preserve all the copyright notices of the Document.
|
||||
|
||||
@item
|
||||
Add an appropriate copyright notice for your modifications
|
||||
adjacent to the other copyright notices.
|
||||
|
||||
@item
|
||||
Include, immediately after the copyright notices, a license notice
|
||||
giving the public permission to use the Modified Version under the
|
||||
terms of this License, in the form shown in the Addendum below.
|
||||
|
||||
@item
|
||||
Preserve in that license notice the full lists of Invariant Sections
|
||||
and required Cover Texts given in the Document's license notice.
|
||||
|
||||
@item
|
||||
Include an unaltered copy of this License.
|
||||
|
||||
@item
|
||||
Preserve the section Entitled ``History'', Preserve its Title, and add
|
||||
to it an item stating at least the title, year, new authors, and
|
||||
publisher of the Modified Version as given on the Title Page. If
|
||||
there is no section Entitled ``History'' in the Document, create one
|
||||
stating the title, year, authors, and publisher of the Document as
|
||||
given on its Title Page, then add an item describing the Modified
|
||||
Version as stated in the previous sentence.
|
||||
|
||||
@item
|
||||
Preserve the network location, if any, given in the Document for
|
||||
public access to a Transparent copy of the Document, and likewise
|
||||
the network locations given in the Document for previous versions
|
||||
it was based on. These may be placed in the ``History'' section.
|
||||
You may omit a network location for a work that was published at
|
||||
least four years before the Document itself, or if the original
|
||||
publisher of the version it refers to gives permission.
|
||||
|
||||
@item
|
||||
For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
|
||||
the Title of the section, and preserve in the section all the
|
||||
substance and tone of each of the contributor acknowledgements and/or
|
||||
dedications given therein.
|
||||
|
||||
@item
|
||||
Preserve all the Invariant Sections of the Document,
|
||||
unaltered in their text and in their titles. Section numbers
|
||||
or the equivalent are not considered part of the section titles.
|
||||
|
||||
@item
|
||||
Delete any section Entitled ``Endorsements''. Such a section
|
||||
may not be included in the Modified Version.
|
||||
|
||||
@item
|
||||
Do not retitle any existing section to be Entitled ``Endorsements'' or
|
||||
to conflict in title with any Invariant Section.
|
||||
|
||||
@item
|
||||
Preserve any Warranty Disclaimers.
|
||||
@end enumerate
|
||||
|
||||
If the Modified Version includes new front-matter sections or
|
||||
appendices that qualify as Secondary Sections and contain no material
|
||||
copied from the Document, you may at your option designate some or all
|
||||
of these sections as invariant. To do this, add their titles to the
|
||||
list of Invariant Sections in the Modified Version's license notice.
|
||||
These titles must be distinct from any other section titles.
|
||||
|
||||
You may add a section Entitled ``Endorsements'', provided it contains
|
||||
nothing but endorsements of your Modified Version by various
|
||||
parties---for example, statements of peer review or that the text has
|
||||
been approved by an organization as the authoritative definition of a
|
||||
standard.
|
||||
|
||||
You may add a passage of up to five words as a Front-Cover Text, and a
|
||||
passage of up to 25 words as a Back-Cover Text, to the end of the list
|
||||
of Cover Texts in the Modified Version. Only one passage of
|
||||
Front-Cover Text and one of Back-Cover Text may be added by (or
|
||||
through arrangements made by) any one entity. If the Document already
|
||||
includes a cover text for the same cover, previously added by you or
|
||||
by arrangement made by the same entity you are acting on behalf of,
|
||||
you may not add another; but you may replace the old one, on explicit
|
||||
permission from the previous publisher that added the old one.
|
||||
|
||||
The author(s) and publisher(s) of the Document do not by this License
|
||||
give permission to use their names for publicity for or to assert or
|
||||
imply endorsement of any Modified Version.
|
||||
|
||||
@item
|
||||
COMBINING DOCUMENTS
|
||||
|
||||
You may combine the Document with other documents released under this
|
||||
License, under the terms defined in section 4 above for modified
|
||||
versions, provided that you include in the combination all of the
|
||||
Invariant Sections of all of the original documents, unmodified, and
|
||||
list them all as Invariant Sections of your combined work in its
|
||||
license notice, and that you preserve all their Warranty Disclaimers.
|
||||
|
||||
The combined work need only contain one copy of this License, and
|
||||
multiple identical Invariant Sections may be replaced with a single
|
||||
copy. If there are multiple Invariant Sections with the same name but
|
||||
different contents, make the title of each such section unique by
|
||||
adding at the end of it, in parentheses, the name of the original
|
||||
author or publisher of that section if known, or else a unique number.
|
||||
Make the same adjustment to the section titles in the list of
|
||||
Invariant Sections in the license notice of the combined work.
|
||||
|
||||
In the combination, you must combine any sections Entitled ``History''
|
||||
in the various original documents, forming one section Entitled
|
||||
``History''; likewise combine any sections Entitled ``Acknowledgements'',
|
||||
and any sections Entitled ``Dedications''. You must delete all
|
||||
sections Entitled ``Endorsements.''
|
||||
|
||||
@item
|
||||
COLLECTIONS OF DOCUMENTS
|
||||
|
||||
You may make a collection consisting of the Document and other documents
|
||||
released under this License, and replace the individual copies of this
|
||||
License in the various documents with a single copy that is included in
|
||||
the collection, provided that you follow the rules of this License for
|
||||
verbatim copying of each of the documents in all other respects.
|
||||
|
||||
You may extract a single document from such a collection, and distribute
|
||||
it individually under this License, provided you insert a copy of this
|
||||
License into the extracted document, and follow this License in all
|
||||
other respects regarding verbatim copying of that document.
|
||||
|
||||
@item
|
||||
AGGREGATION WITH INDEPENDENT WORKS
|
||||
|
||||
A compilation of the Document or its derivatives with other separate
|
||||
and independent documents or works, in or on a volume of a storage or
|
||||
distribution medium, is called an ``aggregate'' if the copyright
|
||||
resulting from the compilation is not used to limit the legal rights
|
||||
of the compilation's users beyond what the individual works permit.
|
||||
When the Document is included in an aggregate, this License does not
|
||||
apply to the other works in the aggregate which are not themselves
|
||||
derivative works of the Document.
|
||||
|
||||
If the Cover Text requirement of section 3 is applicable to these
|
||||
copies of the Document, then if the Document is less than one half of
|
||||
the entire aggregate, the Document's Cover Texts may be placed on
|
||||
covers that bracket the Document within the aggregate, or the
|
||||
electronic equivalent of covers if the Document is in electronic form.
|
||||
Otherwise they must appear on printed covers that bracket the whole
|
||||
aggregate.
|
||||
|
||||
@item
|
||||
TRANSLATION
|
||||
|
||||
Translation is considered a kind of modification, so you may
|
||||
distribute translations of the Document under the terms of section 4.
|
||||
Replacing Invariant Sections with translations requires special
|
||||
permission from their copyright holders, but you may include
|
||||
translations of some or all Invariant Sections in addition to the
|
||||
original versions of these Invariant Sections. You may include a
|
||||
translation of this License, and all the license notices in the
|
||||
Document, and any Warranty Disclaimers, provided that you also include
|
||||
the original English version of this License and the original versions
|
||||
of those notices and disclaimers. In case of a disagreement between
|
||||
the translation and the original version of this License or a notice
|
||||
or disclaimer, the original version will prevail.
|
||||
|
||||
If a section in the Document is Entitled ``Acknowledgements'',
|
||||
``Dedications'', or ``History'', the requirement (section 4) to Preserve
|
||||
its Title (section 1) will typically require changing the actual
|
||||
title.
|
||||
|
||||
@item
|
||||
TERMINATION
|
||||
|
||||
You may not copy, modify, sublicense, or distribute the Document
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense, or distribute it is void, and
|
||||
will automatically terminate your rights under this License.
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally
|
||||
terminates your license, and (b) permanently, if the copyright holder
|
||||
fails to notify you of the violation by some reasonable means prior to
|
||||
60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, receipt of a copy of some or all of the same material does
|
||||
not give you any rights to use it.
|
||||
|
||||
@item
|
||||
FUTURE REVISIONS OF THIS LICENSE
|
||||
|
||||
The Free Software Foundation may publish new, revised versions
|
||||
of the GNU Free Documentation License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns. See
|
||||
@uref{https://www.gnu.org/licenses/}.
|
||||
|
||||
Each version of the License is given a distinguishing version number.
|
||||
If the Document specifies that a particular numbered version of this
|
||||
License ``or any later version'' applies to it, you have the option of
|
||||
following the terms and conditions either of that specified version or
|
||||
of any later version that has been published (not as a draft) by the
|
||||
Free Software Foundation. If the Document does not specify a version
|
||||
number of this License, you may choose any version ever published (not
|
||||
as a draft) by the Free Software Foundation. If the Document
|
||||
specifies that a proxy can decide which future versions of this
|
||||
License can be used, that proxy's public statement of acceptance of a
|
||||
version permanently authorizes you to choose that version for the
|
||||
Document.
|
||||
|
||||
@item
|
||||
RELICENSING
|
||||
|
||||
``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
|
||||
World Wide Web server that publishes copyrightable works and also
|
||||
provides prominent facilities for anybody to edit those works. A
|
||||
public wiki that anybody can edit is an example of such a server. A
|
||||
``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
|
||||
site means any set of copyrightable works thus published on the MMC
|
||||
site.
|
||||
|
||||
``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
|
||||
license published by Creative Commons Corporation, a not-for-profit
|
||||
corporation with a principal place of business in San Francisco,
|
||||
California, as well as future copyleft versions of that license
|
||||
published by that same organization.
|
||||
|
||||
``Incorporate'' means to publish or republish a Document, in whole or
|
||||
in part, as part of another Document.
|
||||
|
||||
An MMC is ``eligible for relicensing'' if it is licensed under this
|
||||
License, and if all works that were first published under this License
|
||||
somewhere other than this MMC, and subsequently incorporated in whole
|
||||
or in part into the MMC, (1) had no cover texts or invariant sections,
|
||||
and (2) were thus incorporated prior to November 1, 2008.
|
||||
|
||||
The operator of an MMC Site may republish an MMC contained in the site
|
||||
under CC-BY-SA on the same site at any time before August 1, 2009,
|
||||
provided the MMC is eligible for relicensing.
|
||||
|
||||
@end enumerate
|
||||
|
||||
@page
|
||||
@heading ADDENDUM: How to use this License for your documents
|
||||
|
||||
To use this License in a document you have written, include a copy of
|
||||
the License in the document and put the following copyright and
|
||||
license notices just after the title page:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
Copyright (C) @var{year} @var{your name}.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
|
||||
Texts. A copy of the license is included in the section entitled ``GNU
|
||||
Free Documentation License''.
|
||||
@end group
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
|
||||
replace the ``with@dots{}Texts.''@: line with this:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
with the Invariant Sections being @var{list their titles}, with
|
||||
the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
|
||||
being @var{list}.
|
||||
@end group
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections without Cover Texts, or some other
|
||||
combination of the three, merge those two alternatives to suit the
|
||||
situation.
|
||||
|
||||
If your document contains nontrivial examples of program code, we
|
||||
recommend releasing these examples in parallel under your choice of
|
||||
free software license, such as the GNU General Public License,
|
||||
to permit their use in free software.
|
||||
|
||||
@c Local Variables:
|
||||
@c ispell-local-pdict: "ispell-dict"
|
||||
@c End:
|
||||
@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
# Wrapper around gettext for programs using the msgid convention.
|
||||
|
||||
# Copyright (C) 1998, 2001, 2004, 2009-2013, 2015-2017 Free Software
|
||||
# Copyright (C) 1998, 2001, 2004, 2009-2013, 2015-2026 Free Software
|
||||
# Foundation, Inc.
|
||||
|
||||
# Written by Paul Eggert <eggert@twinsun.com>.
|
||||
@ -97,7 +97,7 @@ generate_emsgids='
|
||||
line = substr(line, percent_index + 2)
|
||||
bracket_index = index(line, "}")
|
||||
if (bracket_index == 0) {
|
||||
continue
|
||||
continue
|
||||
}
|
||||
msgid = substr(line, 1, bracket_index - 1)
|
||||
if (index(msgid, "%") != 0) {
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
diff --git a/lib/sh-quote.c b/lib/sh-quote.c
|
||||
index e349f40..788989a 100644
|
||||
--- a/lib/sh-quote.c
|
||||
+++ b/lib/sh-quote.c
|
||||
@@ -30,7 +30,7 @@ static struct quoting_options *sh_quoting_options;
|
||||
|
||||
/* Initializes the sh_quoting_options variable. */
|
||||
static void
|
||||
-init_sh_quoting_options ()
|
||||
+init_sh_quoting_options (void)
|
||||
{
|
||||
sh_quoting_options = clone_quoting_options (NULL);
|
||||
set_quoting_style (sh_quoting_options, shell_quoting_style);
|
||||
|
||||
--
|
||||
2
gnulib
2
gnulib
@ -1 +1 @@
|
||||
Subproject commit 809f19dba7cf85e4ea16246786cf19269590abf8
|
||||
Subproject commit 4f6ac2c3c689cd7312b5f9da97791b14bbc2ee53
|
||||
@ -1 +1,3 @@
|
||||
AM_CFLAGS =
|
||||
|
||||
include gnulib.mk
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Automakefile for GNU Diffutils library.
|
||||
|
||||
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2017 Free Software
|
||||
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free Software
|
||||
# Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@ -29,7 +29,7 @@ noinst_HEADERS =
|
||||
|
||||
include gnulib.mk
|
||||
|
||||
noinst_HEADERS += cmpbuf.h prepargs.h
|
||||
libdiffutils_a_SOURCES += cmpbuf.c prepargs.c
|
||||
noinst_HEADERS += cmpbuf.h diagnose.h
|
||||
libdiffutils_a_SOURCES += cmpbuf.c diagnose.c
|
||||
|
||||
AM_CFLAGS += $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
|
||||
|
||||
101
lib/cmpbuf.c
101
lib/cmpbuf.c
@ -1,6 +1,6 @@
|
||||
/* Buffer primitives for comparison operations.
|
||||
|
||||
Copyright (C) 1993, 1995, 1998, 2001-2002, 2006, 2009-2013, 2015-2017 Free
|
||||
Copyright (C) 1993, 1995, 1998, 2001-2002, 2006, 2009-2013, 2015-2026 Free
|
||||
Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -18,64 +18,77 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "cmpbuf.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdckdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include "cmpbuf.h"
|
||||
|
||||
#include "intprops.h"
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
# define SSIZE_MAX TYPE_MAXIMUM (ssize_t)
|
||||
#endif
|
||||
|
||||
#undef MIN
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
#include "minmax.h"
|
||||
|
||||
/* Read NBYTES bytes from descriptor FD into BUF.
|
||||
NBYTES must not be SIZE_MAX.
|
||||
Return the number of characters successfully read.
|
||||
On error, return SIZE_MAX, setting errno.
|
||||
On error, return -1, setting errno.
|
||||
The number returned is always NBYTES unless end-of-file or error. */
|
||||
|
||||
size_t
|
||||
block_read (int fd, char *buf, size_t nbytes)
|
||||
ptrdiff_t
|
||||
block_read (int fd, char *buf, idx_t nbytes)
|
||||
{
|
||||
char *bp = buf;
|
||||
char const *buflim = buf + nbytes;
|
||||
size_t readlim = MIN (SSIZE_MAX, SIZE_MAX);
|
||||
idx_t readlim = MIN (IDX_MAX, MIN (SSIZE_MAX, SIZE_MAX));
|
||||
|
||||
do
|
||||
{
|
||||
size_t bytes_remaining = buflim - bp;
|
||||
size_t bytes_to_read = MIN (bytes_remaining, readlim);
|
||||
idx_t bytes_remaining = buflim - bp;
|
||||
idx_t bytes_to_read = MIN (bytes_remaining, readlim);
|
||||
ssize_t nread = read (fd, bp, bytes_to_read);
|
||||
if (nread <= 0)
|
||||
{
|
||||
if (nread == 0)
|
||||
break;
|
||||
{
|
||||
if (nread == 0)
|
||||
break;
|
||||
|
||||
/* Accommodate Tru64 5.1, which can't read more than INT_MAX
|
||||
bytes at a time. They call that a 64-bit OS? */
|
||||
if (errno == EINVAL && INT_MAX < bytes_to_read)
|
||||
/* Accommodate FreeBSD 13, which can't read more than INT_MAX bytes
|
||||
when debug.iosize_max_clamp is nonzero. Prefer a power
|
||||
of two for the read limit in this case.
|
||||
|
||||
Also, work around a bug in Linux kernel 6.3.8 tmpfs,
|
||||
which fails if the current offset + bytes_to_read exceeds
|
||||
TYPE_MAXIMUM (off_t), even if EOF occurs before then. */
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
readlim = INT_MAX;
|
||||
continue;
|
||||
if (bytes_to_read <= 1)
|
||||
{
|
||||
if (lseek (fd, 0, SEEK_CUR) == TYPE_MAXIMUM (off_t))
|
||||
{
|
||||
nread = 0;
|
||||
break;
|
||||
}
|
||||
errno = EINVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
readlim = MIN (bytes_to_read >> 1, INT_MAX / 2 + 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is needed for programs that have signal handlers on
|
||||
older hosts without SA_RESTART. It also accommodates
|
||||
ancient AIX hosts that set errno to EINTR after uncaught
|
||||
SIGCONT. See <news:1r77ojINN85n@ftp.UU.NET>
|
||||
(1993-04-22). */
|
||||
if (! SA_RESTART && errno == EINTR)
|
||||
continue;
|
||||
/* This is needed for programs that have signal handlers on
|
||||
older hosts without SA_RESTART. It also accommodates
|
||||
ancient AIX hosts that set errno to EINTR after uncaught
|
||||
SIGCONT. See <news:1r77ojINN85n@ftp.UU.NET>
|
||||
(1993-04-22). */
|
||||
if (! SA_RESTART && errno == EINTR)
|
||||
continue;
|
||||
|
||||
return SIZE_MAX;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
bp += nread;
|
||||
}
|
||||
while (bp < buflim);
|
||||
@ -87,23 +100,23 @@ block_read (int fd, char *buf, size_t nbytes)
|
||||
either A or B is zero, or if the multiple is greater than LCM_MAX,
|
||||
return a reasonable buffer size. */
|
||||
|
||||
size_t
|
||||
buffer_lcm (size_t a, size_t b, size_t lcm_max)
|
||||
idx_t
|
||||
buffer_lcm (idx_t a, idx_t b, idx_t lcm_max)
|
||||
{
|
||||
size_t lcm, m, n, q, r;
|
||||
|
||||
/* Yield reasonable values if buffer sizes are zero. */
|
||||
if (!a)
|
||||
return b ? b : 8 * 1024;
|
||||
if (!b)
|
||||
/* Yield reasonable values if buffer sizes are zero. Although A and
|
||||
B must be nonnegative, GCC generates better code from (A <= 0)
|
||||
than from (A != 0). */
|
||||
if (a <= 0)
|
||||
return b <= 0 ? 8 * 1024 : b;
|
||||
if (b <= 0)
|
||||
return a;
|
||||
|
||||
/* n = gcd (a, b) */
|
||||
idx_t m, n, r;
|
||||
for (m = a, n = b; (r = m % n) != 0; m = n, n = r)
|
||||
continue;
|
||||
|
||||
/* Yield a if there is an overflow. */
|
||||
q = a / n;
|
||||
lcm = q * b;
|
||||
return lcm <= lcm_max && lcm / b == q ? lcm : a;
|
||||
idx_t q = a / n, lcm;
|
||||
return !ckd_mul (&lcm, b, q) && lcm <= lcm_max ? lcm : a;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* Buffer primitives for comparison operations.
|
||||
|
||||
Copyright (C) 2002, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
Copyright (C) 2002, 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -15,5 +15,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
size_t block_read (int, char *, size_t);
|
||||
size_t buffer_lcm (size_t, size_t, size_t) _GL_ATTRIBUTE_CONST;
|
||||
#include "idx.h"
|
||||
#include <stddef.h>
|
||||
ptrdiff_t block_read (int, char *, idx_t);
|
||||
idx_t buffer_lcm (idx_t, idx_t, idx_t) _GL_ATTRIBUTE_CONST;
|
||||
|
||||
52
lib/diagnose.c
Normal file
52
lib/diagnose.c
Normal file
@ -0,0 +1,52 @@
|
||||
/* Diagnostics for GNU diffutils
|
||||
|
||||
Copyright 2023-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "diagnose.h"
|
||||
|
||||
#include "gettext.h"
|
||||
#include "progname.h"
|
||||
#include "quotearg.h"
|
||||
|
||||
#include <error.h>
|
||||
|
||||
/* In slot N return NAME, quoted for the shell if NAME has unusual characters.
|
||||
This is for messages that historically did not quote names,
|
||||
where we want to keep the message unchanged for compatibility reasons
|
||||
unless the unusual characters might cause confusion.
|
||||
Also, POSIX requires that names be unquoted in some cases,
|
||||
and we want to respect that for POSIX portable filenames. */
|
||||
char *
|
||||
squote (int n, char const *name)
|
||||
{
|
||||
return quotearg_n_style (n, shell_escape_quoting_style, name);
|
||||
}
|
||||
|
||||
/* Issue help for the program. If REASON_MSGID, first issue a
|
||||
diagnostic with that reason and with optional operand OPERAND.
|
||||
Suggest --help regardless. */
|
||||
void
|
||||
try_help (char const *reason_msgid, char const *operand)
|
||||
{
|
||||
if (reason_msgid)
|
||||
error (0, 0, gettext (reason_msgid), operand);
|
||||
error (EXIT_TROUBLE, 0,
|
||||
gettext ("Try '%s --help' for more information."), program_name);
|
||||
}
|
||||
3
lib/diagnose.h
Normal file
3
lib/diagnose.h
Normal file
@ -0,0 +1,3 @@
|
||||
enum { EXIT_TROUBLE = 2 };
|
||||
char *squote (int, char const *);
|
||||
_Noreturn void try_help (char const *, char const *);
|
||||
@ -1,91 +0,0 @@
|
||||
/* Parse arguments from a string and prepend them to an argv.
|
||||
|
||||
Copyright (C) 1999-2002, 2006, 2009-2013, 2015-2017 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert <eggert@twinsun.com>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "prepargs.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <xalloc.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* IN_CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
|
||||
as an argument to <ctype.h> macros like "isspace". */
|
||||
#ifdef STDC_HEADERS
|
||||
# define IN_CTYPE_DOMAIN(c) 1
|
||||
#else
|
||||
# define IN_CTYPE_DOMAIN(c) ((c) <= 0177)
|
||||
#endif
|
||||
|
||||
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
|
||||
|
||||
/* Find the white-space-separated options specified by OPTIONS, and
|
||||
using BUF to store copies of these options, set ARGV[0], ARGV[1],
|
||||
etc. to the option copies. Return the number N of options found.
|
||||
Do not set ARGV[N]. If ARGV is zero, do not store ARGV[0] etc.
|
||||
Backslash can be used to escape whitespace (and backslashes). */
|
||||
static int
|
||||
prepend_args (char const *options, char *buf, char **argv)
|
||||
{
|
||||
char const *o = options;
|
||||
char *b = buf;
|
||||
int n = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while (ISSPACE ((unsigned char) *o))
|
||||
o++;
|
||||
if (!*o)
|
||||
return n;
|
||||
if (argv)
|
||||
argv[n] = b;
|
||||
n++;
|
||||
|
||||
do
|
||||
if ((*b++ = *o++) == '\\' && *o)
|
||||
b[-1] = *o++;
|
||||
while (*o && ! ISSPACE ((unsigned char) *o));
|
||||
|
||||
*b++ = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepend the whitespace-separated options in OPTIONS to the argument
|
||||
vector of a main program with argument count *PARGC and argument
|
||||
vector *PARGV. */
|
||||
void
|
||||
prepend_default_options (char const *options, int *pargc, char ***pargv)
|
||||
{
|
||||
if (options)
|
||||
{
|
||||
char *buf = xmalloc (strlen (options) + 1);
|
||||
int prepended = prepend_args (options, buf, (char **) 0);
|
||||
int argc = *pargc;
|
||||
char * const *argv = *pargv;
|
||||
char **pp = xmalloc ((prepended + argc + 1) * sizeof *pp);
|
||||
*pargc = prepended + argc;
|
||||
*pargv = pp;
|
||||
*pp++ = *argv++;
|
||||
pp += prepend_args (options, buf, pp);
|
||||
while ((*pp++ = *argv++))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
/* Parse arguments from a string and prepend them to an argv. */
|
||||
|
||||
void prepend_default_options (char const *, int *, char ***);
|
||||
@ -1,6 +1,6 @@
|
||||
# Automakefile for GNU diffutils man pages
|
||||
|
||||
# Copyright (C) 2002, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2002, 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -16,7 +16,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
dist_man1_MANS = cmp.1 diff.1 diff3.1 sdiff.1
|
||||
EXTRA_DIST = $(dist_man1_MANS:%.1=%.x) help2man
|
||||
EXTRA_DIST = $(dist_man1_MANS:%.1=%.x)
|
||||
MAINTAINERCLEANFILES = $(dist_man1_MANS)
|
||||
|
||||
S = $(top_srcdir)/src
|
||||
@ -29,11 +29,13 @@ sdiff.1: $S/sdiff.c sdiff.x
|
||||
# to ensure help2man invokes them via the use of PATH below.
|
||||
bin_dir = ../src
|
||||
|
||||
# Depend on the former to get version number changes.
|
||||
$(dist_man1_MANS): $(SRC_VERSION_C) help2man
|
||||
# Depend on $(SRC_VERSION_C) to get version number changes.
|
||||
$(dist_man1_MANS): $(SRC_VERSION_C)
|
||||
$(AM_V_GEN)base=`expr $@ : '\(.*\).1'` \
|
||||
&& test -x $(bin_dir)/$$base \
|
||||
&& (echo '[NAME]' && sed 's@/\* *@@; s/-/\\-/; q' $S/$$base.c) \
|
||||
&& (echo '[NAME]' \
|
||||
&& sed 's@/\* *@@; s/-/\\-/;s/^GNU //; q' $S/$$base.c) \
|
||||
| PATH="$(bin_dir)$(PATH_SEPARATOR)$$PATH" \
|
||||
$(srcdir)/help2man -i - -i $(srcdir)/$$base.x \
|
||||
-S '$(PACKAGE) $(VERSION)' $$base > $@-t && mv $@-t $@
|
||||
$(HELP2MAN) -i - -i $(srcdir)/$$base.x \
|
||||
-S '$(PACKAGE) $(VERSION)' $$base > $$base.1-t \
|
||||
&& mv $$base.1-t $(srcdir)/$$base.1
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
[SEE ALSO]
|
||||
diff(1), diff3(1), sdiff(1)
|
||||
.BR diff (1),
|
||||
.BR diff3 (1),
|
||||
.BR sdiff (1)
|
||||
|
||||
@ -1,2 +1,6 @@
|
||||
[SEE ALSO]
|
||||
wdiff(1), cmp(1), diff3(1), sdiff(1), patch(1)
|
||||
.BR wdiff (1),
|
||||
.BR cmp (1),
|
||||
.BR diff3 (1),
|
||||
.BR sdiff (1),
|
||||
.BR patch (1)
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
[SEE ALSO]
|
||||
cmp(1), diff(1), sdiff(1)
|
||||
.BR cmp (1),
|
||||
.BR diff (1),
|
||||
.BR sdiff (1)
|
||||
|
||||
671
man/help2man
671
man/help2man
@ -1,671 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# Generate a short man page from --help and --version output.
|
||||
# Copyright (C) 1997-2005, 2009-2011, 2013, 2015-2017 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
# Written by Brendan O'Dea <bod@debian.org>
|
||||
# Available from ftp://ftp.gnu.org/gnu/help2man/
|
||||
|
||||
use 5.008;
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use Text::Tabs qw(expand);
|
||||
use POSIX qw(strftime setlocale LC_ALL);
|
||||
|
||||
my $this_program = 'help2man';
|
||||
my $this_version = '1.40.4';
|
||||
|
||||
sub _ { $_[0] }
|
||||
sub configure_locale
|
||||
{
|
||||
my $locale = shift;
|
||||
die "$this_program: no locale support (Locale::gettext required)\n"
|
||||
unless $locale eq 'C';
|
||||
}
|
||||
|
||||
sub dec { $_[0] }
|
||||
sub enc { $_[0] }
|
||||
sub enc_user { $_[0] }
|
||||
sub kark { die +(sprintf shift, @_), "\n" }
|
||||
sub N_ { $_[0] }
|
||||
|
||||
my $version_info = enc_user sprintf _(<<'EOT'), $this_program, $this_version;
|
||||
GNU %s %s
|
||||
|
||||
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009, 2010,
|
||||
2011 Free Software Foundation, Inc.
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Written by Brendan O'Dea <bod@debian.org>
|
||||
EOT
|
||||
|
||||
my $help_info = enc_user sprintf _(<<'EOT'), $this_program, $this_program;
|
||||
'%s' generates a man page out of '--help' and '--version' output.
|
||||
|
||||
Usage: %s [OPTION]... EXECUTABLE
|
||||
|
||||
-n, --name=STRING description for the NAME paragraph
|
||||
-s, --section=SECTION section number for manual page (1, 6, 8)
|
||||
-m, --manual=TEXT name of manual (User Commands, ...)
|
||||
-S, --source=TEXT source of program (FSF, Debian, ...)
|
||||
-L, --locale=STRING select locale (default "C")
|
||||
-i, --include=FILE include material from 'FILE'
|
||||
-I, --opt-include=FILE include material from 'FILE' if it exists
|
||||
-o, --output=FILE send output to 'FILE'
|
||||
-p, --info-page=TEXT name of Texinfo manual
|
||||
-N, --no-info suppress pointer to Texinfo manual
|
||||
-l, --libtool exclude the 'lt-' from the program name
|
||||
--help print this help, then exit
|
||||
--version print version number, then exit
|
||||
|
||||
EXECUTABLE should accept '--help' and '--version' options and produce output on
|
||||
stdout although alternatives may be specified using:
|
||||
|
||||
-h, --help-option=STRING help option string
|
||||
-v, --version-option=STRING version option string
|
||||
--version-string=STRING version string
|
||||
--no-discard-stderr include stderr when parsing option output
|
||||
|
||||
Report bugs to <bug-help2man@gnu.org>.
|
||||
EOT
|
||||
|
||||
my $section = 1;
|
||||
my $manual = '';
|
||||
my $source = '';
|
||||
my $help_option = '--help';
|
||||
my $version_option = '--version';
|
||||
my $discard_stderr = 1;
|
||||
my ($opt_name, @opt_include, $opt_output, $opt_info, $opt_no_info, $opt_libtool,
|
||||
$version_text);
|
||||
|
||||
my %opt_def = (
|
||||
'n|name=s' => \$opt_name,
|
||||
's|section=s' => \$section,
|
||||
'm|manual=s' => \$manual,
|
||||
'S|source=s' => \$source,
|
||||
'L|locale=s' => sub { configure_locale pop },
|
||||
'i|include=s' => sub { push @opt_include, [ pop, 1 ] },
|
||||
'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] },
|
||||
'o|output=s' => \$opt_output,
|
||||
'p|info-page=s' => \$opt_info,
|
||||
'N|no-info' => \$opt_no_info,
|
||||
'l|libtool' => \$opt_libtool,
|
||||
'help' => sub { print $help_info; exit },
|
||||
'version' => sub { print $version_info; exit },
|
||||
'h|help-option=s' => \$help_option,
|
||||
'v|version-option=s' => \$version_option,
|
||||
'version-string=s' => \$version_text,
|
||||
'discard-stderr!' => \$discard_stderr,
|
||||
);
|
||||
|
||||
# Parse options.
|
||||
Getopt::Long::config('bundling');
|
||||
die $help_info unless GetOptions %opt_def and @ARGV == 1;
|
||||
|
||||
my %include = ();
|
||||
my %append = ();
|
||||
my @include = (); # retain order given in include file
|
||||
|
||||
# Process include file (if given). Format is:
|
||||
#
|
||||
# [section name]
|
||||
# verbatim text
|
||||
#
|
||||
# or
|
||||
#
|
||||
# /pattern/
|
||||
# verbatim text
|
||||
#
|
||||
|
||||
while (@opt_include)
|
||||
{
|
||||
my ($inc, $required) = @{shift @opt_include};
|
||||
|
||||
next unless -f $inc or $required;
|
||||
kark N_("%s: can't open '%s' (%s)"), $this_program, $inc, $!
|
||||
unless open INC, $inc;
|
||||
|
||||
my $key;
|
||||
my $hash = \%include;
|
||||
|
||||
while (<INC>)
|
||||
{
|
||||
# Convert input to internal Perl format, so that multibyte
|
||||
# sequences are treated as single characters.
|
||||
$_ = dec $_;
|
||||
|
||||
# [section]
|
||||
if (/^\[([^]]+)\]\s*$/)
|
||||
{
|
||||
$key = uc $1;
|
||||
$key =~ s/^\s+//;
|
||||
$key =~ s/\s+$//;
|
||||
$hash = \%include;
|
||||
push @include, $key unless $include{$key};
|
||||
next;
|
||||
}
|
||||
|
||||
# /pattern/
|
||||
if (m!^/(.*)/([ims]*)\s*$!)
|
||||
{
|
||||
my $pat = $2 ? "(?$2)$1" : $1;
|
||||
|
||||
# Check pattern.
|
||||
eval { $key = qr($pat) };
|
||||
if ($@)
|
||||
{
|
||||
$@ =~ s/ at .*? line \d.*//;
|
||||
die "$inc:$.:$@";
|
||||
}
|
||||
|
||||
$hash = \%append;
|
||||
next;
|
||||
}
|
||||
|
||||
# Check for options before the first section--anything else is
|
||||
# silently ignored, allowing the first for comments and
|
||||
# revision info.
|
||||
unless ($key)
|
||||
{
|
||||
# handle options
|
||||
if (/^-/)
|
||||
{
|
||||
local @ARGV = split;
|
||||
GetOptions %opt_def;
|
||||
}
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
$hash->{$key} ||= '';
|
||||
$hash->{$key} .= $_;
|
||||
}
|
||||
|
||||
close INC;
|
||||
|
||||
kark N_("%s: no valid information found in '%s'"), $this_program, $inc
|
||||
unless $key;
|
||||
}
|
||||
|
||||
# Compress trailing blank lines.
|
||||
for my $hash (\(%include, %append))
|
||||
{
|
||||
for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ }
|
||||
}
|
||||
|
||||
sub get_option_value;
|
||||
|
||||
# Grab help and version info from executable.
|
||||
my $help_text = get_option_value $ARGV[0], $help_option;
|
||||
$version_text ||= get_option_value $ARGV[0], $version_option;
|
||||
|
||||
# Translators: the following message is a strftime(3) format string, which in
|
||||
# the English version expands to the month as a word and the full year. It
|
||||
# is used on the footer of the generated manual pages. If in doubt, you may
|
||||
# just use %x as the value (which should be the full locale-specific date).
|
||||
my $date = enc strftime _("%B %Y"), localtime;
|
||||
(my $program = $ARGV[0]) =~ s!.*/!!;
|
||||
my $package = $program;
|
||||
my $version;
|
||||
|
||||
if ($opt_output)
|
||||
{
|
||||
unlink $opt_output or kark N_("%s: can't unlink %s (%s)"),
|
||||
$this_program, $opt_output, $! if -e $opt_output;
|
||||
|
||||
open STDOUT, ">$opt_output"
|
||||
or kark N_("%s: can't create %s (%s)"), $this_program, $opt_output, $!;
|
||||
}
|
||||
|
||||
# The first line of the --version information is assumed to be in one
|
||||
# of the following formats:
|
||||
#
|
||||
# <version>
|
||||
# <program> <version>
|
||||
# {GNU,Free} <program> <version>
|
||||
# <program> ({GNU,Free} <package>) <version>
|
||||
# <program> - {GNU,Free} <package> <version>
|
||||
#
|
||||
# and separated from any copyright/author details by a blank line.
|
||||
|
||||
($_, $version_text) = ((split /\n+/, $version_text, 2), '');
|
||||
|
||||
if (/^(\S+) +\(((?:GNU|Free) +[^)]+)\) +(.*)/ or
|
||||
/^(\S+) +- *((?:GNU|Free) +\S+) +(.*)/)
|
||||
{
|
||||
$program = $1;
|
||||
$package = $2;
|
||||
$version = $3;
|
||||
}
|
||||
elsif (/^((?:GNU|Free) +)?(\S+) +(.*)/)
|
||||
{
|
||||
$program = $2;
|
||||
$package = $1 ? "$1$2" : $2;
|
||||
$version = $3;
|
||||
}
|
||||
else
|
||||
{
|
||||
$version = $_;
|
||||
}
|
||||
|
||||
$program =~ s!.*/!!;
|
||||
|
||||
# No info for 'info' itself.
|
||||
$opt_no_info = 1 if $program eq 'info';
|
||||
|
||||
# Translators: "NAME", "SYNOPSIS" and other one or two word strings in all
|
||||
# upper case are manual page section headings. The man(1) manual page in your
|
||||
# language, if available should provide the conventional translations.
|
||||
for ($include{_('NAME')})
|
||||
{
|
||||
if ($opt_name) # --name overrides --include contents.
|
||||
{
|
||||
$_ = "$program \\- $opt_name\n";
|
||||
}
|
||||
elsif ($_) # Use first name given as $program
|
||||
{
|
||||
$program = $1 if /^([^\s,]+)(?:,?\s*[^\s,\\-]+)*\s+\\?-/;
|
||||
}
|
||||
else # Set a default (useless) NAME paragraph.
|
||||
{
|
||||
$_ = sprintf _("%s \\- manual page for %s %s") . "\n", $program,
|
||||
$program, $version;
|
||||
}
|
||||
}
|
||||
|
||||
# Man pages traditionally have the page title in caps.
|
||||
my $PROGRAM = uc $program;
|
||||
|
||||
# Set default page head/footers
|
||||
$source ||= "$program $version";
|
||||
unless ($manual)
|
||||
{
|
||||
for ($section)
|
||||
{
|
||||
if (/^(1[Mm]|8)/) { $manual = enc _('System Administration Utilities') }
|
||||
elsif (/^6/) { $manual = enc _('Games') }
|
||||
else { $manual = enc _('User Commands') }
|
||||
}
|
||||
}
|
||||
|
||||
# Extract usage clause(s) [if any] for SYNOPSIS.
|
||||
# Translators: "Usage" and "or" here are patterns (regular expressions) which
|
||||
# are used to match the usage synopsis in program output. An example from cp
|
||||
# (GNU coreutils) which contains both strings:
|
||||
# Usage: cp [OPTION]... [-T] SOURCE DEST
|
||||
# or: cp [OPTION]... SOURCE... DIRECTORY
|
||||
# or: cp [OPTION]... -t DIRECTORY SOURCE...
|
||||
my $PAT_USAGE = _('Usage');
|
||||
my $PAT_USAGE_CONT = _('or');
|
||||
if ($help_text =~ s/^($PAT_USAGE):( +(\S+))(.*)((?:\n(?: {6}\1| *($PAT_USAGE_CONT): +\S).*)*)//om)
|
||||
{
|
||||
my @syn = $3 . $4;
|
||||
|
||||
if ($_ = $5)
|
||||
{
|
||||
s/^\n//;
|
||||
for (split /\n/) { s/^ *(($PAT_USAGE_CONT): +)?//o; push @syn, $_ }
|
||||
}
|
||||
|
||||
my $synopsis = '';
|
||||
for (@syn)
|
||||
{
|
||||
$synopsis .= ".br\n" if $synopsis;
|
||||
s!^\S*/!!;
|
||||
s/^lt-// if $opt_libtool;
|
||||
s/^(\S+) *//;
|
||||
$synopsis .= ".B $1\n";
|
||||
s/\s+$//;
|
||||
s/(([][]|\.\.+)+)/\\fR$1\\fI/g;
|
||||
s/^/\\fI/ unless s/^\\fR//;
|
||||
$_ .= '\fR';
|
||||
s/(\\fI)( *)/$2$1/g;
|
||||
s/\\fI\\fR//g;
|
||||
s/^\\fR//;
|
||||
s/\\fI$//;
|
||||
s/^\./\\&./;
|
||||
|
||||
$synopsis .= "$_\n";
|
||||
}
|
||||
|
||||
$include{_('SYNOPSIS')} ||= $synopsis;
|
||||
}
|
||||
|
||||
# Process text, initial section is DESCRIPTION.
|
||||
my $sect = _('DESCRIPTION');
|
||||
$_ = "$help_text\n\n$version_text";
|
||||
|
||||
# Normalise paragraph breaks.
|
||||
s/^\n+//;
|
||||
s/\n*$/\n/;
|
||||
s/\n\n+/\n\n/g;
|
||||
|
||||
# Join hyphenated lines.
|
||||
s/([A-Za-z])-\n *([A-Za-z])/$1$2/g;
|
||||
|
||||
# Temporarily exchange leading dots, apostrophes and backslashes for
|
||||
# tokens.
|
||||
s/^\./\x80/mg;
|
||||
s/^'/\x81/mg;
|
||||
s/\\/\x82/g;
|
||||
|
||||
# Translators: patterns are used to match common program output. In the source
|
||||
# these strings are all of the form of "my $PAT_something = _('...');" and are
|
||||
# regular expressions. If there is more than one commonly used string, you
|
||||
# may separate alternatives with "|". Spaces in these expressions are written
|
||||
# as " +" to indicate that more than one space may be matched. The string
|
||||
# "(?:[\\w-]+ +)?" in the bug reporting pattern is used to indicate an
|
||||
# optional word, so that either "Report bugs" or "Report _program_ bugs" will
|
||||
# be matched.
|
||||
my $PAT_BUGS = _('Report +(?:[\w-]+ +)?bugs|Email +bug +reports +to');
|
||||
my $PAT_AUTHOR = _('Written +by');
|
||||
my $PAT_OPTIONS = _('Options');
|
||||
my $PAT_ENVIRONMENT = _('Environment');
|
||||
my $PAT_FILES = _('Files');
|
||||
my $PAT_EXAMPLES = _('Examples');
|
||||
my $PAT_FREE_SOFTWARE = _('This +is +free +software');
|
||||
|
||||
# Start a new paragraph (if required) for these.
|
||||
s/([^\n])\n($PAT_BUGS|$PAT_AUTHOR) /$1\n\n$2 /og;
|
||||
|
||||
# Convert iso-8859-1 copyright symbol or (c) to nroff
|
||||
# character.
|
||||
s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg;
|
||||
|
||||
sub convert_option;
|
||||
|
||||
while (length)
|
||||
{
|
||||
# Convert some standard paragraph names.
|
||||
if (s/^($PAT_OPTIONS): *\n//o)
|
||||
{
|
||||
$sect = _('OPTIONS');
|
||||
next;
|
||||
}
|
||||
if (s/^($PAT_ENVIRONMENT): *\n//o)
|
||||
{
|
||||
$sect = _('ENVIRONMENT');
|
||||
next;
|
||||
}
|
||||
if (s/^($PAT_FILES): *\n//o)
|
||||
{
|
||||
$sect = _('FILES');
|
||||
next;
|
||||
}
|
||||
elsif (s/^($PAT_EXAMPLES): *\n//o)
|
||||
{
|
||||
$sect = _('EXAMPLES');
|
||||
next;
|
||||
}
|
||||
|
||||
# Copyright section
|
||||
if (/^Copyright /)
|
||||
{
|
||||
$sect = _('COPYRIGHT');
|
||||
}
|
||||
|
||||
# Bug reporting section.
|
||||
elsif (/^($PAT_BUGS) /o)
|
||||
{
|
||||
$sect = _('REPORTING BUGS');
|
||||
}
|
||||
|
||||
# Author section.
|
||||
elsif (/^($PAT_AUTHOR)/o)
|
||||
{
|
||||
$sect = _('AUTHOR');
|
||||
}
|
||||
|
||||
# Examples, indicated by an indented leading $, % or > are
|
||||
# rendered in a constant width font.
|
||||
if (/^( +)([\$\%>] )\S/)
|
||||
{
|
||||
my $indent = $1;
|
||||
my $prefix = $2;
|
||||
my $break = '.IP';
|
||||
$include{$sect} ||= '';
|
||||
while (s/^$indent\Q$prefix\E(\S.*)\n*//)
|
||||
{
|
||||
$include{$sect} .= "$break\n\\f(CW$prefix$1\\fR\n";
|
||||
$break = '.br';
|
||||
}
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
my $matched = '';
|
||||
$include{$sect} ||= '';
|
||||
|
||||
# Sub-sections have a trailing colon and the second line indented.
|
||||
if (s/^(\S.*:) *\n / /)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$include{$sect} .= qq(.SS "$1"\n);
|
||||
}
|
||||
|
||||
my $indent = 0;
|
||||
my $content = '';
|
||||
|
||||
# Option with description.
|
||||
if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {20,}))(\S.*)\n//)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$indent = length ($4 || "$1$3");
|
||||
$content = ".TP\n\x84$2\n\x84$5\n";
|
||||
unless ($4)
|
||||
{
|
||||
# Indent may be different on second line.
|
||||
$indent = length $& if /^ {20,}/;
|
||||
}
|
||||
}
|
||||
|
||||
# Option without description.
|
||||
elsif (s/^ {1,10}([+-]\S.*)\n//)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$content = ".HP\n\x84$1\n";
|
||||
$indent = 80; # not continued
|
||||
}
|
||||
|
||||
# Indented paragraph with tag.
|
||||
elsif (s/^( +(\S.*?) +)(\S.*)\n//)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$indent = length $1;
|
||||
$content = ".TP\n\x84$2\n\x84$3\n";
|
||||
}
|
||||
|
||||
# Indented paragraph.
|
||||
elsif (s/^( +)(\S.*)\n//)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$indent = length $1;
|
||||
$content = ".IP\n\x84$2\n";
|
||||
}
|
||||
|
||||
# Left justified paragraph.
|
||||
else
|
||||
{
|
||||
s/(.*)\n//;
|
||||
$matched .= $& if %append;
|
||||
$content = ".PP\n" if $include{$sect};
|
||||
$content .= "$1\n";
|
||||
}
|
||||
|
||||
# Append continuations.
|
||||
while ($indent ? s/^ {$indent}(\S.*)\n// : s/^(\S.*)\n//)
|
||||
{
|
||||
$matched .= $& if %append;
|
||||
$content .= "\x84$1\n";
|
||||
}
|
||||
|
||||
# Move to next paragraph.
|
||||
s/^\n+//;
|
||||
|
||||
for ($content)
|
||||
{
|
||||
# Leading dot and apostrophe protection.
|
||||
s/\x84\./\x80/g;
|
||||
s/\x84'/\x81/g;
|
||||
s/\x84//g;
|
||||
|
||||
# Convert options.
|
||||
s/(^| |\()(-[][\w=-]+)/$1 . convert_option $2/mge;
|
||||
|
||||
# Escape remaining hyphens
|
||||
s/-/\x83/g;
|
||||
|
||||
if ($sect eq 'COPYRIGHT')
|
||||
{
|
||||
# Insert line breaks before additional copyright messages
|
||||
# and the disclaimer.
|
||||
s/\n(Copyright |$PAT_FREE_SOFTWARE)/\n.br\n$1/og;
|
||||
}
|
||||
elsif ($sect eq 'REPORTING BUGS')
|
||||
{
|
||||
# Handle multi-line bug reporting sections of the form:
|
||||
#
|
||||
# Report <program> bugs to <addr>
|
||||
# GNU <package> home page: <url>
|
||||
# ...
|
||||
s/\n([[:upper:]])/\n.br\n$1/g;
|
||||
}
|
||||
}
|
||||
|
||||
# Check if matched paragraph contains /pat/.
|
||||
if (%append)
|
||||
{
|
||||
for my $pat (keys %append)
|
||||
{
|
||||
if ($matched =~ $pat)
|
||||
{
|
||||
$content .= ".PP\n" unless $append{$pat} =~ /^\./;
|
||||
$content .= $append{$pat};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$include{$sect} .= $content;
|
||||
}
|
||||
|
||||
# Refer to the real documentation.
|
||||
unless ($opt_no_info)
|
||||
{
|
||||
my $info_page = $opt_info || $program;
|
||||
|
||||
$sect = _('SEE ALSO');
|
||||
$include{$sect} ||= '';
|
||||
$include{$sect} .= ".PP\n" if $include{$sect};
|
||||
$include{$sect} .= sprintf _(<<'EOT'), $program, $program, $info_page;
|
||||
The full documentation for
|
||||
.B %s
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B %s
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info %s
|
||||
.PP
|
||||
should give you access to the complete manual.
|
||||
EOT
|
||||
}
|
||||
|
||||
# Output header.
|
||||
print <<EOT;
|
||||
.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version.
|
||||
.TH $PROGRAM "$section" "$date" "$source" "$manual"
|
||||
EOT
|
||||
|
||||
# Section ordering.
|
||||
my @pre = (_('NAME'), _('SYNOPSIS'), _('DESCRIPTION'), _('OPTIONS'),
|
||||
_('ENVIRONMENT'), _('FILES'), _('EXAMPLES'));
|
||||
|
||||
my @post = (_('AUTHOR'), _('REPORTING BUGS'), _('COPYRIGHT'), _('SEE ALSO'));
|
||||
my $filter = join '|', @pre, @post;
|
||||
|
||||
# Output content.
|
||||
for my $sect (@pre, (grep ! /^($filter)$/o, @include), @post)
|
||||
{
|
||||
if ($include{$sect})
|
||||
{
|
||||
my $quote = $sect =~ /\W/ ? '"' : '';
|
||||
print enc ".SH $quote$sect$quote\n";
|
||||
|
||||
for ($include{$sect})
|
||||
{
|
||||
# Replace leading dot, apostrophe, backslash and hyphen
|
||||
# tokens.
|
||||
s/\x80/\\&./g;
|
||||
s/\x81/\\&'/g;
|
||||
s/\x82/\\e/g;
|
||||
s/\x83/\\-/g;
|
||||
|
||||
# Convert some latin1 chars to troff equivalents
|
||||
s/\xa0/\\ /g; # non-breaking space
|
||||
|
||||
print enc $_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close STDOUT or kark N_("%s: error writing to %s (%s)"), $this_program,
|
||||
$opt_output || 'stdout', $!;
|
||||
|
||||
exit;
|
||||
|
||||
# Call program with given option and return results.
|
||||
sub get_option_value
|
||||
{
|
||||
my ($prog, $opt) = @_;
|
||||
my $stderr = $discard_stderr ? '/dev/null' : '&1';
|
||||
my $value = join '',
|
||||
map { s/ +$//; expand $_ }
|
||||
map { dec $_ }
|
||||
`$prog $opt 2>$stderr`;
|
||||
|
||||
unless ($value)
|
||||
{
|
||||
my $err = N_("%s: can't get '%s' info from %s%s");
|
||||
my $extra = $discard_stderr
|
||||
? "\n" . N_("Try '--no-discard-stderr' if option outputs to stderr")
|
||||
: '';
|
||||
|
||||
kark $err, $this_program, $opt, $prog, $extra;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
# Convert option dashes to \- to stop nroff from hyphenating 'em, and
|
||||
# embolden. Option arguments get italicised.
|
||||
sub convert_option
|
||||
{
|
||||
local $_ = '\fB' . shift;
|
||||
|
||||
s/-/\x83/g;
|
||||
unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/)
|
||||
{
|
||||
s/=(.)/\\fR=\\fI$1/;
|
||||
s/ (.)/ \\fI$1/;
|
||||
$_ .= '\fR';
|
||||
}
|
||||
|
||||
$_;
|
||||
}
|
||||
@ -1,2 +1,4 @@
|
||||
[SEE ALSO]
|
||||
cmp(1), diff(1), diff3(1)
|
||||
.BR cmp (1),
|
||||
.BR diff (1),
|
||||
.BR diff3 (1)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# List of files that contain translatable strings.
|
||||
|
||||
# Copyright (C) 2001-2002, 2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2002, 2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -16,16 +16,19 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
lib/argmatch.c
|
||||
lib/argmatch.h
|
||||
lib/c-file-type.c
|
||||
lib/c-stack.c
|
||||
lib/diagnose.c
|
||||
lib/error.c
|
||||
lib/file-type.c
|
||||
lib/getopt.c
|
||||
lib/openat-die.c
|
||||
lib/quotearg.c
|
||||
lib/regcomp.c
|
||||
lib/version-etc.c
|
||||
lib/xalloc-die.c
|
||||
lib/xfreopen.c
|
||||
lib/xstrtol-error.c
|
||||
lib/version-etc.c
|
||||
lib/xstdopen.c
|
||||
|
||||
src/analyze.c
|
||||
src/cmp.c
|
||||
|
||||
30
po/en.po
30
po/en.po
@ -1,30 +0,0 @@
|
||||
# English messages for GNU diffutils
|
||||
# Copyright 1998, 2001-2003, 2009-2013, 2015-2017 Free Software Foundation,
|
||||
# Inc.
|
||||
# Paul Eggert <eggert@twinsun.com>, 1998
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GNU diffutils 3.2\n"
|
||||
"POT-Creation-Date: 2002-06-16 23:44-0700\n"
|
||||
"PO-Revision-Date: 2012-01-25 23:11-0700\n"
|
||||
"Last-Translator: Paul Eggert <eggert@cs.ucla.edu>\n"
|
||||
"Language-Team: English <en@translate.freefriends.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-1\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. TRANSLATORS: Please translate "(C)" to the C-in-a-circle symbol
|
||||
#. (U+00A9, COPYRIGHT SIGN) if possible, as this has some minor
|
||||
#. technical advantages in international copyright law. If the
|
||||
#. copyright symbol is not available, please leave it as "(C)".
|
||||
#: lib/version-etc.c:50
|
||||
msgid "(C)"
|
||||
msgstr "©"
|
||||
|
||||
#. TRANSLATORS: Please translate the second "o" in "Torbjorn Granlund"
|
||||
#. to an o-with-umlaut (U+00F6, LATIN SMALL LETTER O WITH DIAERESIS)
|
||||
#. if possible.
|
||||
#: src/cmp.c:47
|
||||
msgid "Written by Torbjorn Granlund and David MacKenzie."
|
||||
msgstr "Written by Torbjörn Granlund and David MacKenzie."
|
||||
@ -10,6 +10,6 @@
|
||||
# To enable this hook, rename this file to "applypatch-msg".
|
||||
|
||||
. git-sh-setup
|
||||
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
|
||||
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
|
||||
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||
:
|
||||
|
||||
@ -16,7 +16,7 @@ $editor = "vi" if $? != 0 or $editor =~ /^\s*\z/;
|
||||
my @valid = qw(
|
||||
diff diff3 cmp sdiff
|
||||
|
||||
gnulib tests maint doc build scripts
|
||||
all gnulib tests maint doc build scripts
|
||||
);
|
||||
my $v_or = join '|', @valid;
|
||||
my $valid_regex = qr/^(?:$v_or)$/;
|
||||
@ -79,7 +79,7 @@ sub check_msg($$)
|
||||
my ($log_file, $line_ref) = @_;
|
||||
|
||||
local *LOG;
|
||||
open LOG, '<', $log_file
|
||||
open LOG, '<:utf8', $log_file
|
||||
or return "failed to open for reading: $!";
|
||||
@$line_ref = <LOG>;
|
||||
close LOG;
|
||||
@ -109,21 +109,24 @@ sub check_msg($$)
|
||||
and return 'second line must be empty';
|
||||
|
||||
# Limit line length to allow for the ChangeLog's leading TAB.
|
||||
my $max_len = 72;
|
||||
foreach my $line (@line)
|
||||
{
|
||||
72 < length $line && $line =~ /^[^#]/
|
||||
and return 'line longer than 72';
|
||||
last if $line =~ '.*-{24} >8 -{24}$';
|
||||
my $len = length $line;
|
||||
$max_len < $len && $line =~ /^[^#]/
|
||||
and return "line length ($len) greater than than max: $max_len";
|
||||
}
|
||||
|
||||
my $buf = join ("\n", @line) . "\n";
|
||||
$buf =~ m!https?://bugzilla\.redhat\.com/show_bug\.cgi\?id=(\d+)!s
|
||||
and return "use shorter http://bugzilla.redhat.com/$1";
|
||||
and return "use shorter https://bugzilla.redhat.com/$1";
|
||||
|
||||
$buf =~ m!https?://debbugs\.gnu\.org/(?:cgi/bugreport\.cgi\?bug=)?(\d+)!s
|
||||
and return "use shorter http://bugs.gnu.org/$1";
|
||||
and return "use shorter https://bugs.gnu.org/$1";
|
||||
|
||||
$buf =~ /^ *Signed-off-by:/mi
|
||||
and return q(do not use "Signed-off-by:");
|
||||
$buf =~ m!https://lists\.gnu\.org/archive/html/!s
|
||||
and return "use '/r/' in place of '/archive/html/' in lists.gnu.org URLs";
|
||||
|
||||
return '';
|
||||
}
|
||||
@ -141,6 +144,7 @@ sub check_msg($$)
|
||||
$err eq ''
|
||||
and last;
|
||||
$err = "$ME: $err\n";
|
||||
-t STDOUT or die $err;
|
||||
warn $err;
|
||||
# Insert the diagnostic as a comment on the first line of $log_file.
|
||||
rewrite $log_file, $err, \@line;
|
||||
|
||||
@ -9,6 +9,6 @@
|
||||
# To enable this hook, rename this file to "pre-applypatch".
|
||||
|
||||
. git-sh-setup
|
||||
precommit="$(git rev-parse --git-path hooks/pre-commit)"
|
||||
test -x "$precommit" && exec "$precommit" ${1+"$@"}
|
||||
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
||||
:
|
||||
|
||||
@ -15,13 +15,13 @@ else
|
||||
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
fi
|
||||
|
||||
# If you want to allow non-ASCII filenames set this variable to true.
|
||||
allownonascii=$(git config --bool hooks.allownonascii)
|
||||
# If you want to allow non-ascii filenames set this variable to true.
|
||||
allownonascii=$(git config hooks.allownonascii)
|
||||
|
||||
# Redirect output to stderr.
|
||||
exec 1>&2
|
||||
|
||||
# Cross platform projects tend to avoid non-ASCII filenames; prevent
|
||||
# Cross platform projects tend to avoid non-ascii filenames; prevent
|
||||
# them from being added to the repository. We exploit the fact that the
|
||||
# printable range starts at the space character and ends with tilde.
|
||||
if [ "$allownonascii" != "true" ] &&
|
||||
@ -31,17 +31,18 @@ if [ "$allownonascii" != "true" ] &&
|
||||
test $(git diff --cached --name-only --diff-filter=A -z $against |
|
||||
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
|
||||
then
|
||||
cat <<\EOF
|
||||
Error: Attempt to add a non-ASCII file name.
|
||||
|
||||
This can cause problems if you want to work with people on other platforms.
|
||||
|
||||
To be portable it is advisable to rename the file.
|
||||
|
||||
If you know what you are doing you can disable this check using:
|
||||
|
||||
git config hooks.allownonascii true
|
||||
EOF
|
||||
echo "Error: Attempt to add a non-ascii file name."
|
||||
echo
|
||||
echo "This can cause problems if you want to work"
|
||||
echo "with people on other platforms."
|
||||
echo
|
||||
echo "To be portable it is advisable to rename the file ..."
|
||||
echo
|
||||
echo "If you know what you are doing you can disable this"
|
||||
echo "check using:"
|
||||
echo
|
||||
echo " git config hooks.allownonascii true"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Automakefile for GNU diffutils programs.
|
||||
|
||||
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2017 Free Software Foundation,
|
||||
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2026 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@ -26,31 +26,33 @@ AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
|
||||
LDADD = \
|
||||
libver.a \
|
||||
../lib/libdiffutils.a \
|
||||
$(CLOCK_TIME_LIB) \
|
||||
$(HARD_LOCALE_LIB) \
|
||||
$(LIBTHREAD) \
|
||||
$(LIBCSTACK) \
|
||||
$(LIBINTL) \
|
||||
$(LIBICONV) \
|
||||
$(LIBSIGSEGV) \
|
||||
$(LIB_CLOCK_GETTIME)
|
||||
$(LIBUNISTRING) \
|
||||
$(MBRTOWC_LIB) \
|
||||
$(LIBC32CONV) \
|
||||
$(SETLOCALE_NULL_LIB)
|
||||
|
||||
diff_LDADD = $(LDADD)
|
||||
cmp_LDADD = $(LDADD)
|
||||
sdiff_LDADD = $(LDADD)
|
||||
sdiff_LDADD = $(LDADD) $(GETRANDOM_LIB)
|
||||
diff3_LDADD = $(LDADD)
|
||||
|
||||
cmp_SOURCES = cmp.c
|
||||
diff3_SOURCES = diff3.c
|
||||
sdiff_SOURCES = sdiff.c
|
||||
cmp_SOURCES = cmp.c system.c
|
||||
diff3_SOURCES = diff3.c system.c
|
||||
sdiff_SOURCES = sdiff.c system.c
|
||||
diff_SOURCES = \
|
||||
analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \
|
||||
normal.c side.c util.c
|
||||
noinst_HEADERS = \
|
||||
die.h \
|
||||
diff.h \
|
||||
system.h
|
||||
normal.c side.c syncsig.c system.c util.c
|
||||
noinst_HEADERS = diff.h syncsig.h system.h
|
||||
|
||||
MOSTLYCLEANFILES = paths.h paths.ht
|
||||
|
||||
cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h
|
||||
cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h version.h
|
||||
|
||||
gdiff = `echo diff|sed '$(transform)'`
|
||||
BUILT_SOURCES = paths.h
|
||||
@ -65,6 +67,7 @@ BUILT_SOURCES += version.c
|
||||
version.c: Makefile
|
||||
$(AM_V_GEN)rm -f $@
|
||||
$(AM_V_at)printf '#include <config.h>\n' > $@t
|
||||
$(AM_V_at)printf '#include <version.h>\n' >> $@t
|
||||
$(AM_V_at)printf 'char const *Version = "$(PACKAGE_VERSION)";\n' >> $@t
|
||||
$(AM_V_at)chmod a-w $@t
|
||||
$(AM_V_at)mv $@t $@
|
||||
|
||||
777
src/analyze.c
777
src/analyze.c
File diff suppressed because it is too large
Load Diff
380
src/context.c
380
src/context.c
@ -1,7 +1,7 @@
|
||||
/* Context-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@ -19,11 +19,11 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "diff.h"
|
||||
#include "c-ctype.h"
|
||||
#include <c-ctype.h>
|
||||
#include <stat-time.h>
|
||||
#include <strftime.h>
|
||||
|
||||
static char const *find_function (char const * const *, lin);
|
||||
static char const *find_function (char const *const *, lin);
|
||||
static struct change *find_hunk (struct change *);
|
||||
static void mark_ignorable (struct change *);
|
||||
static void pr_context_hunk (struct change *);
|
||||
@ -39,40 +39,55 @@ static lin find_function_last_match;
|
||||
|
||||
static void
|
||||
print_context_label (char const *mark,
|
||||
struct file_data *inf,
|
||||
char const *name,
|
||||
char const *label)
|
||||
struct file_data *inf,
|
||||
char const *name,
|
||||
char const *label)
|
||||
{
|
||||
set_color_context (HEADER_CONTEXT);
|
||||
if (label)
|
||||
fprintf (outfile, "%s %s\n", mark, label);
|
||||
fprintf (outfile, "%s %s", mark, label);
|
||||
else
|
||||
{
|
||||
char buf[MAX (INT_STRLEN_BOUND (int) + 32,
|
||||
INT_STRLEN_BOUND (time_t) + 11)];
|
||||
struct tm const *tm = localtime (&inf->stat.st_mtime);
|
||||
int nsec = get_stat_mtime_ns (&inf->stat);
|
||||
if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
|
||||
/* POSIX requires current time for stdin. */
|
||||
struct timespec ts;
|
||||
if (inf->desc == STDIN_FILENO)
|
||||
{
|
||||
verify (TYPE_IS_INTEGER (time_t));
|
||||
if (LONG_MIN <= TYPE_MINIMUM (time_t)
|
||||
&& TYPE_MAXIMUM (time_t) <= LONG_MAX)
|
||||
{
|
||||
long int sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%ld.%.9d", sec, nsec);
|
||||
}
|
||||
else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX)
|
||||
{
|
||||
intmax_t sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
|
||||
}
|
||||
static struct timespec now;
|
||||
if (!now.tv_sec)
|
||||
timespec_get (&now, TIME_UTC);
|
||||
ts = now;
|
||||
}
|
||||
else
|
||||
ts = get_stat_mtime (&inf->stat);
|
||||
|
||||
/* Buffer for nstftime output, big enough to handle any
|
||||
timestamp formatted according to time_format.
|
||||
Its size is an upper bound for the format "%Y-%m-%d %H:%M:%S.%N %z",
|
||||
with an int for year and a time_t for time zone hour.
|
||||
The format "%Y-%m-%d %H:%M:%S %z" generates fewer bytes,
|
||||
and although the format "%a %b %e %T %Y" could in theory
|
||||
generate more bytes in practice it never does. */
|
||||
char buf[INT_STRLEN_BOUND (int) + INT_STRLEN_BOUND (time_t)
|
||||
+ sizeof "-%m-%d %H:%M:%S.000000000 +00"];
|
||||
|
||||
struct tm const *tm = localtime (&ts.tv_sec);
|
||||
int nsec = ts.tv_nsec;
|
||||
if (tm
|
||||
&& 0 <= nstrftime (buf, sizeof buf, time_format, tm, localtz, nsec))
|
||||
fprintf (outfile, "%s %s\t%s", mark, name, buf);
|
||||
else if (TYPE_SIGNED (time_t))
|
||||
{
|
||||
intmax_t sec = inf->stat.st_mtime;
|
||||
fprintf (outfile, "%s %s\t%"PRIdMAX".%.9d", mark, name, sec, nsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t sec = inf->stat.st_mtime;
|
||||
fprintf (outfile, "%s %s\t%"PRIuMAX".%.9d", mark, name, sec, nsec);
|
||||
}
|
||||
fprintf (outfile, "%s %s\t%s\n", mark, name, buf);
|
||||
}
|
||||
set_color_context (RESET_CONTEXT);
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
|
||||
/* Print a header for a context diff, with the file names and dates. */
|
||||
@ -80,7 +95,6 @@ print_context_label (char const *mark,
|
||||
void
|
||||
print_context_header (struct file_data inf[], char const *const *names, bool unidiff)
|
||||
{
|
||||
set_color_context (HEADER_CONTEXT);
|
||||
if (unidiff)
|
||||
{
|
||||
print_context_label ("---", &inf[0], names[0], file_label[0]);
|
||||
@ -91,7 +105,6 @@ print_context_header (struct file_data inf[], char const *const *names, bool uni
|
||||
print_context_label ("***", &inf[0], names[0], file_label[0]);
|
||||
print_context_label ("---", &inf[1], names[1], file_label[1]);
|
||||
}
|
||||
set_color_context (RESET_CONTEXT);
|
||||
}
|
||||
|
||||
/* Print an edit script in context format. */
|
||||
@ -102,13 +115,10 @@ print_context_script (struct change *script, bool unidiff)
|
||||
if (ignore_blank_lines || ignore_regexp.fastmap)
|
||||
mark_ignorable (script);
|
||||
else
|
||||
{
|
||||
struct change *e;
|
||||
for (e = script; e; e = e->link)
|
||||
e->ignore = false;
|
||||
}
|
||||
for (struct change *e = script; e; e = e->link)
|
||||
e->ignore = false;
|
||||
|
||||
find_function_last_search = - files[0].prefix_lines;
|
||||
find_function_last_search = - curr.file[0].prefix_lines;
|
||||
find_function_last_match = LIN_MAX;
|
||||
|
||||
if (unidiff)
|
||||
@ -126,7 +136,7 @@ print_context_script (struct change *script, bool unidiff)
|
||||
static void
|
||||
print_context_number_range (struct file_data const *file, lin a, lin b)
|
||||
{
|
||||
printint trans_a, trans_b;
|
||||
lin trans_a, trans_b;
|
||||
translate_range (file, a, b, &trans_a, &trans_b);
|
||||
|
||||
/* We can have B <= A in the case of a range of no lines.
|
||||
@ -169,38 +179,33 @@ print_context_function (FILE *out, char const *function)
|
||||
static void
|
||||
pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1, i;
|
||||
char const *prefix;
|
||||
char const *function;
|
||||
FILE *out;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (! changes)
|
||||
return;
|
||||
|
||||
/* Include a context's width before and after. */
|
||||
|
||||
i = - files[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, i);
|
||||
first1 = MAX (first1 - context, i);
|
||||
if (last0 < files[0].valid_lines - context)
|
||||
lin minus_prefix_lines = - curr.file[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, minus_prefix_lines);
|
||||
first1 = MAX (first1 - context, minus_prefix_lines);
|
||||
if (last0 < curr.file[0].valid_lines - context)
|
||||
last0 += context;
|
||||
else
|
||||
last0 = files[0].valid_lines - 1;
|
||||
if (last1 < files[1].valid_lines - context)
|
||||
last0 = curr.file[0].valid_lines - 1;
|
||||
if (last1 < curr.file[1].valid_lines - context)
|
||||
last1 += context;
|
||||
else
|
||||
last1 = files[1].valid_lines - 1;
|
||||
last1 = curr.file[1].valid_lines - 1;
|
||||
|
||||
/* If desired, find the preceding function definition line in file 0. */
|
||||
function = NULL;
|
||||
char const *function = nullptr;
|
||||
if (function_regexp.fastmap)
|
||||
function = find_function (files[0].linbuf, first0);
|
||||
function = find_function (curr.file[0].linbuf, first0);
|
||||
|
||||
begin_output ();
|
||||
out = outfile;
|
||||
FILE *out = outfile;
|
||||
|
||||
fputs ("***************", out);
|
||||
|
||||
@ -210,7 +215,7 @@ pr_context_hunk (struct change *hunk)
|
||||
putc ('\n', out);
|
||||
set_color_context (LINE_NUMBER_CONTEXT);
|
||||
fputs ("*** ", out);
|
||||
print_context_number_range (&files[0], first0, last0);
|
||||
print_context_number_range (&curr.file[0], first0, last0);
|
||||
fputs (" ****", out);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
putc ('\n', out);
|
||||
@ -219,38 +224,36 @@ pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
struct change *next = hunk;
|
||||
|
||||
if (first0 <= last0)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
for (lin i = first0; i <= last0; i++)
|
||||
{
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
|
||||
for (i = first0; i <= last0; i++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 0)
|
||||
only to lines before line I. */
|
||||
/* Skip past changes that apply (in file 0)
|
||||
only to lines before line I. */
|
||||
|
||||
while (next && next->line0 + next->deleted <= i)
|
||||
next = next->link;
|
||||
while (next && next->line0 + next->deleted <= i)
|
||||
next = next->link;
|
||||
|
||||
/* Compute the marking for line I. */
|
||||
/* Compute the marking for line I. */
|
||||
|
||||
prefix = " ";
|
||||
if (next && next->line0 <= i)
|
||||
char const *prefix = " ";
|
||||
if (next && next->line0 <= i)
|
||||
{
|
||||
/* The change NEXT covers this line.
|
||||
If lines were inserted here in file 1, this is "changed".
|
||||
Otherwise it is "deleted". */
|
||||
prefix = (next->inserted > 0 ? "!" : "-");
|
||||
}
|
||||
print_1_line_nl (prefix, &files[0].linbuf[i], true);
|
||||
if (i == last0)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[0].linbuf[i + 1][-1] == '\n')
|
||||
print_1_line_nl (prefix, &curr.file[0].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[0].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_color_context (LINE_NUMBER_CONTEXT);
|
||||
fputs ("--- ", out);
|
||||
print_context_number_range (&files[1], first1, last1);
|
||||
print_context_number_range (&curr.file[1], first1, last1);
|
||||
fputs (" ----", out);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
putc ('\n', out);
|
||||
@ -259,33 +262,31 @@ pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
struct change *next = hunk;
|
||||
|
||||
if (first1 <= last1)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
for (lin i = first1; i <= last1; i++)
|
||||
{
|
||||
set_color_context (ADD_CONTEXT);
|
||||
|
||||
for (i = first1; i <= last1; i++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 1)
|
||||
only to lines before line I. */
|
||||
/* Skip past changes that apply (in file 1)
|
||||
only to lines before line I. */
|
||||
|
||||
while (next && next->line1 + next->inserted <= i)
|
||||
next = next->link;
|
||||
while (next && next->line1 + next->inserted <= i)
|
||||
next = next->link;
|
||||
|
||||
/* Compute the marking for line I. */
|
||||
/* Compute the marking for line I. */
|
||||
|
||||
prefix = " ";
|
||||
if (next && next->line1 <= i)
|
||||
char const *prefix = " ";
|
||||
if (next && next->line1 <= i)
|
||||
{
|
||||
/* The change NEXT covers this line.
|
||||
If lines were deleted here in file 0, this is "changed".
|
||||
Otherwise it is "inserted". */
|
||||
prefix = (next->deleted > 0 ? "!" : "+");
|
||||
}
|
||||
print_1_line_nl (prefix, &files[1].linbuf[i], true);
|
||||
if (i == last1)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[1].linbuf[i + 1][-1] == '\n')
|
||||
print_1_line_nl (prefix, &curr.file[1].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[1].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +300,7 @@ pr_context_hunk (struct change *hunk)
|
||||
static void
|
||||
print_unidiff_number_range (struct file_data const *file, lin a, lin b)
|
||||
{
|
||||
printint trans_a, trans_b;
|
||||
lin trans_a, trans_b;
|
||||
translate_range (file, a, b, &trans_a, &trans_b);
|
||||
|
||||
/* We can have B < A in the case of a range of no lines.
|
||||
@ -322,44 +323,38 @@ print_unidiff_number_range (struct file_data const *file, lin a, lin b)
|
||||
static void
|
||||
pr_unidiff_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
lin i, j, k;
|
||||
struct change *next;
|
||||
char const *function;
|
||||
FILE *out;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
|
||||
lin first0, last0, first1, last1;
|
||||
if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
|
||||
return;
|
||||
|
||||
/* Include a context's width before and after. */
|
||||
|
||||
i = - files[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, i);
|
||||
first1 = MAX (first1 - context, i);
|
||||
if (last0 < files[0].valid_lines - context)
|
||||
lin minus_prefix_lines = - curr.file[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, minus_prefix_lines);
|
||||
first1 = MAX (first1 - context, minus_prefix_lines);
|
||||
if (last0 < curr.file[0].valid_lines - context)
|
||||
last0 += context;
|
||||
else
|
||||
last0 = files[0].valid_lines - 1;
|
||||
if (last1 < files[1].valid_lines - context)
|
||||
last0 = curr.file[0].valid_lines - 1;
|
||||
if (last1 < curr.file[1].valid_lines - context)
|
||||
last1 += context;
|
||||
else
|
||||
last1 = files[1].valid_lines - 1;
|
||||
last1 = curr.file[1].valid_lines - 1;
|
||||
|
||||
/* If desired, find the preceding function definition line in file 0. */
|
||||
function = NULL;
|
||||
char const *function = nullptr;
|
||||
if (function_regexp.fastmap)
|
||||
function = find_function (files[0].linbuf, first0);
|
||||
function = find_function (curr.file[0].linbuf, first0);
|
||||
|
||||
begin_output ();
|
||||
out = outfile;
|
||||
FILE *out = outfile;
|
||||
|
||||
set_color_context (LINE_NUMBER_CONTEXT);
|
||||
fputs ("@@ -", out);
|
||||
print_unidiff_number_range (&files[0], first0, last0);
|
||||
print_unidiff_number_range (&curr.file[0], first0, last0);
|
||||
fputs (" +", out);
|
||||
print_unidiff_number_range (&files[1], first1, last1);
|
||||
print_unidiff_number_range (&curr.file[1], first1, last1);
|
||||
fputs (" @@", out);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
@ -368,9 +363,9 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
|
||||
putc ('\n', out);
|
||||
|
||||
next = hunk;
|
||||
i = first0;
|
||||
j = first1;
|
||||
struct change *next = hunk;
|
||||
lin i = first0;
|
||||
lin j = first1;
|
||||
|
||||
while (i <= last0 || j <= last1)
|
||||
{
|
||||
@ -378,61 +373,57 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
/* If the line isn't a difference, output the context from file 0. */
|
||||
|
||||
if (!next || i < next->line0)
|
||||
{
|
||||
char const *const *line = &files[0].linbuf[i++];
|
||||
if (! (suppress_blank_empty && **line == '\n'))
|
||||
putc (initial_tab ? '\t' : ' ', out);
|
||||
print_1_line (NULL, line);
|
||||
j++;
|
||||
}
|
||||
{
|
||||
char const *const *line = &curr.file[0].linbuf[i++];
|
||||
if (! (suppress_blank_empty && **line == '\n'))
|
||||
putc (initial_tab ? '\t' : ' ', out);
|
||||
print_1_line (nullptr, line);
|
||||
j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For each difference, first output the deleted part. */
|
||||
{
|
||||
/* For each difference, first output the deleted part. */
|
||||
|
||||
k = next->deleted;
|
||||
if (k)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const * const *line = &files[0].linbuf[i++];
|
||||
putc ('-', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (NULL, line, true);
|
||||
|
||||
if (!k)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
|
||||
/* Then output the inserted part. */
|
||||
|
||||
k = next->inserted;
|
||||
if (k)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
lin k = next->deleted;
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const * const *line = &files[1].linbuf[j++];
|
||||
putc ('+', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (NULL, line, true);
|
||||
{
|
||||
char const *const *line = &curr.file[0].linbuf[i++];
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
putc ('-', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (nullptr, line, true);
|
||||
|
||||
if (!k)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
}
|
||||
|
||||
/* We're done with this hunk, so on to the next! */
|
||||
/* Then output the inserted part. */
|
||||
|
||||
next = next->link;
|
||||
}
|
||||
k = next->inserted;
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const *const *line = &curr.file[1].linbuf[j++];
|
||||
set_color_context (ADD_CONTEXT);
|
||||
putc ('+', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (nullptr, line, true);
|
||||
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
|
||||
/* We're done with this hunk, so on to the next! */
|
||||
|
||||
next = next->link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,39 +431,33 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
2*CONTEXT unchanged lines appear, and return a pointer
|
||||
to the 'struct change' for the last change before those lines. */
|
||||
|
||||
static struct change * _GL_ATTRIBUTE_PURE
|
||||
find_hunk (struct change *start)
|
||||
static struct change * ATTRIBUTE_PURE
|
||||
find_hunk (struct change *script)
|
||||
{
|
||||
struct change *prev;
|
||||
lin top0, top1;
|
||||
lin thresh;
|
||||
|
||||
/* Threshold distance is CONTEXT if the second change is ignorable,
|
||||
2 * CONTEXT + 1 otherwise. Integer overflow can't happen, due
|
||||
to CONTEXT_LIM. */
|
||||
min (2 * CONTEXT + 1, LIN_MAX) otherwise. */
|
||||
lin ignorable_threshold = context;
|
||||
lin non_ignorable_threshold = 2 * context + 1;
|
||||
lin non_ignorable_threshold = (ckd_mul (&non_ignorable_threshold, context, 2)
|
||||
? LIN_MAX
|
||||
: non_ignorable_threshold + 1);
|
||||
|
||||
do
|
||||
for (struct change *next; ; script = next)
|
||||
{
|
||||
next = script->link;
|
||||
/* Compute number of first line in each file beyond this changed. */
|
||||
top0 = start->line0 + start->deleted;
|
||||
top1 = start->line1 + start->inserted;
|
||||
prev = start;
|
||||
start = start->link;
|
||||
thresh = (start && start->ignore
|
||||
? ignorable_threshold
|
||||
: non_ignorable_threshold);
|
||||
/* It is not supposed to matter which file we check in the end-test.
|
||||
If it would matter, crash. */
|
||||
if (start && start->line0 - top0 != start->line1 - top1)
|
||||
abort ();
|
||||
} while (start
|
||||
/* Keep going if less than THRESH lines
|
||||
elapse before the affected line. */
|
||||
&& start->line0 - top0 < thresh);
|
||||
lin top0 = script->line0 + script->deleted;
|
||||
lin top1 = script->line1 + script->inserted;
|
||||
lin thresh = (next && next->ignore
|
||||
? ignorable_threshold
|
||||
: non_ignorable_threshold);
|
||||
/* It is not supposed to matter which file we check in the end-test. */
|
||||
dassert (!next || next->line0 - top0 == next->line1 - top1);
|
||||
|
||||
return prev;
|
||||
/* Keep going if less than THRESH lines elapse
|
||||
before the affected line. */
|
||||
if (!next || thresh <= next->line0 - top0)
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the 'ignore' flag properly in each change in SCRIPT.
|
||||
@ -485,14 +470,14 @@ mark_ignorable (struct change *script)
|
||||
while (script)
|
||||
{
|
||||
struct change *next = script->link;
|
||||
lin first0, last0, first1, last1;
|
||||
|
||||
/* Turn this change into a hunk: detach it from the others. */
|
||||
script->link = NULL;
|
||||
script->link = nullptr;
|
||||
|
||||
/* Determine whether this change is ignorable. */
|
||||
lin first0, last0, first1, last1;
|
||||
script->ignore = ! analyze_hunk (script,
|
||||
&first0, &last0, &first1, &last1);
|
||||
&first0, &last0, &first1, &last1);
|
||||
|
||||
/* Reconnect the chain as before. */
|
||||
script->link = next;
|
||||
@ -504,10 +489,10 @@ mark_ignorable (struct change *script)
|
||||
|
||||
/* Find the last function-header line in LINBUF prior to line number LINENUM.
|
||||
This is a line containing a match for the regexp in 'function_regexp'.
|
||||
Return the address of the text, or NULL if no function-header is found. */
|
||||
Return the address of the text, or null if no function-header is found. */
|
||||
|
||||
static char const *
|
||||
find_function (char const * const *linbuf, lin linenum)
|
||||
find_function (char const *const *linbuf, lin linenum)
|
||||
{
|
||||
lin i = linenum;
|
||||
lin last = find_function_last_search;
|
||||
@ -517,21 +502,22 @@ find_function (char const * const *linbuf, lin linenum)
|
||||
{
|
||||
/* See if this line is what we want. */
|
||||
char const *line = linbuf[i];
|
||||
size_t linelen = linbuf[i + 1] - line - 1;
|
||||
idx_t linelen = linbuf[i + 1] - line - 1;
|
||||
|
||||
/* FIXME: re_search's size args should be size_t, not int. */
|
||||
int len = MIN (linelen, INT_MAX);
|
||||
/* This line is for documentation; in practice it's equivalent
|
||||
to LEN = LINELEN and no machine code is generated. */
|
||||
regoff_t len = MIN (linelen, TYPE_MAXIMUM (regoff_t));
|
||||
|
||||
if (0 <= re_search (&function_regexp, line, len, 0, len, NULL))
|
||||
{
|
||||
find_function_last_match = i;
|
||||
return line;
|
||||
}
|
||||
if (0 <= re_search (&function_regexp, line, len, 0, len, nullptr))
|
||||
{
|
||||
find_function_last_match = i;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
/* If we search back to where we started searching the previous time,
|
||||
find the line we found last time. */
|
||||
if (find_function_last_match != LIN_MAX)
|
||||
return linbuf[find_function_last_match];
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
31
src/die.h
31
src/die.h
@ -1,31 +0,0 @@
|
||||
/* Report an error and exit.
|
||||
Copyright 2016-2017 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
02110-1301, USA. */
|
||||
|
||||
#ifndef DIE_H
|
||||
# define DIE_H
|
||||
|
||||
# include <error.h>
|
||||
# include <stdbool.h>
|
||||
# include <verify.h>
|
||||
|
||||
/* Like 'error (STATUS, ...)', except STATUS must be a nonzero constant.
|
||||
This may pacify the compiler or help it generate better code. */
|
||||
# define die(status, ...) \
|
||||
verify_expr (status, (error (status, __VA_ARGS__), assume (false)))
|
||||
|
||||
#endif /* DIE_H */
|
||||
1883
src/diff.c
1883
src/diff.c
File diff suppressed because it is too large
Load Diff
216
src/diff.h
216
src/diff.h
@ -1,7 +1,7 @@
|
||||
/* Shared definitions for GNU DIFF
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@ -23,6 +23,12 @@
|
||||
#include <stdio.h>
|
||||
#include <unlocked-io.h>
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
|
||||
#ifndef DIFF_INLINE
|
||||
# define DIFF_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
/* What kind of changes a hunk contains. */
|
||||
enum changes
|
||||
{
|
||||
@ -54,12 +60,6 @@ enum colors_style
|
||||
|
||||
/* Variables for command line options */
|
||||
|
||||
#ifndef GDIFF_MAIN
|
||||
# define XTERN extern
|
||||
#else
|
||||
# define XTERN
|
||||
#endif
|
||||
|
||||
enum output_style
|
||||
{
|
||||
/* No output style specified. */
|
||||
@ -91,27 +91,31 @@ enum output_style
|
||||
};
|
||||
|
||||
/* True for output styles that are robust,
|
||||
i.e. can handle a file that ends in a non-newline. */
|
||||
#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED)
|
||||
i.e. can handle a file that ends in a non-newline.
|
||||
This is indented unusually to pacify 'make syntax-check'. */
|
||||
DIFF_INLINE bool robust_output_style (enum output_style s)
|
||||
{
|
||||
return s != OUTPUT_ED && s != OUTPUT_FORWARD_ED;
|
||||
}
|
||||
|
||||
XTERN enum output_style output_style;
|
||||
extern enum output_style output_style;
|
||||
|
||||
/* Define the current color context used to print a line. */
|
||||
XTERN enum colors_style colors_style;
|
||||
extern enum colors_style colors_style;
|
||||
|
||||
/* Nonzero if output cannot be generated for identical files. */
|
||||
XTERN bool no_diff_means_no_output;
|
||||
extern bool no_diff_means_no_output;
|
||||
|
||||
/* Number of lines of context to show in each set of diffs.
|
||||
This is zero when context is not to be shown. */
|
||||
XTERN lin context;
|
||||
extern lin context;
|
||||
|
||||
/* Consider all files as text files (-a).
|
||||
Don't interpret codes over 0177 as implying a "binary file". */
|
||||
XTERN bool text;
|
||||
extern bool text;
|
||||
|
||||
/* Number of lines to keep in identical prefix and suffix. */
|
||||
XTERN lin horizon_lines;
|
||||
extern lin horizon_lines;
|
||||
|
||||
/* The significance of white space during comparisons. */
|
||||
enum DIFF_white_space
|
||||
@ -136,100 +140,107 @@ enum DIFF_white_space
|
||||
/* Ignore all horizontal white space (-w). */
|
||||
IGNORE_ALL_SPACE
|
||||
};
|
||||
XTERN enum DIFF_white_space ignore_white_space;
|
||||
extern enum DIFF_white_space ignore_white_space;
|
||||
|
||||
/* Ignore changes that affect only blank lines (-B). */
|
||||
XTERN bool ignore_blank_lines;
|
||||
extern bool ignore_blank_lines;
|
||||
|
||||
/* Files can be compared byte-by-byte, as if they were binary.
|
||||
This depends on various options. */
|
||||
XTERN bool files_can_be_treated_as_binary;
|
||||
extern bool files_can_be_treated_as_binary;
|
||||
|
||||
/* Ignore differences in case of letters (-i). */
|
||||
XTERN bool ignore_case;
|
||||
extern bool ignore_case;
|
||||
|
||||
/* Ignore differences in case of letters in file names. */
|
||||
XTERN bool ignore_file_name_case;
|
||||
extern bool ignore_file_name_case;
|
||||
|
||||
/* Act on symbolic links themselves rather than on their target
|
||||
(--no-dereference). */
|
||||
XTERN bool no_dereference_symlinks;
|
||||
extern bool no_dereference_symlinks;
|
||||
|
||||
/* Local timezone for 'c' output headers, if needed. */
|
||||
#if HAVE_TM_GMTOFF
|
||||
# define localtz 0 /* Placeholder since localtz is never needed. */
|
||||
#else
|
||||
extern timezone_t localtz;
|
||||
#endif
|
||||
|
||||
/* File labels for '-c' output headers (--label). */
|
||||
XTERN char *file_label[2];
|
||||
extern char *file_label[2];
|
||||
|
||||
/* Regexp to identify function-header lines (-F). */
|
||||
XTERN struct re_pattern_buffer function_regexp;
|
||||
extern struct re_pattern_buffer function_regexp;
|
||||
|
||||
/* Ignore changes that affect only lines matching this regexp (-I). */
|
||||
XTERN struct re_pattern_buffer ignore_regexp;
|
||||
extern struct re_pattern_buffer ignore_regexp;
|
||||
|
||||
/* Say only whether files differ, not how (-q). */
|
||||
XTERN bool brief;
|
||||
extern bool brief;
|
||||
|
||||
/* Expand tabs in the output so the text lines up properly
|
||||
despite the characters added to the front of each line (-t). */
|
||||
XTERN bool expand_tabs;
|
||||
extern bool expand_tabs;
|
||||
|
||||
/* Number of columns between tab stops. */
|
||||
XTERN size_t tabsize;
|
||||
extern intmax_t tabsize;
|
||||
|
||||
/* Use a tab in the output, rather than a space, before the text of an
|
||||
input line, so as to keep the proper alignment in the input line
|
||||
without changing the characters in it (-T). */
|
||||
XTERN bool initial_tab;
|
||||
extern bool initial_tab;
|
||||
|
||||
/* Do not output an initial space or tab before the text of an empty line. */
|
||||
XTERN bool suppress_blank_empty;
|
||||
extern bool suppress_blank_empty;
|
||||
|
||||
/* Remove trailing carriage returns from input. */
|
||||
XTERN bool strip_trailing_cr;
|
||||
extern bool strip_trailing_cr;
|
||||
|
||||
/* In directory comparison, specify file to start with (-S).
|
||||
This is used for resuming an aborted comparison.
|
||||
All file names less than this name are ignored. */
|
||||
XTERN char const *starting_file;
|
||||
extern char const *starting_file;
|
||||
|
||||
/* Pipe each file's output through pr (-l). */
|
||||
XTERN bool paginate;
|
||||
extern bool paginate;
|
||||
|
||||
/* Line group formats for unchanged, old, new, and changed groups. */
|
||||
XTERN char const *group_format[CHANGED + 1];
|
||||
extern char const *group_format[CHANGED + 1];
|
||||
|
||||
/* Line formats for unchanged, old, and new lines. */
|
||||
XTERN char const *line_format[NEW + 1];
|
||||
extern char const *line_format[NEW + 1];
|
||||
|
||||
/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
|
||||
XTERN bool sdiff_merge_assist;
|
||||
extern bool sdiff_merge_assist;
|
||||
|
||||
/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
|
||||
XTERN bool left_column;
|
||||
extern bool left_column;
|
||||
|
||||
/* Tell OUTPUT_SDIFF to not show common lines. */
|
||||
XTERN bool suppress_common_lines;
|
||||
extern bool suppress_common_lines;
|
||||
|
||||
/* The half line width and column 2 offset for OUTPUT_SDIFF. */
|
||||
XTERN size_t sdiff_half_width;
|
||||
XTERN size_t sdiff_column2_offset;
|
||||
extern intmax_t sdiff_half_width;
|
||||
extern intmax_t sdiff_column2_offset;
|
||||
|
||||
/* String containing all the command options diff received,
|
||||
with spaces between and at the beginning but none at the end.
|
||||
If there were no options given, this string is empty. */
|
||||
XTERN char *switch_string;
|
||||
extern char *switch_string;
|
||||
|
||||
/* Use heuristics for better speed with large files with a small
|
||||
density of changes. */
|
||||
XTERN bool speed_large_files;
|
||||
extern bool speed_large_files;
|
||||
|
||||
/* Patterns that match file names to be excluded. */
|
||||
XTERN struct exclude *excluded;
|
||||
extern struct exclude *excluded;
|
||||
|
||||
/* Don't discard lines. This makes things slower (sometimes much
|
||||
slower) but will find a guaranteed minimal set of changes. */
|
||||
XTERN bool minimal;
|
||||
extern bool minimal;
|
||||
|
||||
/* The strftime format to use for time strings. */
|
||||
XTERN char const *time_format;
|
||||
extern char const *time_format;
|
||||
|
||||
/* The result of comparison is an "edit script": a chain of 'struct change'.
|
||||
Each 'struct change' represents one place where some lines are deleted
|
||||
@ -254,30 +265,57 @@ struct change
|
||||
|
||||
/* Structures that describe the input files. */
|
||||
|
||||
/* Directory entry types. Like dirent DT_* macros, but portable and
|
||||
safe to use as 'char'. Use the same values as GNU/Linux, as this
|
||||
may help compilers on that platform. */
|
||||
enum detype
|
||||
{
|
||||
DE_UNKNOWN,
|
||||
DE_FIFO,
|
||||
DE_CHR,
|
||||
DE_DIR = 4,
|
||||
DE_BLK = 6,
|
||||
DE_REG = 8,
|
||||
DE_LNK = 10,
|
||||
DE_SOCK = 12,
|
||||
DE_WHT = 14,
|
||||
|
||||
/* This one is not in GNU/Linux; it means the directory entry
|
||||
type has been determined but is none of the above. */
|
||||
DE_OTHER
|
||||
};
|
||||
|
||||
/* Data on one input file being compared. */
|
||||
|
||||
struct file_data {
|
||||
int desc; /* File descriptor */
|
||||
int openerr; /* openat errno, or 0 */
|
||||
int err; /* openat or fstatat or fstat errno, or 0 */
|
||||
char const *name; /* File name */
|
||||
char const *filetype; /* file type as untranslated string */
|
||||
struct stat stat; /* File status */
|
||||
|
||||
/* Directory stream corresponding to DESC, if it has been fdopendir'ed.
|
||||
Null otherwise. */
|
||||
DIR *dirstream;
|
||||
|
||||
/* Buffer in which text of file is read. */
|
||||
word *buffer;
|
||||
|
||||
/* Allocated size of buffer, in bytes. Always a multiple of
|
||||
sizeof *buffer. */
|
||||
size_t bufsize;
|
||||
idx_t bufsize;
|
||||
|
||||
/* Number of valid bytes now in the buffer. */
|
||||
size_t buffered;
|
||||
idx_t buffered;
|
||||
|
||||
/* Array of pointers to lines in the file. */
|
||||
char const **linbuf;
|
||||
|
||||
/* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
|
||||
linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
|
||||
linebuf[linbuf_base ... valid_lines - 1] contain valid data.
|
||||
linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
|
||||
/* linbuf_base <= 0 <= buffered_lines <= valid_lines <= alloc_lines.
|
||||
linbuf[0 ... buffered_lines - 1] are possibly differing.
|
||||
linbuf[linbuf_base ... valid_lines - 1] contain valid data.
|
||||
linbuf[linbuf_base ... alloc_lines - 1] are allocated. */
|
||||
lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
|
||||
|
||||
/* Pointer to end of prefix of this file to ignore when hashing. */
|
||||
@ -307,9 +345,9 @@ struct file_data {
|
||||
lin nondiscarded_lines;
|
||||
|
||||
/* Vector, indexed by real origin-0 line number,
|
||||
containing 1 for a line that is an insertion or a deletion.
|
||||
containing true for a line that is an insertion or a deletion.
|
||||
The results of comparison are stored here. */
|
||||
char *changed;
|
||||
bool *changed;
|
||||
|
||||
/* 1 if file ends in a line with no final newline. */
|
||||
bool missing_newline;
|
||||
@ -322,25 +360,38 @@ struct file_data {
|
||||
lin equiv_max;
|
||||
};
|
||||
|
||||
/* The file buffer, considered as an array of bytes rather than
|
||||
as an array of words. */
|
||||
#define FILE_BUFFER(f) ((char *) (f)->buffer)
|
||||
/* struct file_data.desc markers.
|
||||
A top level parent directory desc can be AT_FDCWD;
|
||||
it is OK if AT_FDCWD is one of these other values. */
|
||||
enum { OPEN_FAILED = -1 }; /* open was attempted but failed */
|
||||
enum { NONEXISTENT = -2 }; /* nonexistent file */
|
||||
enum { UNOPENED = -3 }; /* unopened file (e.g., file type mismatch) */
|
||||
|
||||
/* Data on two input files being compared. */
|
||||
|
||||
struct comparison
|
||||
{
|
||||
/* The two files. */
|
||||
struct file_data file[2];
|
||||
struct comparison const *parent; /* parent, if a recursive comparison */
|
||||
|
||||
/* The parent comparison, or &noparent if at the top level. */
|
||||
struct comparison const *parent;
|
||||
};
|
||||
|
||||
/* Describe the two files currently being compared. */
|
||||
|
||||
XTERN struct file_data files[2];
|
||||
extern struct comparison curr;
|
||||
|
||||
/* A placeholder for the parent of the top level comparison.
|
||||
Only the desc slots are used; although they are typically AT_FDCWD,
|
||||
one might be nonnegative for a directory at the top level
|
||||
for 'diff DIR FILE' or 'diff FILE DIR'. */
|
||||
|
||||
extern struct comparison noparent;
|
||||
|
||||
/* Stdio stream to output diffs to. */
|
||||
|
||||
XTERN FILE *outfile;
|
||||
extern FILE *outfile;
|
||||
|
||||
/* Declare various functions. */
|
||||
|
||||
@ -348,14 +399,20 @@ XTERN FILE *outfile;
|
||||
extern int diff_2_files (struct comparison *);
|
||||
|
||||
/* context.c */
|
||||
extern void print_context_header (struct file_data[], char const * const *, bool);
|
||||
extern void print_context_header (struct file_data[],
|
||||
char const *const *, bool);
|
||||
extern void print_context_script (struct change *, bool);
|
||||
|
||||
/* diff.c */
|
||||
extern int compare_files (struct comparison const *, enum detype const[2],
|
||||
char const *, char const *);
|
||||
|
||||
/* dir.c */
|
||||
extern int diff_dirs (struct comparison const *,
|
||||
int (*) (struct comparison const *,
|
||||
char const *, char const *));
|
||||
extern char *find_dir_file_pathname (char const *, char const *);
|
||||
extern int diff_dirs (struct comparison *);
|
||||
extern char *find_dir_file_pathname (struct file_data *, char const *,
|
||||
enum detype *)
|
||||
ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE
|
||||
ATTRIBUTE_RETURNS_NONNULL;
|
||||
|
||||
/* ed.c */
|
||||
extern void print_ed_script (struct change *);
|
||||
@ -365,7 +422,7 @@ extern void pr_forward_ed_script (struct change *);
|
||||
extern void print_ifdef_script (struct change *);
|
||||
|
||||
/* io.c */
|
||||
extern void file_block_read (struct file_data *, size_t);
|
||||
extern void file_block_read (struct file_data *, idx_t);
|
||||
extern bool read_files (struct file_data[], bool);
|
||||
|
||||
/* normal.c */
|
||||
@ -380,33 +437,28 @@ extern void print_sdiff_script (struct change *);
|
||||
/* util.c */
|
||||
extern char const change_letter[4];
|
||||
extern char const pr_program[];
|
||||
extern char *concat (char const *, char const *, char const *);
|
||||
extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
|
||||
extern lin translate_line_number (struct file_data const *, lin);
|
||||
extern struct change *find_change (struct change *);
|
||||
extern struct change *find_reverse_change (struct change *);
|
||||
extern void *zalloc (size_t);
|
||||
extern lin translate_line_number (struct file_data const *, lin)
|
||||
ATTRIBUTE_PURE;
|
||||
extern struct change *find_change (struct change *) ATTRIBUTE_CONST;
|
||||
extern enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *);
|
||||
extern void begin_output (void);
|
||||
extern void cleanup_signal_handlers (void);
|
||||
extern void debug_script (struct change *);
|
||||
extern void fatal (char const *) __attribute__((noreturn));
|
||||
extern _Noreturn void fatal (char const *);
|
||||
extern void finish_output (void);
|
||||
extern void message (char const *, char const *, char const *);
|
||||
extern void message5 (char const *, char const *, char const *,
|
||||
char const *, char const *);
|
||||
extern void message (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
|
||||
extern void output_1_line (char const *, char const *, char const *,
|
||||
char const *);
|
||||
extern void perror_with_name (char const *);
|
||||
extern void pfatal_with_name (char const *) __attribute__((noreturn));
|
||||
extern void print_1_line (char const *, char const * const *);
|
||||
extern void print_1_line_nl (char const *, char const * const *, bool);
|
||||
extern _Noreturn void pfatal_with_name (char const *);
|
||||
extern void print_1_line (char const *, char const *const *);
|
||||
extern void print_1_line_nl (char const *, char const *const *, bool);
|
||||
extern void print_message_queue (void);
|
||||
extern void print_number_range (char, struct file_data *, lin, lin);
|
||||
extern void print_script (struct change *, struct change * (*) (struct change *),
|
||||
void (*) (struct change *));
|
||||
extern void setup_output (char const *, char const *, bool);
|
||||
extern void translate_range (struct file_data const *, lin, lin,
|
||||
printint *, printint *);
|
||||
extern void translate_range (struct file_data const *, lin, lin, lin *, lin *);
|
||||
|
||||
enum color_context
|
||||
{
|
||||
@ -417,7 +469,9 @@ enum color_context
|
||||
LINE_NUMBER_CONTEXT,
|
||||
};
|
||||
|
||||
XTERN bool presume_output_tty;
|
||||
extern bool presume_output_tty;
|
||||
|
||||
extern void set_color_context (enum color_context color_context);
|
||||
extern void set_color_palette (char const *palette);
|
||||
extern void set_color_palette (char *palette);
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
1533
src/diff3.c
1533
src/diff3.c
File diff suppressed because it is too large
Load Diff
413
src/dir.c
413
src/dir.c
@ -1,7 +1,7 @@
|
||||
/* Read, sort and compare two directories. Used for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
|
||||
2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
2009-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@ -19,20 +19,28 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <diagnose.h>
|
||||
#include <dirname.h>
|
||||
#include <error.h>
|
||||
#include <exclude.h>
|
||||
#include <filenamecat.h>
|
||||
#include <mcel.h>
|
||||
#include <quote.h>
|
||||
#include <setjmp.h>
|
||||
#include <xalloc.h>
|
||||
|
||||
/* Read the directory named by DIR and store into DIRDATA a sorted vector
|
||||
of filenames for its contents. DIR->desc == -1 means this directory is
|
||||
known to be nonexistent, so set DIRDATA to an empty vector.
|
||||
Return -1 (setting errno) if error, 0 otherwise. */
|
||||
#ifndef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
# define HAVE_STRUCT_DIRENT_D_TYPE false
|
||||
#endif
|
||||
|
||||
/* A sorted vector of file names obtained by reading a directory.
|
||||
If HAVE_STRUCT_DIRENT_D_TYPE, each name is preceded by a byte
|
||||
giving the file type as an enum detype value. */
|
||||
|
||||
struct dirdata
|
||||
{
|
||||
size_t nnames; /* Number of names. */
|
||||
idx_t nnames; /* Number of names. */
|
||||
char const **names; /* Sorted names of files in dir, followed by 0. */
|
||||
char *data; /* Allocated storage for file names. */
|
||||
};
|
||||
@ -44,98 +52,132 @@ static bool locale_specific_sorting;
|
||||
/* Where to go if locale-specific sorting fails. */
|
||||
static jmp_buf failed_locale_specific_sorting;
|
||||
|
||||
static int compare_names (char const *, char const *);
|
||||
static bool dir_loop (struct comparison const *, int);
|
||||
|
||||
|
||||
/* Read a directory and get its vector of names. */
|
||||
/* Given the parent directory PARENTDIRFD (negative for current dir),
|
||||
read the directory named by DIR and store into DIRDATA a sorted
|
||||
vector of filenames for its contents.
|
||||
Use DIR's basename if PARENTDIRFD is nonnegative, for efficiency.
|
||||
If DIR->desc == NONEXISTENT, this directory is known to be
|
||||
nonexistent so set DIRDATA to an empty vector;
|
||||
otherwise, update DIR->desc and DIR->dirstream as needed.
|
||||
If STARTFILE, ignore directory entries less than STARTFILE, and if
|
||||
STARTFILE_ONLY, also ignore directory entries greater than STARTFILE.
|
||||
Return true if successful, false (setting errno) otherwise. */
|
||||
|
||||
static bool
|
||||
dir_read (struct file_data const *dir, struct dirdata *dirdata)
|
||||
dir_read (int parentdirfd, struct file_data *dir, struct dirdata *dirdata,
|
||||
char const *startfile, bool startfile_only)
|
||||
{
|
||||
register struct dirent *next;
|
||||
register size_t i;
|
||||
|
||||
/* Address of block containing the files that are described. */
|
||||
char const **names;
|
||||
|
||||
/* Number of files in directory. */
|
||||
size_t nnames;
|
||||
idx_t nnames = 0;
|
||||
|
||||
/* Allocated and used storage for file name data. */
|
||||
char *data;
|
||||
size_t data_alloc, data_used;
|
||||
|
||||
dirdata->names = 0;
|
||||
dirdata->data = 0;
|
||||
nnames = 0;
|
||||
data = 0;
|
||||
dirdata->names = nullptr;
|
||||
dirdata->data = nullptr;
|
||||
|
||||
if (dir->desc != -1)
|
||||
if (dir->desc != NONEXISTENT)
|
||||
{
|
||||
/* Open the directory and check for errors. */
|
||||
register DIR *reading = opendir (dir->name);
|
||||
int dirfd = dir->desc;
|
||||
if (dirfd < 0)
|
||||
{
|
||||
dirfd = openat (parentdirfd,
|
||||
(parentdirfd < 0 ? dir->name
|
||||
: last_component (dir->name)),
|
||||
(O_RDONLY | O_CLOEXEC | O_DIRECTORY
|
||||
| (no_dereference_symlinks ? O_NOFOLLOW : 0)));
|
||||
if (dirfd < 0)
|
||||
return false;
|
||||
dir->desc = dirfd;
|
||||
}
|
||||
DIR *reading = fdopendir (dirfd);
|
||||
if (!reading)
|
||||
return false;
|
||||
return false;
|
||||
dir->dirstream = reading;
|
||||
|
||||
/* Initialize the table of filenames. */
|
||||
|
||||
data_alloc = 512;
|
||||
data_used = 0;
|
||||
dirdata->data = data = xmalloc (data_alloc);
|
||||
idx_t data_alloc = 512;
|
||||
idx_t data_used = 0;
|
||||
dirdata->data = data = ximalloc (data_alloc);
|
||||
|
||||
/* Read the directory entries, and insert the subfiles
|
||||
into the 'data' table. */
|
||||
into the 'data' table. */
|
||||
|
||||
while ((errno = 0, (next = readdir (reading)) != 0))
|
||||
{
|
||||
char *d_name = next->d_name;
|
||||
size_t d_size = _D_EXACT_NAMLEN (next) + 1;
|
||||
while (true)
|
||||
{
|
||||
errno = 0;
|
||||
struct dirent *next = readdir (reading);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
/* Ignore "." and "..". */
|
||||
if (d_name[0] == '.'
|
||||
&& (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
|
||||
continue;
|
||||
char *d_name = next->d_name;
|
||||
|
||||
if (excluded_file_name (excluded, d_name))
|
||||
continue;
|
||||
/* Ignore "." and "..". */
|
||||
if (d_name[0] == '.'
|
||||
&& (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
|
||||
continue;
|
||||
|
||||
while (data_alloc < data_used + d_size)
|
||||
if (startfile)
|
||||
{
|
||||
if (PTRDIFF_MAX / 2 <= data_alloc)
|
||||
xalloc_die ();
|
||||
dirdata->data = data = xrealloc (data, data_alloc *= 2);
|
||||
int cmp = compare_names (d_name, startfile);
|
||||
if (cmp < 0 || (startfile_only && !!cmp))
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy (data + data_used, d_name, d_size);
|
||||
data_used += d_size;
|
||||
nnames++;
|
||||
}
|
||||
if (errno)
|
||||
{
|
||||
int e = errno;
|
||||
closedir (reading);
|
||||
errno = e;
|
||||
return false;
|
||||
}
|
||||
#if CLOSEDIR_VOID
|
||||
closedir (reading);
|
||||
#else
|
||||
if (closedir (reading) != 0)
|
||||
return false;
|
||||
if (excluded_file_name (excluded, d_name))
|
||||
continue;
|
||||
|
||||
idx_t d_size = HAVE_STRUCT_DIRENT_D_TYPE + _D_EXACT_NAMLEN (next) + 1;
|
||||
if (data_alloc - data_used < d_size)
|
||||
dirdata->data = data
|
||||
= xpalloc (data, &data_alloc,
|
||||
d_size - (data_alloc - data_used), -1, 1);
|
||||
#if HAVE_STRUCT_DIRENT_D_TYPE
|
||||
char detype;
|
||||
switch (next->d_type)
|
||||
{
|
||||
case DT_BLK: detype = DE_BLK; break;
|
||||
case DT_CHR: detype = DE_CHR; break;
|
||||
case DT_DIR: detype = DE_DIR; break;
|
||||
case DT_FIFO: detype = DE_FIFO; break;
|
||||
case DT_LNK: detype = DE_LNK; break;
|
||||
case DT_REG: detype = DE_REG; break;
|
||||
case DT_SOCK: detype = DE_SOCK; break;
|
||||
# ifdef DT_WHT
|
||||
case DT_WHT: detype = DE_WHT; break;
|
||||
# endif
|
||||
case DT_UNKNOWN: detype = DE_UNKNOWN; break;
|
||||
default: detype = DE_OTHER; break;
|
||||
}
|
||||
data[data_used++] = detype;
|
||||
d_size--;
|
||||
#endif
|
||||
memcpy (data + data_used, d_name, d_size);
|
||||
data_used += d_size;
|
||||
nnames++;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create the 'names' table from the 'data' table. */
|
||||
if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
|
||||
xalloc_die ();
|
||||
dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names);
|
||||
char const **names = xinmalloc (nnames + 1, sizeof *names);
|
||||
dirdata->names = names;
|
||||
dirdata->nnames = nnames;
|
||||
for (i = 0; i < nnames; i++)
|
||||
for (idx_t i = 0; i < nnames; i++)
|
||||
{
|
||||
data += HAVE_STRUCT_DIRENT_D_TYPE;
|
||||
names[i] = data;
|
||||
data += strlen (data) + 1;
|
||||
}
|
||||
names[nnames] = 0;
|
||||
names[nnames] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -145,16 +187,12 @@ dir_read (struct file_data const *dir, struct dirdata *dirdata)
|
||||
static int
|
||||
compare_collated (char const *name1, char const *name2)
|
||||
{
|
||||
int r;
|
||||
errno = 0;
|
||||
if (ignore_file_name_case)
|
||||
r = strcasecoll (name1, name2);
|
||||
else
|
||||
r = strcoll (name1, name2);
|
||||
int r = strcoll (name1, name2);
|
||||
if (errno)
|
||||
{
|
||||
error (0, errno, _("cannot compare file names '%s' and '%s'"),
|
||||
name1, name2);
|
||||
error (0, errno, _("cannot compare file names %s and %s"),
|
||||
quote_n (0, name1), quote_n (1, name2));
|
||||
longjmp (failed_locale_specific_sorting, 1);
|
||||
}
|
||||
return r;
|
||||
@ -165,11 +203,14 @@ compare_collated (char const *name1, char const *name2)
|
||||
static int
|
||||
compare_names (char const *name1, char const *name2)
|
||||
{
|
||||
if (ignore_file_name_case)
|
||||
return mbscasecmp (name1, name2); /* Best we can do. */
|
||||
|
||||
if (locale_specific_sorting)
|
||||
{
|
||||
int diff = compare_collated (name1, name2);
|
||||
if (diff || ignore_file_name_case)
|
||||
return diff;
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
return file_name_cmp (name1, name2);
|
||||
}
|
||||
@ -182,138 +223,108 @@ compare_names_for_qsort (void const *file1, void const *file2)
|
||||
{
|
||||
char const *const *f1 = file1;
|
||||
char const *const *f2 = file2;
|
||||
char const *name1 = *f1;
|
||||
char const *name2 = *f2;
|
||||
if (locale_specific_sorting)
|
||||
{
|
||||
int diff = compare_collated (name1, name2);
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
return file_name_cmp (name1, name2);
|
||||
return compare_names (*f1, *f2);
|
||||
}
|
||||
|
||||
/* Compare the contents of two directories named in CMP.
|
||||
This is a top-level routine; it does everything necessary for diff
|
||||
on two directories.
|
||||
|
||||
CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist,
|
||||
but pretend it is empty. Likewise for CMP->file[1].
|
||||
If CMP->file[0].desc == NONEXISTENT, directory CMP->file[0] doesn't exist
|
||||
and pretend it is empty. Otherwise, update CMP->file[0].desc and
|
||||
CMP->file[0].dirstream as needed. Likewise for CMP->file[1].
|
||||
|
||||
HANDLE_FILE is a caller-provided subroutine called to handle each file.
|
||||
It gets three operands: CMP, name of file in dir 0, name of file in dir 1.
|
||||
These names are relative to the original working directory.
|
||||
|
||||
For a file that appears in only one of the dirs, one of the name-args
|
||||
to HANDLE_FILE is zero.
|
||||
|
||||
Returns the maximum of all the values returned by HANDLE_FILE,
|
||||
Returns the maximum of all the values returned by compare_files,
|
||||
or EXIT_TROUBLE if trouble is encountered in opening files. */
|
||||
|
||||
int
|
||||
diff_dirs (struct comparison const *cmp,
|
||||
int (*handle_file) (struct comparison const *,
|
||||
char const *, char const *))
|
||||
diff_dirs (struct comparison *cmp)
|
||||
{
|
||||
struct dirdata dirdata[2];
|
||||
int volatile val = EXIT_SUCCESS;
|
||||
int i;
|
||||
|
||||
if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0))
|
||||
&& (cmp->file[1].desc == -1 || dir_loop (cmp, 1)))
|
||||
if ((cmp->file[0].desc == NONEXISTENT || dir_loop (cmp, 0))
|
||||
&& (cmp->file[1].desc == NONEXISTENT || dir_loop (cmp, 1)))
|
||||
{
|
||||
error (0, 0, _("%s: recursive directory loop"),
|
||||
cmp->file[cmp->file[0].desc == -1].name);
|
||||
squote (0, cmp->file[cmp->file[0].desc == NONEXISTENT].name));
|
||||
return EXIT_TROUBLE;
|
||||
}
|
||||
|
||||
/* Get contents of both dirs. */
|
||||
for (i = 0; i < 2; i++)
|
||||
if (! dir_read (&cmp->file[i], &dirdata[i]))
|
||||
struct dirdata dirdata[2];
|
||||
int val = EXIT_SUCCESS;
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (! dir_read (cmp->parent->file[i].desc, &cmp->file[i], &dirdata[i],
|
||||
cmp->parent == &noparent ? starting_file : nullptr, false))
|
||||
{
|
||||
perror_with_name (cmp->file[i].name);
|
||||
val = EXIT_TROUBLE;
|
||||
perror_with_name (cmp->file[i].name);
|
||||
val = EXIT_TROUBLE;
|
||||
}
|
||||
|
||||
if (val == EXIT_SUCCESS)
|
||||
{
|
||||
char const **volatile names[2];
|
||||
names[0] = dirdata[0].names;
|
||||
names[1] = dirdata[1].names;
|
||||
|
||||
/* Use locale-specific sorting if possible, else native byte order. */
|
||||
locale_specific_sorting = true;
|
||||
if (setjmp (failed_locale_specific_sorting))
|
||||
locale_specific_sorting = false;
|
||||
if (! ignore_file_name_case)
|
||||
if (setjmp (failed_locale_specific_sorting))
|
||||
locale_specific_sorting = false;
|
||||
|
||||
/* Sort the directories. */
|
||||
for (i = 0; i < 2; i++)
|
||||
qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
|
||||
compare_names_for_qsort);
|
||||
|
||||
/* If '-S name' was given, and this is the topmost level of comparison,
|
||||
ignore all file names less than the specified starting name. */
|
||||
|
||||
if (starting_file && ! cmp->parent)
|
||||
{
|
||||
while (*names[0] && compare_names (*names[0], starting_file) < 0)
|
||||
names[0]++;
|
||||
while (*names[1] && compare_names (*names[1], starting_file) < 0)
|
||||
names[1]++;
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
qsort (dirdata[i].names, dirdata[i].nnames, sizeof *dirdata[i].names,
|
||||
compare_names_for_qsort);
|
||||
|
||||
/* Loop while files remain in one or both dirs. */
|
||||
while (*names[0] || *names[1])
|
||||
{
|
||||
/* Compare next name in dir 0 with next name in dir 1.
|
||||
At the end of a dir,
|
||||
pretend the "next name" in that dir is very large. */
|
||||
int nameorder = (!*names[0] ? 1 : !*names[1] ? -1
|
||||
: compare_names (*names[0], *names[1]));
|
||||
char const **n0 = dirdata[0].names;
|
||||
char const **n1 = dirdata[1].names;
|
||||
while (*n0 || *n1)
|
||||
{
|
||||
/* Compare next name in dir 0 with next name in dir 1.
|
||||
At the end of a dir,
|
||||
pretend the "next name" in that dir is very large. */
|
||||
int nameorder = !*n0 ? 1 : !*n1 ? -1 : compare_names (*n0, *n1);
|
||||
|
||||
/* Prefer a file_name_cmp match if available. This algorithm is
|
||||
O(N**2), where N is the number of names in a directory
|
||||
that compare_names says are all equal, but in practice N
|
||||
is so small it's not worth tuning. */
|
||||
if (nameorder == 0 && ignore_file_name_case)
|
||||
{
|
||||
int raw_order = file_name_cmp (*names[0], *names[1]);
|
||||
if (raw_order != 0)
|
||||
{
|
||||
int greater_side = raw_order < 0;
|
||||
int lesser_side = 1 - greater_side;
|
||||
char const **lesser = names[lesser_side];
|
||||
char const *greater_name = *names[greater_side];
|
||||
char const **p;
|
||||
/* Prefer a file_name_cmp match if available. This algorithm is
|
||||
O(N**2), where N is the number of names in a directory
|
||||
that compare_names says are all equal, but in practice N
|
||||
is so small it's not worth tuning. */
|
||||
if (nameorder == 0 && ignore_file_name_case)
|
||||
{
|
||||
int raw_order = file_name_cmp (*n0, *n1);
|
||||
if (raw_order != 0)
|
||||
{
|
||||
char const **lesser = raw_order < 0 ? n0 : n1;
|
||||
char const *greater_name = *(raw_order < 0 ? n1 : n0);
|
||||
|
||||
for (p = lesser + 1;
|
||||
*p && compare_names (*p, greater_name) == 0;
|
||||
p++)
|
||||
{
|
||||
int c = file_name_cmp (*p, greater_name);
|
||||
if (0 <= c)
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
memmove (lesser + 1, lesser,
|
||||
(char *) p - (char *) lesser);
|
||||
*lesser = greater_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (char const **p = lesser + 1;
|
||||
*p && compare_names (*p, greater_name) == 0;
|
||||
p++)
|
||||
{
|
||||
int c = file_name_cmp (*p, greater_name);
|
||||
if (0 <= c)
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
memmove (lesser + 1, lesser,
|
||||
(char *) p - (char *) lesser);
|
||||
*lesser = greater_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int v1 = (*handle_file) (cmp,
|
||||
0 < nameorder ? 0 : *names[0]++,
|
||||
nameorder < 0 ? 0 : *names[1]++);
|
||||
if (val < v1)
|
||||
val = v1;
|
||||
}
|
||||
enum detype detypes[]
|
||||
= { HAVE_STRUCT_DIRENT_D_TYPE && *n0 ? (*n0)[-1] : DE_UNKNOWN,
|
||||
HAVE_STRUCT_DIRENT_D_TYPE && *n1 ? (*n1)[-1] : DE_UNKNOWN };
|
||||
int v1 = compare_files (cmp, detypes,
|
||||
0 < nameorder ? nullptr : *n0++,
|
||||
nameorder < 0 ? nullptr : *n1++);
|
||||
if (val < v1)
|
||||
val = v1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
free (dirdata[i].names);
|
||||
free (dirdata[i].data);
|
||||
@ -324,12 +335,11 @@ diff_dirs (struct comparison const *cmp,
|
||||
|
||||
/* Return nonzero if CMP is looping recursively in argument I. */
|
||||
|
||||
static bool _GL_ATTRIBUTE_PURE
|
||||
static bool ATTRIBUTE_PURE
|
||||
dir_loop (struct comparison const *cmp, int i)
|
||||
{
|
||||
struct comparison const *p = cmp;
|
||||
while ((p = p->parent))
|
||||
if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
|
||||
for (struct comparison const *p = cmp; (p = p->parent) != &noparent; )
|
||||
if (same_file (&p->file[i].stat, &cmp->file[i].stat))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -337,48 +347,29 @@ dir_loop (struct comparison const *cmp, int i)
|
||||
/* Find a matching filename in a directory. */
|
||||
|
||||
char *
|
||||
find_dir_file_pathname (char const *dir, char const *file)
|
||||
find_dir_file_pathname (struct file_data *dir, char const *file,
|
||||
enum detype *detype)
|
||||
{
|
||||
/* The 'IF_LINT (volatile)' works around what appears to be a bug in
|
||||
gcc 4.8.0 20120825; see
|
||||
<http://lists.gnu.org/archive/html/bug-diffutils/2012-08/msg00007.html>.
|
||||
*/
|
||||
char const * IF_LINT (volatile) match = file;
|
||||
char const *match = file;
|
||||
|
||||
char *val;
|
||||
struct dirdata dirdata;
|
||||
dirdata.names = NULL;
|
||||
dirdata.data = NULL;
|
||||
dirdata.names = nullptr;
|
||||
dirdata.data = nullptr;
|
||||
|
||||
if (ignore_file_name_case)
|
||||
{
|
||||
struct file_data filedata;
|
||||
filedata.name = dir;
|
||||
filedata.desc = 0;
|
||||
if (ignore_file_name_case && dir_read (AT_FDCWD, dir, &dirdata, file, true))
|
||||
for (char const **p = dirdata.names; *p; p++)
|
||||
{
|
||||
if (file_name_cmp (*p, file) == 0)
|
||||
{
|
||||
match = *p;
|
||||
break;
|
||||
}
|
||||
if (match == file)
|
||||
match = *p;
|
||||
}
|
||||
|
||||
if (dir_read (&filedata, &dirdata))
|
||||
{
|
||||
locale_specific_sorting = true;
|
||||
if (setjmp (failed_locale_specific_sorting))
|
||||
match = file; /* longjmp may mess up MATCH. */
|
||||
else
|
||||
{
|
||||
for (char const **p = dirdata.names; *p; p++)
|
||||
if (compare_names (*p, file) == 0)
|
||||
{
|
||||
if (file_name_cmp (*p, file) == 0)
|
||||
{
|
||||
match = *p;
|
||||
break;
|
||||
}
|
||||
if (match == file)
|
||||
match = *p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val = file_name_concat (dir, match, NULL);
|
||||
*detype = HAVE_STRUCT_DIRENT_D_TYPE && match != file ? match[-1] : DE_UNKNOWN;
|
||||
char *val = file_name_concat (dir->name, match, nullptr);
|
||||
free (dirdata.names);
|
||||
free (dirdata.data);
|
||||
return val;
|
||||
|
||||
81
src/ed.c
81
src/ed.c
@ -1,7 +1,7 @@
|
||||
/* Output routines for ed-script format.
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1993, 1995, 1998, 2001, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@ -29,7 +29,8 @@ static void pr_forward_ed_hunk (struct change *);
|
||||
void
|
||||
print_ed_script (struct change *script)
|
||||
{
|
||||
print_script (script, find_reverse_change, print_ed_hunk);
|
||||
/* The script is reversed, so plain find_change suffices. */
|
||||
print_script (script, find_change, print_ed_hunk);
|
||||
}
|
||||
|
||||
/* Print a hunk of an ed diff */
|
||||
@ -37,52 +38,50 @@ print_ed_script (struct change *script)
|
||||
static void
|
||||
print_ed_hunk (struct change *hunk)
|
||||
{
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes;
|
||||
|
||||
#ifdef DEBUG
|
||||
debug_script (hunk);
|
||||
#endif
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
|
||||
begin_output ();
|
||||
|
||||
/* Print out the line number header for this hunk */
|
||||
print_number_range (',', &files[0], f0, l0);
|
||||
print_number_range (',', &curr.file[0], f0, l0);
|
||||
fputc (change_letter[changes], outfile);
|
||||
fputc ('\n', outfile);
|
||||
|
||||
/* Print new/changed lines from second file, if needed */
|
||||
if (changes != OLD)
|
||||
{
|
||||
lin i;
|
||||
bool insert_mode = true;
|
||||
|
||||
for (i = f1; i <= l1; i++)
|
||||
{
|
||||
if (!insert_mode)
|
||||
{
|
||||
fputs ("a\n", outfile);
|
||||
insert_mode = true;
|
||||
}
|
||||
if (files[1].linbuf[i][0] == '.' && files[1].linbuf[i][1] == '\n')
|
||||
{
|
||||
/* The file's line is just a dot, and it would exit
|
||||
insert mode. Precede the dot with another dot, exit
|
||||
insert mode and remove the extra dot. */
|
||||
fputs ("..\n.\ns/.//\n", outfile);
|
||||
insert_mode = false;
|
||||
}
|
||||
else
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
}
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
{
|
||||
if (!insert_mode)
|
||||
{
|
||||
fputs ("a\n", outfile);
|
||||
insert_mode = true;
|
||||
}
|
||||
if (curr.file[1].linbuf[i][0] == '.'
|
||||
&& curr.file[1].linbuf[i][1] == '\n')
|
||||
{
|
||||
/* The file's line is just a dot, and it would exit
|
||||
insert mode. Precede the dot with another dot, exit
|
||||
insert mode and remove the extra dot. */
|
||||
fputs ("..\n.\ns/.//\n", outfile);
|
||||
insert_mode = false;
|
||||
}
|
||||
else
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
}
|
||||
|
||||
if (insert_mode)
|
||||
fputs (".\n", outfile);
|
||||
fputs (".\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,9 +100,8 @@ pr_forward_ed_script (struct change *script)
|
||||
static void
|
||||
pr_forward_ed_hunk (struct change *hunk)
|
||||
{
|
||||
lin i, f0, l0, f1, l1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
@ -111,7 +109,7 @@ pr_forward_ed_hunk (struct change *hunk)
|
||||
begin_output ();
|
||||
|
||||
fputc (change_letter[changes], outfile);
|
||||
print_number_range (' ', files, f0, l0);
|
||||
print_number_range (' ', &curr.file[0], f0, l0);
|
||||
fputc ('\n', outfile);
|
||||
|
||||
/* If deletion only, print just the number range. */
|
||||
@ -122,8 +120,8 @@ pr_forward_ed_hunk (struct change *hunk)
|
||||
/* For insertion (with or without deletion), print the number range
|
||||
and the lines from file 2. */
|
||||
|
||||
for (i = f1; i <= l1; i++)
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
|
||||
fputs (".\n", outfile);
|
||||
}
|
||||
@ -143,35 +141,34 @@ print_rcs_script (struct change *script)
|
||||
static void
|
||||
print_rcs_hunk (struct change *hunk)
|
||||
{
|
||||
lin i, f0, l0, f1, l1;
|
||||
printint tf0, tl0, tf1, tl1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
|
||||
begin_output ();
|
||||
|
||||
translate_range (&files[0], f0, l0, &tf0, &tl0);
|
||||
lin tf0, tl0, tf1, tl1;
|
||||
translate_range (&curr.file[0], f0, l0, &tf0, &tl0);
|
||||
|
||||
if (changes & OLD)
|
||||
{
|
||||
/* For deletion, print just the starting line number from file 0
|
||||
and the number of lines deleted. */
|
||||
and the number of lines deleted. */
|
||||
fprintf (outfile, "d%"pI"d %"pI"d\n", tf0,
|
||||
tf0 <= tl0 ? tl0 - tf0 + 1 : 1);
|
||||
tf0 <= tl0 ? tl0 - tf0 + 1 : 1);
|
||||
}
|
||||
|
||||
if (changes & NEW)
|
||||
{
|
||||
/* Take last-line-number from file 0 and # lines from file 1. */
|
||||
translate_range (&files[1], f1, l1, &tf1, &tl1);
|
||||
translate_range (&curr.file[1], f1, l1, &tf1, &tl1);
|
||||
fprintf (outfile, "a%"pI"d %"pI"d\n", tl0,
|
||||
tf1 <= tl1 ? tl1 - tf1 + 1 : 1);
|
||||
tf1 <= tl1 ? tl1 - tf1 + 1 : 1);
|
||||
|
||||
/* Print the inserted lines. */
|
||||
for (i = f1; i <= l1; i++)
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
417
src/ifdef.c
417
src/ifdef.c
@ -1,6 +1,6 @@
|
||||
/* #ifdef-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2017
|
||||
Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2026
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@ -22,7 +22,8 @@
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <xalloc.h>
|
||||
#include <c-ctype.h>
|
||||
#include <xmalloca.h>
|
||||
|
||||
struct group
|
||||
{
|
||||
@ -31,10 +32,10 @@ struct group
|
||||
};
|
||||
|
||||
static char const *format_group (FILE *, char const *, char,
|
||||
struct group const *);
|
||||
struct group const *);
|
||||
static char const *do_printf_spec (FILE *, char const *,
|
||||
struct file_data const *, lin,
|
||||
struct group const *);
|
||||
struct file_data const *, lin,
|
||||
struct group const *);
|
||||
static char const *scan_char_literal (char const *, char *);
|
||||
static lin groups_letter_value (struct group const *, char);
|
||||
static void format_ifdef (char const *, lin, lin, lin, lin);
|
||||
@ -49,15 +50,15 @@ static lin next_line1;
|
||||
void
|
||||
print_ifdef_script (struct change *script)
|
||||
{
|
||||
next_line0 = next_line1 = - files[0].prefix_lines;
|
||||
next_line0 = next_line1 = - curr.file[0].prefix_lines;
|
||||
print_script (script, find_change, print_ifdef_hunk);
|
||||
if (next_line0 < files[0].valid_lines
|
||||
|| next_line1 < files[1].valid_lines)
|
||||
if (next_line0 < curr.file[0].valid_lines
|
||||
|| next_line1 < curr.file[1].valid_lines)
|
||||
{
|
||||
begin_output ();
|
||||
format_ifdef (group_format[UNCHANGED],
|
||||
next_line0, files[0].valid_lines,
|
||||
next_line1, files[1].valid_lines);
|
||||
next_line0, curr.file[0].valid_lines,
|
||||
next_line1, curr.file[1].valid_lines);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,9 +69,8 @@ print_ifdef_script (struct change *script)
|
||||
static void
|
||||
print_ifdef_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
return;
|
||||
@ -80,15 +80,15 @@ print_ifdef_hunk (struct change *hunk)
|
||||
/* Print lines up to this change. */
|
||||
if (next_line0 < first0 || next_line1 < first1)
|
||||
format_ifdef (group_format[UNCHANGED],
|
||||
next_line0, first0,
|
||||
next_line1, first1);
|
||||
next_line0, first0,
|
||||
next_line1, first1);
|
||||
|
||||
/* Print this change. */
|
||||
next_line0 = last0 + 1;
|
||||
next_line1 = last1 + 1;
|
||||
format_ifdef (group_format[changes],
|
||||
first0, next_line0,
|
||||
first1, next_line1);
|
||||
first0, next_line0,
|
||||
first1, next_line1);
|
||||
}
|
||||
|
||||
/* Print a set of lines according to FORMAT.
|
||||
@ -98,15 +98,10 @@ print_ifdef_hunk (struct change *hunk)
|
||||
static void
|
||||
format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
|
||||
{
|
||||
struct group groups[2];
|
||||
|
||||
groups[0].file = &files[0];
|
||||
groups[0].from = beg0;
|
||||
groups[0].upto = end0;
|
||||
groups[1].file = &files[1];
|
||||
groups[1].from = beg1;
|
||||
groups[1].upto = end1;
|
||||
format_group (outfile, format, 0, groups);
|
||||
format_group (outfile, format, '\0',
|
||||
((struct group const[])
|
||||
{{.file = &curr.file[0], .from = beg0, .upto = end0},
|
||||
{.file = &curr.file[1], .from = beg1, .upto = end1}}));
|
||||
}
|
||||
|
||||
/* Print to file OUT a set of lines according to FORMAT.
|
||||
@ -116,91 +111,91 @@ format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
|
||||
If OUT is zero, do not actually print anything; just scan the format. */
|
||||
|
||||
static char const *
|
||||
format_group (register FILE *out, char const *format, char endchar,
|
||||
struct group const *groups)
|
||||
format_group (FILE *out, char const *format, char endchar,
|
||||
struct group const *groups)
|
||||
{
|
||||
register char c;
|
||||
register char const *f = format;
|
||||
char const *f = format;
|
||||
|
||||
while ((c = *f) != endchar && c != 0)
|
||||
for (char c; (c = *f) != endchar && c; )
|
||||
{
|
||||
char const *f1 = ++f;
|
||||
if (c == '%')
|
||||
switch ((c = *f++))
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
{
|
||||
c = *f++;
|
||||
switch (c)
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
|
||||
case '(':
|
||||
/* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
|
||||
{
|
||||
int i;
|
||||
uintmax_t value[2];
|
||||
FILE *thenout, *elseout;
|
||||
case '(':
|
||||
/* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
|
||||
{
|
||||
intmax_t value[2];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if (ISDIGIT (*f))
|
||||
{
|
||||
char *fend;
|
||||
errno = 0;
|
||||
value[i] = strtoumax (f, &fend, 10);
|
||||
if (errno)
|
||||
goto bad_format;
|
||||
f = fend;
|
||||
}
|
||||
else
|
||||
{
|
||||
value[i] = groups_letter_value (groups, *f);
|
||||
if (value[i] == -1)
|
||||
goto bad_format;
|
||||
f++;
|
||||
}
|
||||
if (*f++ != "=?"[i])
|
||||
goto bad_format;
|
||||
}
|
||||
if (value[0] == value[1])
|
||||
thenout = out, elseout = 0;
|
||||
else
|
||||
thenout = 0, elseout = out;
|
||||
f = format_group (thenout, f, ':', groups);
|
||||
if (*f)
|
||||
{
|
||||
f = format_group (elseout, f + 1, ')', groups);
|
||||
if (*f)
|
||||
f++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (c_isdigit (*f))
|
||||
{
|
||||
char *fend;
|
||||
errno = 0;
|
||||
value[i] = strtoimax (f, &fend, 10);
|
||||
if (errno)
|
||||
goto bad_format;
|
||||
f = fend;
|
||||
}
|
||||
else
|
||||
{
|
||||
value[i] = groups_letter_value (groups, *f);
|
||||
if (value[i] < 0)
|
||||
goto bad_format;
|
||||
f++;
|
||||
}
|
||||
if (*f++ != "=?"[i])
|
||||
goto bad_format;
|
||||
}
|
||||
|
||||
case '<':
|
||||
/* Print lines deleted from first file. */
|
||||
print_ifdef_lines (out, line_format[OLD], &groups[0]);
|
||||
continue;
|
||||
bool equal_values = value[0] == value[1];
|
||||
FILE *thenout = equal_values ? out : nullptr;
|
||||
FILE *elseout = equal_values ? nullptr : out;
|
||||
f = format_group (thenout, f, ':', groups);
|
||||
if (*f)
|
||||
{
|
||||
f = format_group (elseout, f + 1, ')', groups);
|
||||
if (*f)
|
||||
f++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
||||
case '=':
|
||||
/* Print common lines. */
|
||||
print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
|
||||
continue;
|
||||
case '<':
|
||||
/* Print lines deleted from first file. */
|
||||
print_ifdef_lines (out, line_format[OLD], &groups[0]);
|
||||
continue;
|
||||
|
||||
case '>':
|
||||
/* Print lines inserted from second file. */
|
||||
print_ifdef_lines (out, line_format[NEW], &groups[1]);
|
||||
continue;
|
||||
case '=':
|
||||
/* Print common lines. */
|
||||
print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, 0, 0, groups);
|
||||
if (f)
|
||||
continue;
|
||||
/* Fall through. */
|
||||
bad_format:
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
case '>':
|
||||
/* Print lines inserted from second file. */
|
||||
print_ifdef_lines (out, line_format[NEW], &groups[1]);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, nullptr, 0, groups);
|
||||
if (f)
|
||||
continue;
|
||||
/* Fall through. */
|
||||
bad_format:
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (out)
|
||||
putc (c, out);
|
||||
putc (c, out);
|
||||
}
|
||||
|
||||
return f;
|
||||
@ -234,154 +229,149 @@ groups_letter_value (struct group const *g, char letter)
|
||||
/* Print to file OUT, using FORMAT to print the line group GROUP.
|
||||
But do nothing if OUT is zero. */
|
||||
static void
|
||||
print_ifdef_lines (register FILE *out, char const *format,
|
||||
struct group const *group)
|
||||
print_ifdef_lines (FILE *out, char const *format,
|
||||
struct group const *group)
|
||||
{
|
||||
struct file_data const *file = group->file;
|
||||
char const * const *linbuf = file->linbuf;
|
||||
lin from = group->from, upto = group->upto;
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
|
||||
struct file_data const *file = group->file;
|
||||
char const *const *linbuf = file->linbuf;
|
||||
lin from = group->from, upto = group->upto;
|
||||
|
||||
/* If possible, use a single fwrite; it's faster. */
|
||||
if (!expand_tabs && format[0] == '%')
|
||||
{
|
||||
if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
|
||||
out);
|
||||
return;
|
||||
}
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
|
||||
out);
|
||||
return;
|
||||
}
|
||||
if (format[1] == 'L' && !format[2])
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] - linbuf[from], out);
|
||||
return;
|
||||
}
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] - linbuf[from], out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (; from < upto; from++)
|
||||
{
|
||||
register char c;
|
||||
register char const *f = format;
|
||||
char c;
|
||||
char const *f = format;
|
||||
|
||||
while ((c = *f++) != 0)
|
||||
{
|
||||
char const *f1 = f;
|
||||
if (c == '%')
|
||||
switch ((c = *f++))
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
while ((c = *f++))
|
||||
{
|
||||
char const *f1 = f;
|
||||
if (c == '%')
|
||||
{
|
||||
c = *f++;
|
||||
switch (c)
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
output_1_line (linbuf[from],
|
||||
(linbuf[from + 1]
|
||||
- (linbuf[from + 1][-1] == '\n')),
|
||||
0, 0);
|
||||
continue;
|
||||
case 'l':
|
||||
output_1_line (linbuf[from],
|
||||
(linbuf[from + 1]
|
||||
- (linbuf[from + 1][-1] == '\n')),
|
||||
nullptr, nullptr);
|
||||
continue;
|
||||
|
||||
case 'L':
|
||||
output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
|
||||
continue;
|
||||
case 'L':
|
||||
output_1_line (linbuf[from], linbuf[from + 1],
|
||||
nullptr, nullptr);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, file, from, 0);
|
||||
if (f)
|
||||
continue;
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, file, from, nullptr);
|
||||
if (f)
|
||||
continue;
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putc (c, out);
|
||||
}
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char const *
|
||||
do_printf_spec (FILE *out, char const *spec,
|
||||
struct file_data const *file, lin n,
|
||||
struct group const *groups)
|
||||
struct file_data const *file, lin n,
|
||||
struct group const *groups)
|
||||
{
|
||||
char const *f = spec;
|
||||
char c;
|
||||
char c1;
|
||||
|
||||
/* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
|
||||
/* assert (*f == '%'); */
|
||||
dassert (*f == '%');
|
||||
f++;
|
||||
while ((c = *f++) == '-' || c == '\'' || c == '0')
|
||||
continue;
|
||||
while (ISDIGIT (c))
|
||||
while (c_isdigit (c))
|
||||
c = *f++;
|
||||
if (c == '.')
|
||||
while (ISDIGIT (c = *f++))
|
||||
while (c_isdigit (c = *f++))
|
||||
continue;
|
||||
c1 = *f++;
|
||||
char c1 = *f++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'c':
|
||||
if (c1 != '\'')
|
||||
return 0;
|
||||
return nullptr;
|
||||
else
|
||||
{
|
||||
char value IF_LINT (= 0);
|
||||
f = scan_char_literal (f, &value);
|
||||
if (!f)
|
||||
return 0;
|
||||
if (out)
|
||||
putc (value, out);
|
||||
}
|
||||
{
|
||||
char value;
|
||||
f = scan_char_literal (f, &value);
|
||||
if (!f)
|
||||
return nullptr;
|
||||
if (out)
|
||||
putc (value, out);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd': case 'o': case 'x': case 'X':
|
||||
{
|
||||
lin value;
|
||||
lin value;
|
||||
|
||||
if (file)
|
||||
{
|
||||
if (c1 != 'n')
|
||||
return 0;
|
||||
value = translate_line_number (file, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = groups_letter_value (groups, c1);
|
||||
if (value < 0)
|
||||
return 0;
|
||||
}
|
||||
if (file)
|
||||
{
|
||||
if (c1 != 'n')
|
||||
return nullptr;
|
||||
value = translate_line_number (file, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = groups_letter_value (groups, c1);
|
||||
if (value < 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (out)
|
||||
{
|
||||
/* For example, if the spec is "%3xn" and pI is "l", use the printf
|
||||
format spec "%3lx". Here the spec prefix is "%3". */
|
||||
printint print_value = value;
|
||||
size_t spec_prefix_len = f - spec - 2;
|
||||
size_t pI_len = sizeof pI - 1;
|
||||
#if 0
|
||||
char format[spec_prefix_len + pI_len + 2];
|
||||
#else
|
||||
char *format = xmalloc (spec_prefix_len + pI_len + 2);
|
||||
#endif
|
||||
char *p = format + spec_prefix_len + pI_len;
|
||||
memcpy (format, spec, spec_prefix_len);
|
||||
memcpy (format + spec_prefix_len, pI, pI_len);
|
||||
*p++ = c;
|
||||
*p = '\0';
|
||||
fprintf (out, format, print_value);
|
||||
#if ! HAVE_C_VARARRAYS
|
||||
free (format);
|
||||
#endif
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
/* For example, if the spec is "%3xn" and pI is "l", use the printf
|
||||
format spec "%3lx". Here the spec prefix is "%3". */
|
||||
idx_t spec_prefix_len = f - spec - 2;
|
||||
idx_t pI_len = sizeof pI - 1;
|
||||
char *format = xmalloca (spec_prefix_len + pI_len + 2);
|
||||
char *p = mempcpy (format, spec, spec_prefix_len);
|
||||
p = stpcpy (p, pI);
|
||||
*p++ = c;
|
||||
*p = '\0';
|
||||
fprintf (out, format, value);
|
||||
freea (format);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return f;
|
||||
@ -394,36 +384,35 @@ do_printf_spec (FILE *out, char const *spec,
|
||||
static char const *
|
||||
scan_char_literal (char const *lit, char *valptr)
|
||||
{
|
||||
register char const *p = lit;
|
||||
char const *p = lit;
|
||||
char value;
|
||||
ptrdiff_t digits;
|
||||
char c = *p++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
case '\'':
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
case '\\':
|
||||
value = 0;
|
||||
while ((c = *p++) != '\'')
|
||||
{
|
||||
unsigned int digit = c - '0';
|
||||
if (8 <= digit)
|
||||
return NULL;
|
||||
value = 8 * value + digit;
|
||||
}
|
||||
digits = p - lit - 2;
|
||||
if (! (1 <= digits && digits <= 3))
|
||||
return NULL;
|
||||
break;
|
||||
value = '\0';
|
||||
while ((c = *p++) != '\'')
|
||||
{
|
||||
unsigned int digit = c - '0';
|
||||
if (8 <= digit)
|
||||
return nullptr;
|
||||
value = 8 * value + digit;
|
||||
}
|
||||
ptrdiff_t digits = p - lit - 2;
|
||||
if (! (1 <= digits && digits <= 3))
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = c;
|
||||
if (*p++ != '\'')
|
||||
return NULL;
|
||||
break;
|
||||
value = c;
|
||||
if (*p++ != '\'')
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
*valptr = value;
|
||||
|
||||
34
src/normal.c
34
src/normal.c
@ -1,6 +1,6 @@
|
||||
/* Normal-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2017
|
||||
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2026
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@ -38,10 +38,8 @@ print_normal_script (struct change *script)
|
||||
static void
|
||||
print_normal_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
register lin i;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
return;
|
||||
@ -50,23 +48,21 @@ print_normal_hunk (struct change *hunk)
|
||||
|
||||
/* Print out the line number header for this hunk */
|
||||
set_color_context (LINE_NUMBER_CONTEXT);
|
||||
print_number_range (',', &files[0], first0, last0);
|
||||
print_number_range (',', &curr.file[0], first0, last0);
|
||||
fputc (change_letter[changes], outfile);
|
||||
print_number_range (',', &files[1], first1, last1);
|
||||
print_number_range (',', &curr.file[1], first1, last1);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
fputc ('\n', outfile);
|
||||
|
||||
/* Print the lines that the first file has. */
|
||||
if (changes & OLD)
|
||||
{
|
||||
if (first0 <= last0)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
for (i = first0; i <= last0; i++)
|
||||
for (lin i = first0; i <= last0; i++)
|
||||
{
|
||||
print_1_line_nl ("<", &files[0].linbuf[i], true);
|
||||
if (i == last0)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[0].linbuf[i + 1][-1] == '\n')
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
print_1_line_nl ("<", &curr.file[0].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[0].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
}
|
||||
@ -77,14 +73,12 @@ print_normal_hunk (struct change *hunk)
|
||||
/* Print the lines that the second file has. */
|
||||
if (changes & NEW)
|
||||
{
|
||||
if (first1 <= last1)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
for (i = first1; i <= last1; i++)
|
||||
for (lin i = first1; i <= last1; i++)
|
||||
{
|
||||
print_1_line_nl (">", &files[1].linbuf[i], true);
|
||||
if (i == last1)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[1].linbuf[i + 1][-1] == '\n')
|
||||
set_color_context (ADD_CONTEXT);
|
||||
print_1_line_nl (">", &curr.file[1].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[1].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
}
|
||||
|
||||
1002
src/sdiff.c
1002
src/sdiff.c
File diff suppressed because it is too large
Load Diff
312
src/side.c
312
src/side.c
@ -1,6 +1,6 @@
|
||||
/* sdiff-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2017 Free
|
||||
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2026 Free
|
||||
Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <wchar.h>
|
||||
#include <mcel.h>
|
||||
|
||||
static void print_sdiff_common_lines (lin, lin);
|
||||
static void print_sdiff_hunk (struct change *);
|
||||
@ -37,157 +37,169 @@ print_sdiff_script (struct change *script)
|
||||
{
|
||||
begin_output ();
|
||||
|
||||
next0 = next1 = - files[0].prefix_lines;
|
||||
next0 = next1 = - curr.file[0].prefix_lines;
|
||||
print_script (script, find_change, print_sdiff_hunk);
|
||||
|
||||
print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
|
||||
print_sdiff_common_lines (curr.file[0].valid_lines,
|
||||
curr.file[1].valid_lines);
|
||||
}
|
||||
|
||||
/* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */
|
||||
|
||||
static size_t
|
||||
tab_from_to (size_t from, size_t to)
|
||||
static intmax_t
|
||||
tab_from_to (intmax_t from, intmax_t to)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
size_t tab;
|
||||
size_t tab_size = tabsize;
|
||||
|
||||
if (!expand_tabs)
|
||||
for (tab = from + tab_size - from % tab_size; tab <= to; tab += tab_size)
|
||||
{
|
||||
putc ('\t', out);
|
||||
from = tab;
|
||||
}
|
||||
{
|
||||
intmax_t tab_size = tabsize;
|
||||
for (intmax_t tab = from + tab_size - from % tab_size;
|
||||
tab <= to; tab += tab_size)
|
||||
{
|
||||
putc ('\t', out);
|
||||
from = tab;
|
||||
}
|
||||
}
|
||||
while (from++ < to)
|
||||
putc (' ', out);
|
||||
return to;
|
||||
}
|
||||
|
||||
/* Print the text for half an sdiff line. This means truncate to
|
||||
width observing tabs, and trim a trailing newline. Return the
|
||||
last column written (not the number of chars). */
|
||||
OUT_BOUND columns, observing tabs, and trim a trailing newline.
|
||||
Return the presumed column position on the output device after
|
||||
the write (not the number of chars). */
|
||||
|
||||
static size_t
|
||||
print_half_line (char const *const *line, size_t indent, size_t out_bound)
|
||||
static intmax_t
|
||||
print_half_line (char const *const *line, intmax_t indent, intmax_t out_bound)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
register size_t in_position = 0;
|
||||
register size_t out_position = 0;
|
||||
register char const *text_pointer = line[0];
|
||||
register char const *text_limit = line[1];
|
||||
mbstate_t mbstate = { 0 };
|
||||
/* IN_POSITION is the current column position if we were outputting the
|
||||
entire line, i.e. ignoring OUT_BOUND. */
|
||||
intmax_t in_position = 0;
|
||||
/* OUT_POSITION is the current column position. It stays <= OUT_BOUND
|
||||
at any moment. */
|
||||
intmax_t out_position = 0;
|
||||
char const *text_pointer = line[0];
|
||||
char const *text_limit = line[1];
|
||||
|
||||
while (text_pointer < text_limit)
|
||||
{
|
||||
char const *tp0 = text_pointer;
|
||||
register char c = *text_pointer++;
|
||||
char c = *text_pointer++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\t':
|
||||
{
|
||||
size_t spaces = tabsize - in_position % tabsize;
|
||||
if (in_position == out_position)
|
||||
{
|
||||
case '\t':
|
||||
{
|
||||
intmax_t spaces = tabsize - in_position % tabsize;
|
||||
intmax_t tabstop;
|
||||
if (ckd_add (&tabstop, in_position, spaces))
|
||||
return out_position;
|
||||
if (in_position == out_position)
|
||||
{
|
||||
if (expand_tabs)
|
||||
{
|
||||
if (out_bound < tabstop)
|
||||
tabstop = out_bound;
|
||||
for (; out_position < tabstop; out_position++)
|
||||
putc (' ', out);
|
||||
}
|
||||
else
|
||||
if (tabstop < out_bound)
|
||||
{
|
||||
out_position = tabstop;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
in_position = tabstop;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
{
|
||||
putc (c, out);
|
||||
tab_from_to (0, indent);
|
||||
in_position = out_position = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (in_position != 0 && --in_position < out_bound)
|
||||
{
|
||||
if (out_position <= in_position)
|
||||
/* Add spaces to make up for suppressed tab past out_bound. */
|
||||
for (; out_position < in_position; out_position++)
|
||||
putc (' ', out);
|
||||
else
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
/* A byte that might start a multibyte character.
|
||||
Increase TEXT_POINTER, counting columns.
|
||||
Assume encoding errors have print width 1. */
|
||||
mcel_t g = mcel_scan (tp0, text_limit);
|
||||
int width = g.err ? 1 : c32width (g.ch);
|
||||
if (0 < width && ckd_add (&in_position, in_position, width))
|
||||
return out_position;
|
||||
|
||||
/* If there is room, output the bytes since TP0. */
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
size_t tabstop = out_position + spaces;
|
||||
if (expand_tabs)
|
||||
{
|
||||
if (out_bound < tabstop)
|
||||
tabstop = out_bound;
|
||||
for (; out_position < tabstop; out_position++)
|
||||
putc (' ', out);
|
||||
}
|
||||
else
|
||||
if (tabstop < out_bound)
|
||||
{
|
||||
out_position = tabstop;
|
||||
putc (c, out);
|
||||
}
|
||||
out_position = in_position;
|
||||
fwrite (tp0, 1, g.len, out);
|
||||
}
|
||||
in_position += spaces;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
{
|
||||
putc (c, out);
|
||||
tab_from_to (0, indent);
|
||||
in_position = out_position = 0;
|
||||
}
|
||||
break;
|
||||
text_pointer = tp0 + g.len;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (in_position != 0 && --in_position < out_bound)
|
||||
{
|
||||
if (out_position <= in_position)
|
||||
/* Add spaces to make up for suppressed tab past out_bound. */
|
||||
for (; out_position < in_position; out_position++)
|
||||
putc (' ', out);
|
||||
else
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
wchar_t wc;
|
||||
size_t bytes = mbrtowc (&wc, tp0, text_limit - tp0, &mbstate);
|
||||
|
||||
if (0 < bytes && bytes < (size_t) -2)
|
||||
{
|
||||
int width = wcwidth (wc);
|
||||
if (0 < width)
|
||||
in_position += width;
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
out_position = in_position;
|
||||
fwrite (tp0, 1, bytes, stdout);
|
||||
}
|
||||
text_pointer = tp0 + bytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case '\f':
|
||||
case '\v':
|
||||
if (in_position < out_bound)
|
||||
putc (c, out);
|
||||
break;
|
||||
|
||||
case ' ': case '!': case '"': case '#': case '%':
|
||||
case '&': case '\'': case '(': case ')': case '*':
|
||||
case '+': case ',': case '-': case '.': case '/':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case ':': case ';': case '<': case '=': case '>':
|
||||
case '?':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
||||
case 'Z':
|
||||
case '[': case '\\': case ']': case '^': case '_':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z': case '{': case '|': case '}': case '~':
|
||||
/* These characters are printable ASCII characters. */
|
||||
if (in_position++ < out_bound)
|
||||
/* Print width 1. */
|
||||
case ' ': case '!': case '"': case '#': case '$': case '%':
|
||||
case '&': case '\'': case '(': case ')': case '*':
|
||||
case '+': case ',': case '-': case '.': case '/':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case ':': case ';': case '<': case '=': case '>':
|
||||
case '?': case '@':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
||||
case 'Z':
|
||||
case '[': case '\\': case ']': case '^': case '_': case '`':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z': case '{': case '|': case '}': case '~':
|
||||
if (ckd_add (&in_position, in_position, 1))
|
||||
return out_position;
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
return out_position;
|
||||
}
|
||||
/* Print width 0. */
|
||||
case '\0': case '\a': case '\f': case '\v':
|
||||
if (in_position <= out_bound)
|
||||
putc (c, out);
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
return out_position;
|
||||
}
|
||||
}
|
||||
|
||||
return out_position;
|
||||
@ -199,12 +211,12 @@ print_half_line (char const *const *line, size_t indent, size_t out_bound)
|
||||
|
||||
static void
|
||||
print_1sdiff_line (char const *const *left, char sep,
|
||||
char const *const *right)
|
||||
char const *const *right)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
size_t hw = sdiff_half_width;
|
||||
size_t c2o = sdiff_column2_offset;
|
||||
size_t col = 0;
|
||||
intmax_t hw = sdiff_half_width;
|
||||
intmax_t c2o = sdiff_column2_offset;
|
||||
intmax_t col = 0;
|
||||
bool put_newline = false;
|
||||
bool color_to_reset = false;
|
||||
|
||||
@ -227,9 +239,9 @@ print_1sdiff_line (char const *const *left, char sep,
|
||||
|
||||
if (sep != ' ')
|
||||
{
|
||||
col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
|
||||
col = tab_from_to (col, (hw + c2o - 1) >> 1) + 1;
|
||||
if (sep == '|' && put_newline != (right[1][-1] == '\n'))
|
||||
sep = put_newline ? '/' : '\\';
|
||||
sep = put_newline ? '/' : '\\';
|
||||
putc (sep, out);
|
||||
}
|
||||
|
||||
@ -237,10 +249,10 @@ print_1sdiff_line (char const *const *left, char sep,
|
||||
{
|
||||
put_newline |= right[1][-1] == '\n';
|
||||
if (**right != '\n')
|
||||
{
|
||||
col = tab_from_to (col, c2o);
|
||||
print_half_line (right, col, hw);
|
||||
}
|
||||
{
|
||||
col = tab_from_to (col, c2o);
|
||||
print_half_line (right, col, hw);
|
||||
}
|
||||
}
|
||||
|
||||
if (put_newline)
|
||||
@ -259,22 +271,18 @@ print_sdiff_common_lines (lin limit0, lin limit1)
|
||||
if (!suppress_common_lines && (i0 != limit0 || i1 != limit1))
|
||||
{
|
||||
if (sdiff_merge_assist)
|
||||
{
|
||||
printint len0 = limit0 - i0;
|
||||
printint len1 = limit1 - i1;
|
||||
fprintf (outfile, "i%"pI"d,%"pI"d\n", len0, len1);
|
||||
}
|
||||
fprintf (outfile, "i%"pI"d,%"pI"d\n", limit0 - i0, limit1 - i1);
|
||||
|
||||
if (!left_column)
|
||||
{
|
||||
while (i0 != limit0 && i1 != limit1)
|
||||
print_1sdiff_line (&files[0].linbuf[i0++], ' ',
|
||||
&files[1].linbuf[i1++]);
|
||||
while (i1 != limit1)
|
||||
print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
|
||||
}
|
||||
{
|
||||
while (i0 != limit0 && i1 != limit1)
|
||||
print_1sdiff_line (&curr.file[0].linbuf[i0++], ' ',
|
||||
&curr.file[1].linbuf[i1++]);
|
||||
while (i1 != limit1)
|
||||
print_1sdiff_line (0, ')', &curr.file[1].linbuf[i1++]);
|
||||
}
|
||||
while (i0 != limit0)
|
||||
print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
|
||||
print_1sdiff_line (&curr.file[0].linbuf[i0++], '(', 0);
|
||||
}
|
||||
|
||||
next0 = limit0;
|
||||
@ -288,10 +296,8 @@ print_sdiff_common_lines (lin limit0, lin limit1)
|
||||
static void
|
||||
print_sdiff_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
register lin i, j;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes =
|
||||
analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
@ -301,17 +307,17 @@ print_sdiff_hunk (struct change *hunk)
|
||||
print_sdiff_common_lines (first0, first1);
|
||||
|
||||
if (sdiff_merge_assist)
|
||||
{
|
||||
printint len0 = last0 - first0 + 1;
|
||||
printint len1 = last1 - first1 + 1;
|
||||
fprintf (outfile, "c%"pI"d,%"pI"d\n", len0, len1);
|
||||
}
|
||||
fprintf (outfile, "c%"pI"d,%"pI"d\n",
|
||||
last0 - first0 + 1,
|
||||
last1 - first1 + 1);
|
||||
|
||||
/* Print "xxx | xxx " lines. */
|
||||
if (changes == CHANGED)
|
||||
{
|
||||
lin i, j;
|
||||
for (i = first0, j = first1; i <= last0 && j <= last1; i++, j++)
|
||||
print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
|
||||
print_1sdiff_line (&curr.file[0].linbuf[i], '|',
|
||||
&curr.file[1].linbuf[j]);
|
||||
changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0);
|
||||
next0 = first0 = i;
|
||||
next1 = first1 = j;
|
||||
@ -320,16 +326,18 @@ print_sdiff_hunk (struct change *hunk)
|
||||
/* Print " > xxx " lines. */
|
||||
if (changes & NEW)
|
||||
{
|
||||
lin j;
|
||||
for (j = first1; j <= last1; ++j)
|
||||
print_1sdiff_line (0, '>', &files[1].linbuf[j]);
|
||||
print_1sdiff_line (0, '>', &curr.file[1].linbuf[j]);
|
||||
next1 = j;
|
||||
}
|
||||
|
||||
/* Print "xxx < " lines. */
|
||||
if (changes & OLD)
|
||||
{
|
||||
lin i;
|
||||
for (i = first0; i <= last0; ++i)
|
||||
print_1sdiff_line (&files[0].linbuf[i], '<', 0);
|
||||
print_1sdiff_line (&curr.file[0].linbuf[i], '<', 0);
|
||||
next0 = i;
|
||||
}
|
||||
}
|
||||
|
||||
350
src/syncsig.c
Normal file
350
src/syncsig.c
Normal file
@ -0,0 +1,350 @@
|
||||
/* Synchronous signal handling
|
||||
|
||||
Copyright 2025-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "syncsig.h"
|
||||
|
||||
#include "minmax.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdcountof.h>
|
||||
|
||||
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
|
||||
present. */
|
||||
#ifndef SA_NOCLDSTOP
|
||||
# define SA_NOCLDSTOP 0
|
||||
# define sigprocmask(how, set, oset) 0
|
||||
# if ! HAVE_SIGINTERRUPT
|
||||
# define siginterrupt(sig, flag) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SA_RESTART
|
||||
# define SA_RESTART 0
|
||||
#endif
|
||||
#ifndef SIGSTOP
|
||||
# define SIGSTOP 0
|
||||
#endif
|
||||
|
||||
/* The set of signals that are caught. */
|
||||
static sigset_t caught_signals;
|
||||
|
||||
/* The signals we can catch.
|
||||
This includes all catchable GNU/Linux or POSIX signals that by
|
||||
default are ignored, or that stop or terminate the process.
|
||||
It also includes SIGQUIT since that can come from the terminal.
|
||||
It excludes other signals that normally come from program failure.
|
||||
If you modify this table, also modify signal_count's initializer. */
|
||||
static int const catchable[] =
|
||||
{
|
||||
/* SIGABRT is normally from program failure. */
|
||||
SIGALRM,
|
||||
/* SIGBUS is normally from program failure. */
|
||||
#ifdef SIGCHLD
|
||||
SIGCHLD,
|
||||
#else
|
||||
# define SIGCHLD 0
|
||||
#endif
|
||||
/* SIGCLD (older platforms) is an alias for SIGCHLD. */
|
||||
#ifdef SIGCONT
|
||||
SIGCONT,
|
||||
#else
|
||||
# define SIGCONT 0
|
||||
#endif
|
||||
/* SIGEMT is normally from program failure. */
|
||||
/* SIGFPE is normally from program failure. */
|
||||
SIGHUP,
|
||||
/* SIGILL is normally from program failure. */
|
||||
/* SIGINFO is an alias for SIGPWR on Linux. */
|
||||
SIGINT,
|
||||
/* SIGIO is an alias for SIGPOLL. */
|
||||
/* SIGKILL can't be caught. */
|
||||
#ifdef SIGLOST
|
||||
SIGLOST,
|
||||
#else
|
||||
# define SIGLOST 0
|
||||
#endif
|
||||
SIGPIPE,
|
||||
#ifdef SIGPOLL /* Removed from POSIX.1-2024; still in Linux. */
|
||||
SIGPOLL,
|
||||
#else
|
||||
# define SIGPOLL 0
|
||||
#endif
|
||||
#ifdef SIGPROF /* Removed from POSIX.1-2024; still in Linux. */
|
||||
SIGPROF,
|
||||
#else
|
||||
# define SIGPROF 0
|
||||
#endif
|
||||
#ifdef SIGPWR
|
||||
SIGPWR,
|
||||
#else
|
||||
# define SIGPWR 0
|
||||
#endif
|
||||
SIGQUIT,
|
||||
/* SIGSEGV is normally from program failure. */
|
||||
/* Linux SIGSTKFLT is unused. */
|
||||
/* SIGSTOP can't be caught. */
|
||||
/* SIGSYS is normally from program failure. */
|
||||
SIGTERM,
|
||||
/* SIGTRAP is normally from program failure. */
|
||||
#ifdef SIGTSTP
|
||||
SIGTSTP,
|
||||
#else
|
||||
# define SIGTSTP 0
|
||||
#endif
|
||||
#ifdef SIGTTIN
|
||||
SIGTTIN,
|
||||
#else
|
||||
# define SIGTTIN 0
|
||||
#endif
|
||||
#ifdef SIGTTOU
|
||||
SIGTTOU,
|
||||
#else
|
||||
# define SIGTTOU 0
|
||||
#endif
|
||||
#ifdef SIGURG
|
||||
SIGURG,
|
||||
#else
|
||||
# define SIGURG 0
|
||||
#endif
|
||||
#ifdef SIGUSR1
|
||||
SIGUSR1,
|
||||
#else
|
||||
# define SIGUSR1 0
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
SIGUSR2,
|
||||
#else
|
||||
# define SIGUSR2 0
|
||||
#endif
|
||||
#ifdef SIGVTALRM
|
||||
SIGVTALRM,
|
||||
#else
|
||||
# define SIGVTALRM 0
|
||||
#endif
|
||||
#ifdef SIGWINCH
|
||||
SIGWINCH,
|
||||
#else
|
||||
# define SIGWINCH 0
|
||||
#endif
|
||||
#ifdef SIGXCPU
|
||||
SIGXCPU,
|
||||
#else
|
||||
# define SIGXCPU 0
|
||||
#endif
|
||||
#ifdef SIGXFSZ
|
||||
SIGXFSZ,
|
||||
#else
|
||||
# define SIGXFSZ 0
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Number of pending signals received, for each signal type. */
|
||||
static sig_atomic_t volatile signal_count[] =
|
||||
{
|
||||
/* Explicitly initialize, so that the table is large enough. */
|
||||
[SIGALRM] = 0,
|
||||
[SIGCHLD] = 0,
|
||||
[SIGCONT] = 0,
|
||||
[SIGHUP] = 0,
|
||||
[SIGINT] = 0,
|
||||
[SIGLOST] = 0,
|
||||
[SIGPIPE] = 0,
|
||||
[SIGPOLL] = 0,
|
||||
[SIGPROF] = 0,
|
||||
[SIGPWR] = 0,
|
||||
[SIGQUIT] = 0,
|
||||
[SIGTERM] = 0,
|
||||
[SIGTSTP] = 0,
|
||||
[SIGTTIN] = 0,
|
||||
[SIGTTOU] = 0,
|
||||
[SIGURG] = 0,
|
||||
[SIGUSR1] = 0,
|
||||
[SIGUSR2] = 0,
|
||||
[SIGVTALRM] = 0,
|
||||
[SIGWINCH] = 0,
|
||||
[SIGXCPU] = 0,
|
||||
[SIGXFSZ] = 0,
|
||||
};
|
||||
|
||||
/* This acts as bool though its type is sig_atomic_t.
|
||||
If true, signal_count might contain nonzero entries.
|
||||
If false, signal_count is all zero.
|
||||
This is to speed up syncsig_poll when it returns 0. */
|
||||
static sig_atomic_t volatile possible_signal_count;
|
||||
|
||||
/* Actions before syncsig_install was called. */
|
||||
#if SA_NOCLDSTOP
|
||||
static struct sigaction oldact[countof (signal_count)];
|
||||
#else
|
||||
static void (*oldact[countof (signal_count)]) (int);
|
||||
#endif
|
||||
|
||||
/* Record an asynchronous signal. This function is async-signal-safe. */
|
||||
static void
|
||||
sighandler (int sig)
|
||||
{
|
||||
#if !SA_NOCLDSTOP
|
||||
/* An unavoidable race here: the default action might mistakenly
|
||||
be taken before 'signal' is called. */
|
||||
signal (sig, sighandler);
|
||||
#endif
|
||||
|
||||
possible_signal_count = true;
|
||||
signal_count[sig]++;
|
||||
}
|
||||
|
||||
void
|
||||
syncsig_install (int flags)
|
||||
{
|
||||
for (int i = 0; i < countof (catchable); i++)
|
||||
signal_count[catchable[i]] = 0;
|
||||
possible_signal_count = false;
|
||||
|
||||
sigemptyset (&caught_signals);
|
||||
|
||||
for (int i = 0; i < countof (catchable); i++)
|
||||
{
|
||||
int sig = catchable[i];
|
||||
|
||||
if (((sig == SIGTSTP) & !(flags & SYNCSIG_TSTP))
|
||||
| ((sig == SIGTTIN) & !(flags & SYNCSIG_TTIN))
|
||||
| ((sig == SIGTTOU) & !(flags & SYNCSIG_TTOU)))
|
||||
continue;
|
||||
|
||||
#if SA_NOCLDSTOP
|
||||
sigaction (sig, nullptr, &oldact[sig]);
|
||||
bool catching_sig = oldact[sig].sa_handler != SIG_IGN;
|
||||
#else
|
||||
oldact[i] = signal (sig, SIG_IGN);
|
||||
bool catching_sig = oldact[i] != SIG_IGN;
|
||||
if (catching_sig)
|
||||
{
|
||||
/* An unavoidable race here: SIG might be mistakenly ignored
|
||||
before 'signal' is called. */
|
||||
signal (sig, sighandler);
|
||||
siginterrupt (sig, 0);
|
||||
}
|
||||
#endif
|
||||
if (catching_sig)
|
||||
sigaddset (&caught_signals, sig);
|
||||
}
|
||||
|
||||
#if SA_NOCLDSTOP
|
||||
struct sigaction act;
|
||||
act.sa_handler = sighandler;
|
||||
act.sa_mask = caught_signals;
|
||||
act.sa_flags = SA_RESTART;
|
||||
|
||||
for (int i = 0; i < countof (catchable); i++)
|
||||
{
|
||||
int sig = catchable[i];
|
||||
if (sigismember (&caught_signals, sig))
|
||||
sigaction (sig, &act, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
syncsig_uninstall (void)
|
||||
{
|
||||
for (int i = 0; i < countof (catchable); i++)
|
||||
{
|
||||
int sig = catchable[i];
|
||||
if (sigismember (&caught_signals, sig))
|
||||
{
|
||||
#if SA_NOCLDSTOP
|
||||
sigaction (sig, &oldact[sig], nullptr);
|
||||
#else
|
||||
signal (sig, oldact[sig]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
syncsig_poll (void)
|
||||
{
|
||||
int sig = 0;
|
||||
|
||||
if (possible_signal_count)
|
||||
{
|
||||
/* This module uses static rather than thread-local storage,
|
||||
so it is useful only in single-threaded programs,
|
||||
and it uses sigprocmask rather than pthread_sigmask. */
|
||||
sigset_t oldset;
|
||||
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
if (i == countof (catchable))
|
||||
{
|
||||
possible_signal_count = false;
|
||||
break;
|
||||
}
|
||||
int s = catchable[i];
|
||||
int c = signal_count[s];
|
||||
if (c)
|
||||
{
|
||||
signal_count[s] = c - 1;
|
||||
sig = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sigprocmask (SIG_SETMASK, &oldset, nullptr);
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
void
|
||||
syncsig_deliver (int sig)
|
||||
{
|
||||
#if SA_NOCLDSTOP
|
||||
struct sigaction act;
|
||||
#else
|
||||
void (*act) (int);
|
||||
#endif
|
||||
|
||||
if (sig == SIGTSTP)
|
||||
sig = SIGSTOP;
|
||||
else
|
||||
{
|
||||
#if SA_NOCLDSTOP
|
||||
sigaction (sig, &oldact[sig], &act);
|
||||
#else
|
||||
act = signal (sig, oldact[sig]);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
raise (sig);
|
||||
|
||||
if (sig != SIGSTOP)
|
||||
{
|
||||
/* The program did not exit due to the raised signal, so continue. */
|
||||
#if SA_NOCLDSTOP
|
||||
sigaction (sig, &act, nullptr);
|
||||
#else
|
||||
signal (sig, act);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
60
src/syncsig.h
Normal file
60
src/syncsig.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* Synchronous signal handling
|
||||
|
||||
Copyright 2025-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Paul Eggert. */
|
||||
|
||||
/* Flags for syncsig_install. */
|
||||
enum
|
||||
{
|
||||
/* Also catch SIGTSTP, SIGTTIN, SIGTTOU, which by default stop the process.
|
||||
These flags have no effect on platforms lacking these signals. */
|
||||
SYNCSIG_TSTP = (1 << 0),
|
||||
SYNCSIG_TTIN = (1 << 1),
|
||||
SYNCSIG_TTOU = (1 << 2),
|
||||
};
|
||||
|
||||
/* Set up asynchronous signal handling according to FLAGS.
|
||||
syncsig_install fails only on unusual platforms where
|
||||
valid calls to functions like sigaction can fail;
|
||||
if it fails, signal handling is in a weird state
|
||||
and neither syncsig_process nor syncsig_uninstall should be called. */
|
||||
extern void syncsig_install (int flags);
|
||||
|
||||
/* Return a signal number if a signal has arrived, zero otherwise.
|
||||
After syncsig_install, there should not be an unbounded amount of
|
||||
time between calls to this function, and its result should be dealt
|
||||
with promptly. */
|
||||
extern int syncsig_poll (void);
|
||||
|
||||
/* Do the action for SIG that would have been done
|
||||
had syncsig_install not been called.
|
||||
SIG should have recently been returned by syncsig_poll.
|
||||
|
||||
For example, if SIG is SIGTSTP stop the process and return after
|
||||
SIGCONT is delivered. Another example: kill the process if SIG is
|
||||
SIGINT and SIGINT handling is the default.
|
||||
|
||||
This function should be called only after syncsig_install. */
|
||||
extern void syncsig_deliver (int sig);
|
||||
|
||||
/* Stop doing asynchronous signal handling, undoing syncsig_install.
|
||||
This function should be called only after syncsig_install.
|
||||
To deal with signals arriving just before calling this function,
|
||||
call syncsig_poll afterwards. */
|
||||
extern void syncsig_uninstall (void);
|
||||
163
src/system.c
Normal file
163
src/system.c
Normal file
@ -0,0 +1,163 @@
|
||||
/* System dependent declarations.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define SYSTEM_INLINE _GL_EXTERN_INLINE
|
||||
#include "system.h"
|
||||
|
||||
/* Do struct stat *S, *T describe the same file? */
|
||||
bool
|
||||
same_file (struct stat const *s, struct stat const *t)
|
||||
{
|
||||
if (! SAME_INODE (*s, *t))
|
||||
{
|
||||
# if HAVE_STRUCT_STAT_ST_RDEV
|
||||
/* Two character special files describe the same device if st_rdev
|
||||
is the same, and likewise for block special devices.
|
||||
They have the same contents, so treat them as the same. */
|
||||
if (((S_ISCHR (s->st_mode) && S_ISCHR (t->st_mode))
|
||||
|| (S_ISBLK (s->st_mode) && S_ISBLK (t->st_mode)))
|
||||
&& s->st_rdev == t->st_rdev)
|
||||
return true;
|
||||
# endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Although POSIX says that two files are identical if st_ino and st_dev
|
||||
are the same, all too many file systems incorrectly assign the same
|
||||
(device, inode) pair to two distinct files, including:
|
||||
|
||||
- GNU/Linux NFS servers that export all local file systems as a
|
||||
single NFS file system, if a local (device, inode) pair collides
|
||||
with another one after hashing.
|
||||
|
||||
- GNU/Linux NFS servers that export Btrfs file systems with subvolumes,
|
||||
if the Btrfs (subvolume, inode) hashing function collides.
|
||||
See <https://lwn.net/Articles/866709/>.
|
||||
|
||||
- Qemu virtio-fs before Qemu 5.2 (2020); see
|
||||
<https://bugzilla.redhat.com/show_bug.cgi?id=1795362>.
|
||||
|
||||
- Network Appliance NFS servers in snapshot directories; see
|
||||
Network Appliance bug #195.
|
||||
|
||||
- ClearCase MVFS; see bug id ATRia04618.
|
||||
|
||||
Check whether two files that purport to be the same have the same
|
||||
attributes, to work around instances of this common bug.
|
||||
|
||||
Birthtime is special as st_birthtime is not portable, but when
|
||||
either birthtime is available comparing them should be definitive. */
|
||||
|
||||
#if (defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC \
|
||||
|| defined HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC \
|
||||
|| defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC \
|
||||
|| (defined _WIN32 && ! defined __CYGWIN__))
|
||||
/* If either file has a birth time, comparing them is definitive. */
|
||||
struct timespec sbirth = get_stat_birthtime (s);
|
||||
struct timespec tbirth = get_stat_birthtime (t);
|
||||
if (0 <= sbirth.tv_nsec || 0 <= tbirth.tv_nsec)
|
||||
return timespec_cmp (sbirth, tbirth) == 0;
|
||||
#endif
|
||||
|
||||
/* Fall back on comparing other easily-obtainable attributes.
|
||||
Do not inspect all attributes, only attributes useful in checking
|
||||
for the bug. Check attributes most likely to differ first.
|
||||
|
||||
It's possible for two distinct files on a buggy file system to have
|
||||
the same attributes, but it's not worth slowing down all
|
||||
implementations (or complicating the configuration) to cater to
|
||||
these rare cases in buggy implementations.
|
||||
|
||||
It's also possible for the same file to appear to be two different
|
||||
files, e.g., because its permissions were changed between the two
|
||||
stat calls. In that case cmp and diff will do extra work
|
||||
to determine that the file contents are the same. */
|
||||
|
||||
return (get_stat_ctime_ns (s) == get_stat_ctime_ns (t)
|
||||
&& get_stat_mtime_ns (s) == get_stat_mtime_ns (t)
|
||||
&& s->st_ctime == t->st_ctime
|
||||
&& s->st_mtime == t->st_mtime
|
||||
&& s->st_size == t->st_size
|
||||
&& s->st_mode == t->st_mode
|
||||
&& s->st_uid == t->st_uid
|
||||
&& s->st_gid == t->st_gid
|
||||
&& s->st_nlink == t->st_nlink);
|
||||
}
|
||||
|
||||
/* Use this for code that could be used if diff ever cares about
|
||||
st_size for symlinks, which it doesn't now. */
|
||||
#define care_about_symlink_size false
|
||||
|
||||
/* Return the number of bytes in the file described by *S,
|
||||
or -1 if this cannot be determined reliably. */
|
||||
off_t
|
||||
stat_size (struct stat const *s)
|
||||
{
|
||||
mode_t mode = s->st_mode;
|
||||
off_t size = s->st_size;
|
||||
if (size < 0)
|
||||
return -1;
|
||||
if (! (S_ISREG (mode) || (care_about_symlink_size && S_ISLNK (mode))
|
||||
|| S_TYPEISSHM (s) || S_TYPEISTMO (s)))
|
||||
return -1;
|
||||
|
||||
#if (defined __linux__ || defined __CYGWIN__ || defined __FreeBSD__ \
|
||||
|| defined __NetBSD__ || defined _AIX)
|
||||
/* On some systems, /proc files with size zero are suspect. */
|
||||
if (size == 0)
|
||||
{
|
||||
static dev_t proc_dev;
|
||||
if (!proc_dev)
|
||||
{
|
||||
struct stat st;
|
||||
st.st_dev = 0;
|
||||
lstat ("/proc/self", &st);
|
||||
proc_dev = st.st_dev;
|
||||
}
|
||||
if (proc_dev && s->st_dev == proc_dev)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#if care_about_symlink_size && (defined __linux__ || defined __ANDROID__)
|
||||
/* Symlinks have suspect sizes on Linux kernels before 5.15,
|
||||
due to bugs in fscrypt. */
|
||||
if (S_ISLNK (mode))
|
||||
{
|
||||
static signed char symlink_size_ok;
|
||||
if (! symlink_size_ok)
|
||||
{
|
||||
struct utsname name;
|
||||
uname (&name);
|
||||
char *p = name.release;
|
||||
symlink_size_ok = ((p[1] != '.' || '5' < p[0]
|
||||
|| (p[0] == '5'
|
||||
&& ('1' <= p[2] && p[2] <= '9')
|
||||
&& ('0' <= p[3] && p[3] <= '9')
|
||||
&& ('5' <= p[3]
|
||||
|| ('0' <= p[4] && p[4] <= '9'))))
|
||||
? 1 : -1);
|
||||
}
|
||||
if (symlink_size_ok < 0)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return size;
|
||||
}
|
||||
199
src/system.h
199
src/system.h
@ -1,7 +1,7 @@
|
||||
/* System dependent declarations.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@ -20,33 +20,15 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Use this to suppress gcc's "...may be used before initialized" warnings. */
|
||||
#ifdef lint
|
||||
# define IF_LINT(Code) Code
|
||||
#else
|
||||
# define IF_LINT(Code) /* empty */
|
||||
#endif
|
||||
|
||||
/* Define '__attribute__' and 'volatile' first
|
||||
so that they're used consistently in all system includes. */
|
||||
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#include <verify.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "stat-macros.h"
|
||||
|
||||
#ifndef STAT_BLOCKSIZE
|
||||
# if HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||
# define STAT_BLOCKSIZE(s) ((s).st_blksize)
|
||||
# else
|
||||
# define STAT_BLOCKSIZE(s) (8 * 1024)
|
||||
# endif
|
||||
#endif
|
||||
#include <stat-macros.h>
|
||||
#include <stat-size.h>
|
||||
#include <stat-time.h>
|
||||
#include <timespec.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@ -61,24 +43,14 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#define EXIT_TROUBLE 2
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <stdbit.h>
|
||||
#include <stdckdint.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <string.h>
|
||||
#if ! HAVE_STRCASECOLL
|
||||
# if HAVE_STRICOLL || defined stricoll
|
||||
# define strcasecoll(a, b) stricoll (a, b)
|
||||
# else
|
||||
# define strcasecoll(a, b) strcasecmp (a, b) /* best we can do */
|
||||
# endif
|
||||
#endif
|
||||
#if ! (HAVE_STRCASECMP || defined strcasecmp)
|
||||
int strcasecmp (char const *, char const *);
|
||||
#endif
|
||||
|
||||
#include <gettext.h>
|
||||
#if ! ENABLE_NLS
|
||||
@ -91,17 +63,6 @@ int strcasecmp (char const *, char const *);
|
||||
#define _(msgid) gettext (msgid)
|
||||
#define N_(msgid) msgid
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* ISDIGIT differs from isdigit, as follows:
|
||||
- Its arg may be any int or unsigned int; it need not be an unsigned char.
|
||||
- It's guaranteed to evaluate its argument exactly once.
|
||||
- It's typically faster.
|
||||
POSIX 1003.1-2001 says that only '0' through '9' are digits.
|
||||
Prefer ISDIGIT to isdigit unless it's important to use the locale's
|
||||
definition of 'digit' even when the host does not conform to POSIX. */
|
||||
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <signal.h>
|
||||
@ -109,55 +70,50 @@ int strcasecmp (char const *, char const *);
|
||||
# define SIGCHLD SIGCLD
|
||||
#endif
|
||||
|
||||
#undef MIN
|
||||
#undef MAX
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <attribute.h>
|
||||
#include <idx.h>
|
||||
#include <intprops.h>
|
||||
#include "propername.h"
|
||||
#include <minmax.h>
|
||||
#include <propername.h>
|
||||
#include <same-inode.h>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
/* Evaluate an assertion E that is guaranteed to be true.
|
||||
E should not crash, loop forever, or have side effects. */
|
||||
#if defined DDEBUG && !defined NDEBUG
|
||||
/* Abort the program if E is false. */
|
||||
# include <assert.h>
|
||||
# define dassert(e) assert (e)
|
||||
#else
|
||||
/* The compiler can assume E, as behavior is undefined otherwise. */
|
||||
# define dassert(e) assume (e)
|
||||
#endif
|
||||
|
||||
#ifndef SYSTEM_INLINE
|
||||
# define SYSTEM_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
|
||||
/* Type used for fast comparison of several bytes at a time.
|
||||
This used to be uintmax_t, but changing it to size_t
|
||||
The type is a pointer to an incomplete struct,
|
||||
so that its values are less likely to be misused.
|
||||
This used to be uintmax_t, but changing it to the size of a pointer
|
||||
made plain 'cmp' 90% faster (GCC 4.8.1, x86). */
|
||||
|
||||
#ifndef word
|
||||
# define word size_t
|
||||
typedef struct incomplete *word;
|
||||
#endif
|
||||
|
||||
/* The signed integer type of a line number. Since files are read
|
||||
into main memory, ptrdiff_t should be wide enough. */
|
||||
into main memory, ptrdiff_t should be wide enough. pI is for
|
||||
printing line numbers. */
|
||||
|
||||
typedef ptrdiff_t lin;
|
||||
#define LIN_MAX PTRDIFF_MAX
|
||||
|
||||
/* The signed integer type for printing line numbers, and its printf
|
||||
length modifier. This is not simply ptrdiff_t, to cater to older
|
||||
and/or nonstandard C libraries where "l" works but "ll" and "t" do
|
||||
not, or where 'long' is too narrow and "ll" works but "t" does not. */
|
||||
|
||||
#if LIN_MAX <= LONG_MAX
|
||||
typedef long int printint;
|
||||
# define pI "l"
|
||||
#elif LIN_MAX <= LLONG_MAX
|
||||
typedef long long int printint;
|
||||
# define pI "ll"
|
||||
#else
|
||||
typedef ptrdiff_t printint;
|
||||
# define pI "t"
|
||||
#endif
|
||||
|
||||
verify (TYPE_SIGNED (lin));
|
||||
verify (TYPE_SIGNED (printint));
|
||||
verify (LIN_MAX == TYPE_MAXIMUM (lin));
|
||||
verify (LIN_MAX <= TYPE_MAXIMUM (printint));
|
||||
|
||||
/* Limit so that 2 * CONTEXT + 1 does not overflow. */
|
||||
|
||||
#define CONTEXT_MAX ((LIN_MAX - 1) / 2)
|
||||
|
||||
#define pI "t"
|
||||
static_assert (LIN_MAX == IDX_MAX);
|
||||
|
||||
/* This section contains POSIX-compliant defaults for macros
|
||||
that are meant to be overridden by hand in config.h as needed. */
|
||||
@ -174,67 +130,20 @@ verify (LIN_MAX <= TYPE_MAXIMUM (printint));
|
||||
# define NULL_DEVICE "/dev/null"
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T describe the same special file? */
|
||||
#ifndef same_special_file
|
||||
# if HAVE_STRUCT_STAT_ST_RDEV && defined S_ISBLK && defined S_ISCHR
|
||||
# define same_special_file(s, t) \
|
||||
(((S_ISBLK ((s)->st_mode) && S_ISBLK ((t)->st_mode)) \
|
||||
|| (S_ISCHR ((s)->st_mode) && S_ISCHR ((t)->st_mode))) \
|
||||
&& (s)->st_rdev == (t)->st_rdev)
|
||||
# else
|
||||
# define same_special_file(s, t) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T describe the same file? Answer -1 if unknown. */
|
||||
#ifndef same_file
|
||||
# define same_file(s, t) \
|
||||
((((s)->st_ino == (t)->st_ino) && ((s)->st_dev == (t)->st_dev)) \
|
||||
|| same_special_file (s, t))
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T have the same file attributes?
|
||||
|
||||
POSIX says that two files are identical if st_ino and st_dev are
|
||||
the same, but many file systems incorrectly assign the same (device,
|
||||
inode) pair to two distinct files, including:
|
||||
|
||||
- GNU/Linux NFS servers that export all local file systems as a
|
||||
single NFS file system, if a local device number (st_dev) exceeds
|
||||
255, or if a local inode number (st_ino) exceeds 16777215.
|
||||
|
||||
- Network Appliance NFS servers in snapshot directories; see
|
||||
Network Appliance bug #195.
|
||||
|
||||
- ClearCase MVFS; see bug id ATRia04618.
|
||||
|
||||
Check whether two files that purport to be the same have the same
|
||||
attributes, to work around instances of this common bug. Do not
|
||||
inspect all attributes, only attributes useful in checking for this
|
||||
bug.
|
||||
|
||||
It's possible for two distinct files on a buggy file system to have
|
||||
the same attributes, but it's not worth slowing down all
|
||||
implementations (or complicating the configuration) to cater to
|
||||
these rare cases in buggy implementations. */
|
||||
|
||||
#ifndef same_file_attributes
|
||||
# define same_file_attributes(s, t) \
|
||||
((s)->st_mode == (t)->st_mode \
|
||||
&& (s)->st_nlink == (t)->st_nlink \
|
||||
&& (s)->st_uid == (t)->st_uid \
|
||||
&& (s)->st_gid == (t)->st_gid \
|
||||
&& (s)->st_size == (t)->st_size \
|
||||
&& (s)->st_mtime == (t)->st_mtime \
|
||||
&& (s)->st_ctime == (t)->st_ctime)
|
||||
#endif
|
||||
|
||||
#define STREQ(a, b) (strcmp (a, b) == 0)
|
||||
|
||||
#ifndef FALLTHROUGH
|
||||
# if __GNUC__ < 7
|
||||
# define FALLTHROUGH ((void) 0)
|
||||
# else
|
||||
# define FALLTHROUGH __attribute__ ((__fallthrough__))
|
||||
# endif
|
||||
#endif
|
||||
/* Return the floor of the log base 2 of N. Return -1 if N is zero. */
|
||||
SYSTEM_INLINE int floor_log2 (idx_t n)
|
||||
{
|
||||
static_assert (IDX_MAX <= SIZE_MAX);
|
||||
size_t s = n;
|
||||
int w = stdc_bit_width (s);
|
||||
return w - 1;
|
||||
}
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
extern bool same_file (struct stat const *, struct stat const *)
|
||||
ATTRIBUTE_PURE;
|
||||
extern off_t stat_size (struct stat const *)
|
||||
ATTRIBUTE_PURE;
|
||||
|
||||
1091
src/util.c
1091
src/util.c
File diff suppressed because it is too large
Load Diff
@ -5,25 +5,42 @@ TESTS = \
|
||||
bignum \
|
||||
binary \
|
||||
brief-vs-stat-zero-kernel-lies \
|
||||
bug-64316 \
|
||||
cmp \
|
||||
colliding-file-names \
|
||||
diff3 \
|
||||
empty-file \
|
||||
excess-slash \
|
||||
expand-tabs \
|
||||
help-version \
|
||||
ifdef \
|
||||
invalid-re \
|
||||
function-line-vs-leading-space \
|
||||
ignore-case \
|
||||
ignore-matching-lines \
|
||||
ignore-tab-expansion \
|
||||
label-vs-func \
|
||||
large-subopt \
|
||||
new-file \
|
||||
no-dereference \
|
||||
no-newline-at-eof \
|
||||
side-by-side \
|
||||
side-by-side-seq \
|
||||
starting-file \
|
||||
stdin \
|
||||
strcoll-0-names \
|
||||
filename-quoting \
|
||||
colors
|
||||
strip-trailing-cr \
|
||||
timezone \
|
||||
colors \
|
||||
y2038-vs-32bit
|
||||
|
||||
XFAIL_TESTS = large-subopt
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(TESTS) init.sh t-local.sh envvar-check
|
||||
$(TESTS) init.cfg init.sh t-local.sh envvar-check \
|
||||
large-subopt.in1 \
|
||||
large-subopt.in2
|
||||
|
||||
# Note that the first lines are statements. They ensure that environment
|
||||
# variables that can perturb tests are unset or set to expected values.
|
||||
@ -75,7 +92,6 @@ TESTS_ENVIRONMENT = \
|
||||
PREFERABLY_POSIX_SHELL='$(PREFERABLY_POSIX_SHELL)' \
|
||||
REPLACE_GETCWD=$(REPLACE_GETCWD) \
|
||||
PATH='$(abs_top_builddir)/src$(PATH_SEPARATOR)'"$$PATH" \
|
||||
stderr_fileno_=9 \
|
||||
; 9>&2
|
||||
|
||||
LOG_COMPILER= $(SHELL)
|
||||
|
||||
@ -35,4 +35,9 @@ printf 'diff\0--brief\0/proc/self/cmdline\0bin\0' > bin || framework_failure_
|
||||
diff --brief /proc/self/cmdline bin > out 2>&1 || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# Similarly for cmp -s.
|
||||
printf 'cmp\0-s\0/proc/self/cmdline\0bin\0' > bin || framework_failure_
|
||||
cmp -s /proc/self/cmdline bin > out 2>&1 || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
Exit $fail
|
||||
|
||||
31
tests/bug-64316
Executable file
31
tests/bug-64316
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# Test for signed integer overflow bug within diff.
|
||||
# Bug reported by Gisele Vanem <http://bugs.gnu.org/64316>.
|
||||
# Compile with gcc -fsanitize=undefined to test for this bug.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
for f in a b; do
|
||||
printf 'c\nd\ne\nf\ng\n%s\nh\ni\nj\nk\nl\n' $f >$f || framework_failure_
|
||||
done
|
||||
|
||||
cat >exp <<'EOF' || framework_failure_
|
||||
@@ -3,7 +3,7 @@
|
||||
e
|
||||
f
|
||||
g
|
||||
-a
|
||||
+b
|
||||
h
|
||||
i
|
||||
j
|
||||
EOF
|
||||
|
||||
returns_ 1 diff -u a b >out 2>err || fail=1
|
||||
sed '1,2d' out >out1 || framework_failure_
|
||||
compare exp out1 || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
Exit $fail
|
||||
73
tests/cmp
73
tests/cmp
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# Test 'cmp'.
|
||||
|
||||
# Copyright 2017 Free Software Foundation, Inc.
|
||||
# Copyright 2017-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -27,7 +27,7 @@ cmp a b
|
||||
a b differ: char 1, line 1
|
||||
1
|
||||
cmp a c
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp a d
|
||||
cmp: d: No such file or directory
|
||||
@ -38,16 +38,16 @@ b a differ: char 1, line 1
|
||||
cmp b b
|
||||
0
|
||||
cmp b c
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp b d
|
||||
cmp: d: No such file or directory
|
||||
2
|
||||
cmp c a
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp c b
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp c c
|
||||
0
|
||||
@ -72,7 +72,7 @@ cmp -l a b
|
||||
1 141 142
|
||||
1
|
||||
cmp -l a c
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp -l a d
|
||||
cmp: d: No such file or directory
|
||||
@ -83,16 +83,16 @@ cmp -l b a
|
||||
cmp -l b b
|
||||
0
|
||||
cmp -l b c
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp -l b d
|
||||
cmp: d: No such file or directory
|
||||
2
|
||||
cmp -l c a
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp -l c b
|
||||
cmp: EOF on c which is empty
|
||||
cmp: EOF on 'c' which is empty
|
||||
1
|
||||
cmp -l c c
|
||||
0
|
||||
@ -166,22 +166,22 @@ compare exp out || fail=1
|
||||
|
||||
cat <<'EOF' > exp1 || fail=1
|
||||
cmp a0 a1
|
||||
cmp: EOF on a0 which is empty
|
||||
cmp: EOF on 'a0' which is empty
|
||||
1
|
||||
cmp a1 a2
|
||||
cmp: EOF on a1 after byte 2, line 1
|
||||
cmp: EOF on 'a1' after byte 2, line 1
|
||||
1
|
||||
cmp a2 a3
|
||||
cmp: EOF on a2 after byte 5, in line 2
|
||||
cmp: EOF on 'a2' after byte 5, in line 2
|
||||
1
|
||||
cmp -l a0 a1
|
||||
cmp: EOF on a0 which is empty
|
||||
cmp: EOF on 'a0' which is empty
|
||||
1
|
||||
cmp -l a1 a2
|
||||
cmp: EOF on a1 after byte 2
|
||||
cmp: EOF on 'a1' after byte 2
|
||||
1
|
||||
cmp -l a2 a3
|
||||
cmp: EOF on a2 after byte 5
|
||||
cmp: EOF on 'a2' after byte 5
|
||||
1
|
||||
cmp -s a0 a1
|
||||
1
|
||||
@ -208,4 +208,47 @@ done >out1
|
||||
|
||||
compare exp1 out1 || fail=1
|
||||
|
||||
printf 'bad\n' >bad
|
||||
printf 'bug\n' >bug
|
||||
echo LC_ALL=C cmp -b bad bug
|
||||
LC_ALL=C cmp -b bad bug
|
||||
test $? -eq 1 || fail=1
|
||||
case `LC_ALL=C cmp -b bad bug` in
|
||||
'bad bug differ: byte 2, line 1 is '*' a '*' u') ;;
|
||||
*) echo 'expected cmp -b to report a and u'; fail=1;;
|
||||
esac
|
||||
|
||||
printf 'Jackdaws love my big sphinx of quartz!' > j1
|
||||
printf 'jackdaws love my big sphinx of quartz.' > j2
|
||||
cat <<'EOF' > exp2 || fail=1
|
||||
1 112 J 152 j
|
||||
38 41 ! 56 .
|
||||
EOF
|
||||
cmp -bl j1 j2 > out2
|
||||
test $? -eq 1 || fail=1
|
||||
compare exp2 out2 || fail=1
|
||||
|
||||
if (LC_ALL=en_US.iso8859-1 locale charmap) >/dev/null 2>&1; then
|
||||
printf '\200\300\377\n' >j3
|
||||
printf '\0@\177\n' >j4
|
||||
cat <<'EOF' >exp3 || fail=1
|
||||
1 200 M-^@ 0 ^@
|
||||
2 300 M-@ 100 @
|
||||
3 377 M-^? 177 ^?
|
||||
EOF
|
||||
LC_ALL=en_US.iso8859-1 cmp -bl j3 j4 >out3
|
||||
test $? -eq 1 || fail=1
|
||||
compare exp3 out3 || fail=1
|
||||
fi
|
||||
|
||||
big=99999999999999999999999999999999999999999999999999999999999
|
||||
cmp -i $big j1 j2 || fail=1
|
||||
cmp -i 1000 -n $big j1 j2 || fail=1
|
||||
|
||||
rm -f a b
|
||||
if timeout 0.1 true && truncate -s 14T a && truncate -s 15T b; then
|
||||
returns_ 1 timeout 0.4 cmp a b >/dev/null || fail=1
|
||||
fi
|
||||
rm -f a b
|
||||
|
||||
Exit $fail
|
||||
|
||||
30
tests/colors
30
tests/colors
@ -10,6 +10,10 @@ fail=0
|
||||
echo a > a
|
||||
echo b > b
|
||||
|
||||
# On systems lacking fractional timestamp support, diff -u does not print
|
||||
# a decimal point and 9-digit nanosecond suffix.
|
||||
nanosecond_zeros=$(diff -u a b|grep '\.' > /dev/null && echo .000000000)
|
||||
|
||||
epoch='1970-01-01 00:00:00'
|
||||
touch -t 197001010000.00 a b
|
||||
|
||||
@ -18,16 +22,16 @@ tab=$(printf '\t')
|
||||
|
||||
gen_exp_u()
|
||||
{
|
||||
local epoch_plus="$epoch.000000000 +0000"
|
||||
local epoch_plus="$epoch$nanosecond_zeros +0000"
|
||||
local rs=$(printf "$e[${rs}m")
|
||||
local hd=$(printf "$e[${hd}m")
|
||||
local ad=$(printf "$e[${ad}m")
|
||||
local de=$(printf "$e[${de}m")
|
||||
local ln=$(printf "$e[${ln}m")
|
||||
printf '%s' \
|
||||
"$hd--- a$tab$epoch_plus
|
||||
+++ b$tab$epoch_plus
|
||||
$rs${ln}@@ -1 +1 @@$rs
|
||||
"$hd--- a$tab$epoch_plus$rs
|
||||
$hd+++ b$tab$epoch_plus$rs
|
||||
${ln}@@ -1 +1 @@$rs
|
||||
$de-a$rs
|
||||
$ad+b$rs
|
||||
"
|
||||
@ -42,9 +46,9 @@ gen_exp_c()
|
||||
local de=$(printf "$e[${de}m")
|
||||
local ln=$(printf "$e[${ln}m")
|
||||
printf '%s' \
|
||||
"$hd*** a$tab$epoch_posix_1003_1_2001
|
||||
--- b$tab$epoch_posix_1003_1_2001
|
||||
$rs***************
|
||||
"$hd*** a$tab$epoch_posix_1003_1_2001$rs
|
||||
$hd--- b$tab$epoch_posix_1003_1_2001$rs
|
||||
***************
|
||||
$ln*** 1 ****$rs
|
||||
$de! a$rs
|
||||
$ln--- 1 ----$rs
|
||||
@ -85,7 +89,8 @@ returns_ 1 diff --color=auto a b > out || fail=1
|
||||
gen_exp_default > exp || framework_failure_
|
||||
compare exp out || fail=1
|
||||
|
||||
returns_ 1 env TERM=dumb diff ---presume-output-tty --color=auto a b > out \
|
||||
returns_ 1 env PATH="$PATH" TERM=dumb \
|
||||
diff ---presume-output-tty --color=auto a b > out \
|
||||
|| fail=1
|
||||
gen_exp_default > exp || framework_failure_
|
||||
compare exp out || fail=1
|
||||
@ -119,10 +124,13 @@ compare exp out || fail=1
|
||||
# Before the fix in http://debbugs.gnu.org/22067,
|
||||
# this test would trigger an infinite loop bug.
|
||||
mkfifo fifo
|
||||
printf '%*s-a' 1000000 > a
|
||||
printf '%*s-b' 1000000 > b
|
||||
printf '%1000000s-a' > a
|
||||
printf '%1000000s-b' > b
|
||||
head -c 10 < fifo > /dev/null &
|
||||
diff --color=always ---presume-output-tty a b > fifo
|
||||
test $? = 141 || fail=1
|
||||
|
||||
# Depending on version of GNU make (4.3.92-4.4 set SIGPIPE to "ignore"),
|
||||
# either of these is acceptable.
|
||||
case $? in 2|141) ;; *) fail=1 ;; esac
|
||||
|
||||
Exit $fail
|
||||
|
||||
20
tests/diff3
20
tests/diff3
@ -3,18 +3,6 @@
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
# Some systems lack seq.
|
||||
# A limited replacement for seq: handle 1 or 2 args; increment must be 1
|
||||
seq()
|
||||
{
|
||||
case $# in
|
||||
1) start=1 final=$1;;
|
||||
2) start=$1 final=$2;;
|
||||
*) echo you lose 1>&2; exit 1;;
|
||||
esac
|
||||
awk 'BEGIN{for(i='$start';i<='$final';i++) print i}' < /dev/null
|
||||
}
|
||||
|
||||
echo a > a || framework_failure_
|
||||
echo b > b || framework_failure_
|
||||
echo c > c || framework_failure_
|
||||
@ -30,12 +18,14 @@ EOF
|
||||
|
||||
fail=0
|
||||
|
||||
diff3 a b c > out 2> err || fail=1
|
||||
# Use --diff-program=diff since --program-transform-name and similar are
|
||||
# applied after 'make install'.
|
||||
diff3 --diff-program=diff a b c > out 2> err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
# Repeat, but with all three files the same:
|
||||
diff3 a a a > out 2> err || fail=1
|
||||
diff3 --diff-program=diff a a a > out 2> err || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
@ -101,7 +91,7 @@ cat <<'EOF' > exp40 || framework_failure_
|
||||
3z
|
||||
EOF
|
||||
|
||||
diff3 d e f > out 2> err
|
||||
diff3 --diff-program=diff d e f > out 2> err
|
||||
compare exp40 out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
|
||||
23
tests/empty-file
Executable file
23
tests/empty-file
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Test empty files
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
mkdir a b
|
||||
touch a/test
|
||||
echo 'content' > b/test
|
||||
|
||||
returns_ 1 diff -Naur a b >out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
diff -Naur a/test b/test
|
||||
--- a/test
|
||||
+++ b/test
|
||||
@@ -0,0 +1 @@
|
||||
+content
|
||||
EOF
|
||||
# Remove date and time.
|
||||
sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out > k; mv k out
|
||||
compare expected out || fail=1
|
||||
|
||||
Exit $fail
|
||||
@ -1,7 +1,7 @@
|
||||
# -*- sh -*-
|
||||
# Check environment variables for sane values while testing.
|
||||
|
||||
# Copyright (C) 2000-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2000-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
19
tests/expand-tabs
Executable file
19
tests/expand-tabs
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
# Test tab expansion.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
cat >exp <<'EOF' || framework_failure_
|
||||
0a1
|
||||
> x
|
||||
EOF
|
||||
|
||||
for p in '\b\tx\n' '\b x\n' '\b \tx\n'; do
|
||||
printf "$p" | returns_ 1 diff -t /dev/null - >out || fail=1
|
||||
done
|
||||
|
||||
compare exp out || fail=1
|
||||
|
||||
Exit $fail
|
||||
@ -2,7 +2,7 @@
|
||||
# Make sure all these programs work properly
|
||||
# when invoked with --help or --version.
|
||||
|
||||
# Copyright (C) 2000-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2000-2013, 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -170,8 +170,12 @@ egrep_setup () { args="0 $tmp_in"; }
|
||||
fgrep_setup () { args="0 $tmp_in"; }
|
||||
|
||||
diff_setup () { args="$tmp_in $tmp_in2"; }
|
||||
sdiff_setup () { args="$tmp_in $tmp_in2"; }
|
||||
diff3_setup () { args="$tmp_in $tmp_in2 $tmp_in2"; }
|
||||
|
||||
# Use --diff-program=diff since --program-transform-name and similar are
|
||||
# applied after 'make install'.
|
||||
sdiff_setup () { args="--diff-program=diff $tmp_in $tmp_in2"; }
|
||||
diff3_setup () { args="--diff-program=diff $tmp_in $tmp_in2 $tmp_in2"; }
|
||||
|
||||
cp_setup () { args="$tmp_in $tmp_in2"; }
|
||||
ln_setup () { args="$tmp_in ln-target"; }
|
||||
ginstall_setup () { args="$tmp_in $tmp_in2"; }
|
||||
|
||||
37
tests/ifdef
Executable file
37
tests/ifdef
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
# --ifdef
|
||||
|
||||
# Bug reported by Robert Webb in <http://bugs.gnu.org/61193>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
cat <<'EOF' >a
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
EOF
|
||||
|
||||
cat <<'EOF' >b
|
||||
1
|
||||
4
|
||||
5
|
||||
EOF
|
||||
|
||||
cat <<'EOF' >exp
|
||||
1
|
||||
#ifndef ZZZ
|
||||
2
|
||||
3
|
||||
#endif /* ! ZZZ */
|
||||
4
|
||||
5
|
||||
EOF
|
||||
|
||||
returns_ 1 diff -D ZZZ a b >out 2>err || fail=1
|
||||
compare exp out || fail=1
|
||||
|
||||
Exit $fail
|
||||
26
tests/ignore-case
Executable file
26
tests/ignore-case
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Test ignoring case
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
printf 'Funky Stuff\n' >a
|
||||
printf 'fUNKy stuFf\n' >b
|
||||
|
||||
diff -i a b >out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
require_utf8_locale_
|
||||
|
||||
echo 'AĀȀΆΑАӐḀἈA𐐀-Δ' >a
|
||||
echo 'aāȁάαаӑḁἀa𐐨-δ' >b
|
||||
|
||||
diff -i a b >out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
mkdir d || framework_failure_
|
||||
touch f d/F || framework_failure_
|
||||
diff --ignore-file-name-case f d || fail=1
|
||||
|
||||
Exit $fail
|
||||
28
tests/ignore-tab-expansion
Executable file
28
tests/ignore-tab-expansion
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Test ignoring tab expansion.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
for p in '\b' '\r' '\t ' '\n'; do
|
||||
printf "$p"'\b\tx\n' >a || framework_failure_
|
||||
printf "$p"'\b x\n' >b || framework_failure
|
||||
|
||||
diff -E a b >out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
done
|
||||
|
||||
require_utf8_locale_
|
||||
|
||||
tr '_@' ' \t' >a <<\EOF
|
||||
@字字字xx@x_@字_@xxx_x@@x
|
||||
EOF
|
||||
tr '_@' ' \t' >b <<\EOF
|
||||
_@字字字xx________x_@字_@xxx_x@@x
|
||||
EOF
|
||||
|
||||
diff -E a b >out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
Exit $fail
|
||||
135
tests/init.cfg
Normal file
135
tests/init.cfg
Normal file
@ -0,0 +1,135 @@
|
||||
# This file is sourced by init.sh, *before* its initialization.
|
||||
|
||||
# Copyright (C) 2010-2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Skip the current test if valgrind doesn't work,
|
||||
# which could happen if not installed,
|
||||
# or hasn't support for the built architecture,
|
||||
# or hasn't appropriate error suppressions installed etc.
|
||||
|
||||
# This goes hand in hand with the "exec 9>&2;" in tests/Makefile.am's
|
||||
# TESTS_ENVIRONMENT definition.
|
||||
stderr_fileno_=9
|
||||
|
||||
# Having an unsearchable directory in PATH causes execve to fail with EACCES
|
||||
# when applied to an unresolvable program name, contrary to the desired ENOENT.
|
||||
# Avoid the problem by rewriting PATH to exclude unsearchable directories.
|
||||
# Also, if PATH lacks /sbin and/or /usr/sbin, append it/them.
|
||||
sanitize_path_()
|
||||
{
|
||||
# FIXME: remove double quotes around $IFS when all tests use init.sh.
|
||||
# They constitute a work-around for a bug in FreeBSD 8.1's /bin/sh.
|
||||
local saved_IFS="$IFS"
|
||||
IFS=$PATH_SEPARATOR
|
||||
set -- $PATH
|
||||
IFS=$saved_IFS
|
||||
|
||||
local d d1
|
||||
local colon=
|
||||
local new_path=
|
||||
for d in "$@"; do
|
||||
test -z "$d" && d1=. || d1=$d
|
||||
if ls -d "$d1/." > /dev/null 2>&1; then
|
||||
new_path="$new_path$colon$d"
|
||||
colon=$PATH_SEPARATOR
|
||||
fi
|
||||
done
|
||||
|
||||
for d in /sbin /usr/sbin ; do
|
||||
case "$PATH_SEPARATOR$new_path$PATH_SEPARATOR" in
|
||||
*$PATH_SEPARATOR$d$PATH_SEPARATOR*) ;;
|
||||
*) new_path="$new_path$PATH_SEPARATOR$d" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
PATH=$new_path
|
||||
export PATH
|
||||
}
|
||||
|
||||
require_timeout_()
|
||||
{
|
||||
( timeout 10s true ) > /dev/null 2>&1 \
|
||||
|| skip_ your system lacks the timeout program
|
||||
returns_ 1 timeout 10s false \
|
||||
|| skip_ your system has a non-GNU timeout program
|
||||
returns_ 124 timeout 0.01 sleep 0.02 \
|
||||
|| skip_ "'timeout 0.01 sleep 0.02' did not time out"
|
||||
}
|
||||
|
||||
require_valgrind_()
|
||||
{
|
||||
require_timeout_
|
||||
local errout; errout=$(
|
||||
LC_ALL=C timeout --signal=9 3 valgrind --error-exitcode=1 diff /dev/null /dev/null 2>&1
|
||||
) ||
|
||||
skip_ "requires a working valgrind"
|
||||
case $errout in
|
||||
*'Serious error'*)
|
||||
skip_ "requires a valgrind without serious errors";;
|
||||
esac
|
||||
}
|
||||
|
||||
# Skip the current test if we lack Perl.
|
||||
require_perl_()
|
||||
{
|
||||
: ${PERL=perl}
|
||||
$PERL -e 'use warnings' > /dev/null 2>&1 \
|
||||
|| skip_ 'configure did not find a usable version of Perl'
|
||||
}
|
||||
|
||||
# Run this test in a UTF-8 locale if possible, and skip the test otherwise.
|
||||
# Prefer en_US for its diagnostics.
|
||||
require_utf8_locale_()
|
||||
{
|
||||
local locale
|
||||
|
||||
if test "`(locale charmap) 2>/dev/null`" != UTF-8; then
|
||||
for locale in en_US.UTF-8 `(locale -a) 2>/dev/null` not-found; do
|
||||
case $locale in
|
||||
*.[Uu][Tt][Ff]*8)
|
||||
if test "`(LC_ALL=$locale locale charmap) 2>/dev/null`" = UTF-8; then
|
||||
LC_ALL=$locale
|
||||
export LC_ALL
|
||||
break
|
||||
fi;;
|
||||
not-found)
|
||||
skip_ "No UTF-8 locale found";;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
# Solaris 10's /usr/bin/tr can silently malfunction. Try to find one that works.
|
||||
found_working_tr=0
|
||||
for i in tr /usr/xpg4/bin/tr gtr; do
|
||||
tr() { env $i "$@"; }
|
||||
test '一' = "$(printf 一|tr a b)" && { found_working_tr=1; break; }
|
||||
done
|
||||
test $found_working_tr = 1 || skip_ "failed to find a working tr program"
|
||||
}
|
||||
|
||||
# Some systems lack seq.
|
||||
# A limited replacement for seq: handle 1 or 2 args; increment must be 1
|
||||
seq()
|
||||
{
|
||||
case $# in
|
||||
1) start=1 final=$1;;
|
||||
2) start=$1 final=$2;;
|
||||
*) echo you lose 1>&2; exit 1;;
|
||||
esac
|
||||
awk 'BEGIN{for(i='$start';i<='$final';i++) print i}' < /dev/null
|
||||
}
|
||||
|
||||
sanitize_path_
|
||||
605
tests/init.sh
605
tests/init.sh
@ -1,605 +0,0 @@
|
||||
# source this file; set up for tests
|
||||
|
||||
# Copyright (C) 2009-2017 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Using this file in a test
|
||||
# =========================
|
||||
#
|
||||
# The typical skeleton of a test looks like this:
|
||||
#
|
||||
# #!/bin/sh
|
||||
# . "${srcdir=.}/init.sh"; path_prepend_ .
|
||||
# Execute some commands.
|
||||
# Note that these commands are executed in a subdirectory, therefore you
|
||||
# need to prepend "../" to relative filenames in the build directory.
|
||||
# Note that the "path_prepend_ ." is useful only if the body of your
|
||||
# test invokes programs residing in the initial directory.
|
||||
# For example, if the programs you want to test are in src/, and this test
|
||||
# script is named tests/test-1, then you would use "path_prepend_ ../src",
|
||||
# or perhaps export PATH='$(abs_top_builddir)/src$(PATH_SEPARATOR)'"$$PATH"
|
||||
# to all tests via automake's TESTS_ENVIRONMENT.
|
||||
# Set the exit code 0 for success, 77 for skipped, or 1 or other for failure.
|
||||
# Use the skip_ and fail_ functions to print a diagnostic and then exit
|
||||
# with the corresponding exit code.
|
||||
# Exit $?
|
||||
|
||||
# Executing a test that uses this file
|
||||
# ====================================
|
||||
#
|
||||
# Running a single test:
|
||||
# $ make check TESTS=test-foo.sh
|
||||
#
|
||||
# Running a single test, with verbose output:
|
||||
# $ make check TESTS=test-foo.sh VERBOSE=yes
|
||||
#
|
||||
# Running a single test, keeping the temporary directory:
|
||||
# $ make check TESTS=test-foo.sh KEEP=yes
|
||||
#
|
||||
# Running a single test, with single-stepping:
|
||||
# 1. Go into a sub-shell:
|
||||
# $ bash
|
||||
# 2. Set relevant environment variables from TESTS_ENVIRONMENT in the
|
||||
# Makefile:
|
||||
# $ export srcdir=../../tests # this is an example
|
||||
# 3. Execute the commands from the test, copy&pasting them one by one:
|
||||
# $ . "$srcdir/init.sh"; path_prepend_ .
|
||||
# ...
|
||||
# 4. Finally
|
||||
# $ exit
|
||||
|
||||
ME_=`expr "./$0" : '.*/\(.*\)$'`
|
||||
|
||||
# We use a trap below for cleanup. This requires us to go through
|
||||
# hoops to get the right exit status transported through the handler.
|
||||
# So use 'Exit STATUS' instead of 'exit STATUS' inside of the tests.
|
||||
# Turn off errexit here so that we don't trip the bug with OSF1/Tru64
|
||||
# sh inside this function.
|
||||
Exit () { set +e; (exit $1); exit $1; }
|
||||
|
||||
# Print warnings (e.g., about skipped and failed tests) to this file number.
|
||||
# Override by defining to say, 9, in init.cfg, and putting say,
|
||||
# export ...ENVVAR_SETTINGS...; $(SHELL) 9>&2
|
||||
# in the definition of TESTS_ENVIRONMENT in your tests/Makefile.am file.
|
||||
# This is useful when using automake's parallel tests mode, to print
|
||||
# the reason for skip/failure to console, rather than to the .log files.
|
||||
: ${stderr_fileno_=2}
|
||||
|
||||
# Note that correct expansion of "$*" depends on IFS starting with ' '.
|
||||
# Always write the full diagnostic to stderr.
|
||||
# When stderr_fileno_ is not 2, also emit the first line of the
|
||||
# diagnostic to that file descriptor.
|
||||
warn_ ()
|
||||
{
|
||||
# If IFS does not start with ' ', set it and emit the warning in a subshell.
|
||||
case $IFS in
|
||||
' '*) printf '%s\n' "$*" >&2
|
||||
test $stderr_fileno_ = 2 \
|
||||
|| { printf '%s\n' "$*" | sed 1q >&$stderr_fileno_ ; } ;;
|
||||
*) (IFS=' '; warn_ "$@");;
|
||||
esac
|
||||
}
|
||||
fail_ () { warn_ "$ME_: failed test: $@"; Exit 1; }
|
||||
skip_ () { warn_ "$ME_: skipped test: $@"; Exit 77; }
|
||||
fatal_ () { warn_ "$ME_: hard error: $@"; Exit 99; }
|
||||
framework_failure_ () { warn_ "$ME_: set-up failure: $@"; Exit 99; }
|
||||
|
||||
# This is used to simplify checking of the return value
|
||||
# which is useful when ensuring a command fails as desired.
|
||||
# I.e., just doing `command ... &&fail=1` will not catch
|
||||
# a segfault in command for example. With this helper you
|
||||
# instead check an explicit exit code like
|
||||
# returns_ 1 command ... || fail
|
||||
returns_ () {
|
||||
# Disable tracing so it doesn't interfere with stderr of the wrapped command
|
||||
{ set +x; } 2>/dev/null
|
||||
|
||||
local exp_exit="$1"
|
||||
shift
|
||||
"$@"
|
||||
test $? -eq $exp_exit && ret_=0 || ret_=1
|
||||
|
||||
if test "$VERBOSE" = yes && test "$gl_set_x_corrupts_stderr_" = false; then
|
||||
set -x
|
||||
fi
|
||||
{ return $ret_; } 2>/dev/null
|
||||
}
|
||||
|
||||
# Sanitize this shell to POSIX mode, if possible.
|
||||
DUALCASE=1; export DUALCASE
|
||||
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
|
||||
emulate sh
|
||||
NULLCMD=:
|
||||
alias -g '${1+"$@"}'='"$@"'
|
||||
setopt NO_GLOB_SUBST
|
||||
else
|
||||
case `(set -o) 2>/dev/null` in
|
||||
*posix*) set -o posix ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# We require $(...) support unconditionally.
|
||||
# We require non-surprising "local" semantics (this eliminates dash).
|
||||
# This takes the admittedly draconian step of eliminating dash, because the
|
||||
# assignment tab=$(printf '\t') works fine, yet preceding it with "local "
|
||||
# transforms it into an assignment that sets the variable to the empty string.
|
||||
# That is too counter-intuitive, and can lead to subtle run-time malfunction.
|
||||
# The example below is less subtle in that with dash, it evokes the run-time
|
||||
# exception "dash: 1: local: 1: bad variable name".
|
||||
# We require a few additional shell features only when $EXEEXT is nonempty,
|
||||
# in order to support automatic $EXEEXT emulation:
|
||||
# - hyphen-containing alias names
|
||||
# - we prefer to use ${var#...} substitution, rather than having
|
||||
# to work around lack of support for that feature.
|
||||
# The following code attempts to find a shell with support for these features.
|
||||
# If the current shell passes the test, we're done. Otherwise, test other
|
||||
# shells until we find one that passes. If one is found, re-exec it.
|
||||
# If no acceptable shell is found, skip the current test.
|
||||
#
|
||||
# The "...set -x; P=1 true 2>err..." test is to disqualify any shell that
|
||||
# emits "P=1" into err, as /bin/sh from SunOS 5.11 and OpenBSD 4.7 do.
|
||||
#
|
||||
# Use "9" to indicate success (rather than 0), in case some shell acts
|
||||
# like Solaris 10's /bin/sh but exits successfully instead of with status 2.
|
||||
|
||||
# Eval this code in a subshell to determine a shell's suitability.
|
||||
# 10 - passes all tests; ok to use
|
||||
# 9 - ok, but enabling "set -x" corrupts app stderr; prefer higher score
|
||||
# ? - not ok
|
||||
gl_shell_test_script_='
|
||||
test $(echo y) = y || exit 1
|
||||
f_local_() { local v=1; }; f_local_ || exit 1
|
||||
f_dash_local_fail_() { local t=$(printf " 1"); }; f_dash_local_fail_
|
||||
score_=10
|
||||
if test "$VERBOSE" = yes; then
|
||||
test -n "$( (exec 3>&1; set -x; P=1 true 2>&3) 2> /dev/null)" && score_=9
|
||||
fi
|
||||
test -z "$EXEEXT" && exit $score_
|
||||
shopt -s expand_aliases
|
||||
alias a-b="echo zoo"
|
||||
v=abx
|
||||
test ${v%x} = ab \
|
||||
&& test ${v#a} = bx \
|
||||
&& test $(a-b) = zoo \
|
||||
&& exit $score_
|
||||
'
|
||||
|
||||
if test "x$1" = "x--no-reexec"; then
|
||||
shift
|
||||
else
|
||||
# Assume a working shell. Export to subshells (setup_ needs this).
|
||||
gl_set_x_corrupts_stderr_=false
|
||||
export gl_set_x_corrupts_stderr_
|
||||
|
||||
# Record the first marginally acceptable shell.
|
||||
marginal_=
|
||||
|
||||
# Search for a shell that meets our requirements.
|
||||
for re_shell_ in __current__ "${CONFIG_SHELL:-no_shell}" \
|
||||
/bin/sh bash dash zsh pdksh fail
|
||||
do
|
||||
test "$re_shell_" = no_shell && continue
|
||||
|
||||
# If we've made it all the way to the sentinel, "fail" without
|
||||
# finding even a marginal shell, skip this test.
|
||||
if test "$re_shell_" = fail; then
|
||||
test -z "$marginal_" && skip_ failed to find an adequate shell
|
||||
re_shell_=$marginal_
|
||||
break
|
||||
fi
|
||||
|
||||
# When testing the current shell, simply "eval" the test code.
|
||||
# Otherwise, run it via $re_shell_ -c ...
|
||||
if test "$re_shell_" = __current__; then
|
||||
# 'eval'ing this code makes Solaris 10's /bin/sh exit with
|
||||
# $? set to 2. It does not evaluate any of the code after the
|
||||
# "unexpected" first '('. Thus, we must run it in a subshell.
|
||||
( eval "$gl_shell_test_script_" ) > /dev/null 2>&1
|
||||
else
|
||||
"$re_shell_" -c "$gl_shell_test_script_" 2>/dev/null
|
||||
fi
|
||||
|
||||
st_=$?
|
||||
|
||||
# $re_shell_ works just fine. Use it.
|
||||
if test $st_ = 10; then
|
||||
gl_set_x_corrupts_stderr_=false
|
||||
break
|
||||
fi
|
||||
|
||||
# If this is our first marginally acceptable shell, remember it.
|
||||
if test "$st_:$marginal_" = 9: ; then
|
||||
marginal_="$re_shell_"
|
||||
gl_set_x_corrupts_stderr_=true
|
||||
fi
|
||||
done
|
||||
|
||||
if test "$re_shell_" != __current__; then
|
||||
# Found a usable shell. Preserve -v and -x.
|
||||
case $- in
|
||||
*v*x* | *x*v*) opts_=-vx ;;
|
||||
*v*) opts_=-v ;;
|
||||
*x*) opts_=-x ;;
|
||||
*) opts_= ;;
|
||||
esac
|
||||
re_shell=$re_shell_
|
||||
export re_shell
|
||||
exec "$re_shell_" $opts_ "$0" --no-reexec "$@"
|
||||
echo "$ME_: exec failed" 1>&2
|
||||
exit 127
|
||||
fi
|
||||
fi
|
||||
|
||||
# If this is bash, turn off all aliases.
|
||||
test -n "$BASH_VERSION" && unalias -a
|
||||
|
||||
# Note that when supporting $EXEEXT (transparently mapping from PROG_NAME to
|
||||
# PROG_NAME.exe), we want to support hyphen-containing names like test-acos.
|
||||
# That is part of the shell-selection test above. Why use aliases rather
|
||||
# than functions? Because support for hyphen-containing aliases is more
|
||||
# widespread than that for hyphen-containing function names.
|
||||
test -n "$EXEEXT" && shopt -s expand_aliases
|
||||
|
||||
# Enable glibc's malloc-perturbing option.
|
||||
# This is useful for exposing code that depends on the fact that
|
||||
# malloc-related functions often return memory that is mostly zeroed.
|
||||
# If you have the time and cycles, use valgrind to do an even better job.
|
||||
: ${MALLOC_PERTURB_=87}
|
||||
export MALLOC_PERTURB_
|
||||
|
||||
# This is a stub function that is run upon trap (upon regular exit and
|
||||
# interrupt). Override it with a per-test function, e.g., to unmount
|
||||
# a partition, or to undo any other global state changes.
|
||||
cleanup_ () { :; }
|
||||
|
||||
# Emit a header similar to that from diff -u; Print the simulated "diff"
|
||||
# command so that the order of arguments is clear. Don't bother with @@ lines.
|
||||
emit_diff_u_header_ ()
|
||||
{
|
||||
printf '%s\n' "diff -u $*" \
|
||||
"--- $1 1970-01-01" \
|
||||
"+++ $2 1970-01-01"
|
||||
}
|
||||
|
||||
# Arrange not to let diff or cmp operate on /dev/null,
|
||||
# since on some systems (at least OSF/1 5.1), that doesn't work.
|
||||
# When there are not two arguments, or no argument is /dev/null, return 2.
|
||||
# When one argument is /dev/null and the other is not empty,
|
||||
# cat the nonempty file to stderr and return 1.
|
||||
# Otherwise, return 0.
|
||||
compare_dev_null_ ()
|
||||
{
|
||||
test $# = 2 || return 2
|
||||
|
||||
if test "x$1" = x/dev/null; then
|
||||
test -s "$2" || return 0
|
||||
emit_diff_u_header_ "$@"; sed 's/^/+/' "$2"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if test "x$2" = x/dev/null; then
|
||||
test -s "$1" || return 0
|
||||
emit_diff_u_header_ "$@"; sed 's/^/-/' "$1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 2
|
||||
}
|
||||
|
||||
for diff_opt_ in -u -U3 -c '' no; do
|
||||
test "$diff_opt_" != no &&
|
||||
diff_out_=`exec 2>/dev/null; diff $diff_opt_ "$0" "$0" < /dev/null` &&
|
||||
break
|
||||
done
|
||||
if test "$diff_opt_" != no; then
|
||||
if test -z "$diff_out_"; then
|
||||
compare_ () { diff $diff_opt_ "$@"; }
|
||||
else
|
||||
compare_ ()
|
||||
{
|
||||
# If no differences were found, AIX and HP-UX 'diff' produce output
|
||||
# like "No differences encountered". Hide this output.
|
||||
diff $diff_opt_ "$@" > diff.out
|
||||
diff_status_=$?
|
||||
test $diff_status_ -eq 0 || cat diff.out || diff_status_=2
|
||||
rm -f diff.out || diff_status_=2
|
||||
return $diff_status_
|
||||
}
|
||||
fi
|
||||
elif cmp -s /dev/null /dev/null 2>/dev/null; then
|
||||
compare_ () { cmp -s "$@"; }
|
||||
else
|
||||
compare_ () { cmp "$@"; }
|
||||
fi
|
||||
|
||||
# Usage: compare EXPECTED ACTUAL
|
||||
#
|
||||
# Given compare_dev_null_'s preprocessing, defer to compare_ if 2 or more.
|
||||
# Otherwise, propagate $? to caller: any diffs have already been printed.
|
||||
compare ()
|
||||
{
|
||||
# This looks like it can be factored to use a simple "case $?"
|
||||
# after unchecked compare_dev_null_ invocation, but that would
|
||||
# fail in a "set -e" environment.
|
||||
if compare_dev_null_ "$@"; then
|
||||
return 0
|
||||
else
|
||||
case $? in
|
||||
1) return 1;;
|
||||
*) compare_ "$@";;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# An arbitrary prefix to help distinguish test directories.
|
||||
testdir_prefix_ () { printf gt; }
|
||||
|
||||
# Run the user-overridable cleanup_ function, remove the temporary
|
||||
# directory and exit with the incoming value of $?.
|
||||
remove_tmp_ ()
|
||||
{
|
||||
__st=$?
|
||||
cleanup_
|
||||
if test "$KEEP" = yes; then
|
||||
echo "Not removing temporary directory $test_dir_"
|
||||
else
|
||||
# cd out of the directory we're about to remove
|
||||
cd "$initial_cwd_" || cd / || cd /tmp
|
||||
chmod -R u+rwx "$test_dir_"
|
||||
# If removal fails and exit status was to be 0, then change it to 1.
|
||||
rm -rf "$test_dir_" || { test $__st = 0 && __st=1; }
|
||||
fi
|
||||
exit $__st
|
||||
}
|
||||
|
||||
# Given a directory name, DIR, if every entry in it that matches *.exe
|
||||
# contains only the specified bytes (see the case stmt below), then print
|
||||
# a space-separated list of those names and return 0. Otherwise, don't
|
||||
# print anything and return 1. Naming constraints apply also to DIR.
|
||||
find_exe_basenames_ ()
|
||||
{
|
||||
feb_dir_=$1
|
||||
feb_fail_=0
|
||||
feb_result_=
|
||||
feb_sp_=
|
||||
for feb_file_ in $feb_dir_/*.exe; do
|
||||
# If there was no *.exe file, or there existed a file named "*.exe" that
|
||||
# was deleted between the above glob expansion and the existence test
|
||||
# below, just skip it.
|
||||
test "x$feb_file_" = "x$feb_dir_/*.exe" && test ! -f "$feb_file_" \
|
||||
&& continue
|
||||
# Exempt [.exe, since we can't create a function by that name, yet
|
||||
# we can't invoke [ by PATH search anyways due to shell builtins.
|
||||
test "x$feb_file_" = "x$feb_dir_/[.exe" && continue
|
||||
case $feb_file_ in
|
||||
*[!-a-zA-Z/0-9_.+]*) feb_fail_=1; break;;
|
||||
*) # Remove leading file name components as well as the .exe suffix.
|
||||
feb_file_=${feb_file_##*/}
|
||||
feb_file_=${feb_file_%.exe}
|
||||
feb_result_="$feb_result_$feb_sp_$feb_file_";;
|
||||
esac
|
||||
feb_sp_=' '
|
||||
done
|
||||
test $feb_fail_ = 0 && printf %s "$feb_result_"
|
||||
return $feb_fail_
|
||||
}
|
||||
|
||||
# Consider the files in directory, $1.
|
||||
# For each file name of the form PROG.exe, create an alias named
|
||||
# PROG that simply invokes PROG.exe, then return 0. If any selected
|
||||
# file name or the directory name, $1, contains an unexpected character,
|
||||
# define no alias and return 1.
|
||||
create_exe_shims_ ()
|
||||
{
|
||||
case $EXEEXT in
|
||||
'') return 0 ;;
|
||||
.exe) ;;
|
||||
*) echo "$0: unexpected \$EXEEXT value: $EXEEXT" 1>&2; return 1 ;;
|
||||
esac
|
||||
|
||||
base_names_=`find_exe_basenames_ $1` \
|
||||
|| { echo "$0 (exe_shim): skipping directory: $1" 1>&2; return 0; }
|
||||
|
||||
if test -n "$base_names_"; then
|
||||
for base_ in $base_names_; do
|
||||
alias "$base_"="$base_$EXEEXT"
|
||||
done
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Use this function to prepend to PATH an absolute name for each
|
||||
# specified, possibly-$initial_cwd_-relative, directory.
|
||||
path_prepend_ ()
|
||||
{
|
||||
while test $# != 0; do
|
||||
path_dir_=$1
|
||||
case $path_dir_ in
|
||||
'') fail_ "invalid path dir: '$1'";;
|
||||
/*) abs_path_dir_=$path_dir_;;
|
||||
*) abs_path_dir_=$initial_cwd_/$path_dir_;;
|
||||
esac
|
||||
case $abs_path_dir_ in
|
||||
*:*) fail_ "invalid path dir: '$abs_path_dir_'";;
|
||||
esac
|
||||
PATH="$abs_path_dir_:$PATH"
|
||||
|
||||
# Create an alias, FOO, for each FOO.exe in this directory.
|
||||
create_exe_shims_ "$abs_path_dir_" \
|
||||
|| fail_ "something failed (above): $abs_path_dir_"
|
||||
shift
|
||||
done
|
||||
export PATH
|
||||
}
|
||||
|
||||
setup_ ()
|
||||
{
|
||||
if test "$VERBOSE" = yes; then
|
||||
# Test whether set -x may cause the selected shell to corrupt an
|
||||
# application's stderr. Many do, including zsh-4.3.10 and the /bin/sh
|
||||
# from SunOS 5.11, OpenBSD 4.7 and Irix 5.x and 6.5.
|
||||
# If enabling verbose output this way would cause trouble, simply
|
||||
# issue a warning and refrain.
|
||||
if $gl_set_x_corrupts_stderr_; then
|
||||
warn_ "using SHELL=$SHELL with 'set -x' corrupts stderr"
|
||||
else
|
||||
set -x
|
||||
fi
|
||||
fi
|
||||
|
||||
initial_cwd_=$PWD
|
||||
|
||||
pfx_=`testdir_prefix_`
|
||||
test_dir_=`mktempd_ "$initial_cwd_" "$pfx_-$ME_.XXXX"` \
|
||||
|| fail_ "failed to create temporary directory in $initial_cwd_"
|
||||
cd "$test_dir_" || fail_ "failed to cd to temporary directory"
|
||||
|
||||
# As autoconf-generated configure scripts do, ensure that IFS
|
||||
# is defined initially, so that saving and restoring $IFS works.
|
||||
gl_init_sh_nl_='
|
||||
'
|
||||
IFS=" "" $gl_init_sh_nl_"
|
||||
|
||||
# This trap statement, along with a trap on 0 below, ensure that the
|
||||
# temporary directory, $test_dir_, is removed upon exit as well as
|
||||
# upon receipt of any of the listed signals.
|
||||
for sig_ in 1 2 3 13 15; do
|
||||
eval "trap 'Exit $(expr $sig_ + 128)' $sig_"
|
||||
done
|
||||
}
|
||||
|
||||
# Create a temporary directory, much like mktemp -d does.
|
||||
# Written by Jim Meyering.
|
||||
#
|
||||
# Usage: mktempd_ /tmp phoey.XXXXXXXXXX
|
||||
#
|
||||
# First, try to use the mktemp program.
|
||||
# Failing that, we'll roll our own mktemp-like function:
|
||||
# - try to get random bytes from /dev/urandom
|
||||
# - failing that, generate output from a combination of quickly-varying
|
||||
# sources and gzip. Ignore non-varying gzip header, and extract
|
||||
# "random" bits from there.
|
||||
# - given those bits, map to file-name bytes using tr, and try to create
|
||||
# the desired directory.
|
||||
# - make only $MAX_TRIES_ attempts
|
||||
|
||||
# Helper function. Print $N pseudo-random bytes from a-zA-Z0-9.
|
||||
rand_bytes_ ()
|
||||
{
|
||||
n_=$1
|
||||
|
||||
# Maybe try openssl rand -base64 $n_prime_|tr '+/=\012' abcd first?
|
||||
# But if they have openssl, they probably have mktemp, too.
|
||||
|
||||
chars_=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
||||
dev_rand_=/dev/urandom
|
||||
if test -r "$dev_rand_"; then
|
||||
# Note: 256-length($chars_) == 194; 3 copies of $chars_ is 186 + 8 = 194.
|
||||
dd ibs=$n_ count=1 if=$dev_rand_ 2>/dev/null \
|
||||
| LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
|
||||
return
|
||||
fi
|
||||
|
||||
n_plus_50_=`expr $n_ + 50`
|
||||
cmds_='date; date +%N; free; who -a; w; ps auxww; ps ef; netstat -n'
|
||||
data_=` (eval "$cmds_") 2>&1 | gzip `
|
||||
|
||||
# Ensure that $data_ has length at least 50+$n_
|
||||
while :; do
|
||||
len_=`echo "$data_"|wc -c`
|
||||
test $n_plus_50_ -le $len_ && break;
|
||||
data_=` (echo "$data_"; eval "$cmds_") 2>&1 | gzip `
|
||||
done
|
||||
|
||||
echo "$data_" \
|
||||
| dd bs=1 skip=50 count=$n_ 2>/dev/null \
|
||||
| LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
|
||||
}
|
||||
|
||||
mktempd_ ()
|
||||
{
|
||||
case $# in
|
||||
2);;
|
||||
*) fail_ "Usage: mktempd_ DIR TEMPLATE";;
|
||||
esac
|
||||
|
||||
destdir_=$1
|
||||
template_=$2
|
||||
|
||||
MAX_TRIES_=4
|
||||
|
||||
# Disallow any trailing slash on specified destdir:
|
||||
# it would subvert the post-mktemp "case"-based destdir test.
|
||||
case $destdir_ in
|
||||
/ | //) destdir_slash_=$destdir;;
|
||||
*/) fail_ "invalid destination dir: remove trailing slash(es)";;
|
||||
*) destdir_slash_=$destdir_/;;
|
||||
esac
|
||||
|
||||
case $template_ in
|
||||
*XXXX) ;;
|
||||
*) fail_ \
|
||||
"invalid template: $template_ (must have a suffix of at least 4 X's)";;
|
||||
esac
|
||||
|
||||
# First, try to use mktemp.
|
||||
d=`unset TMPDIR; { mktemp -d -t -p "$destdir_" "$template_"; } 2>/dev/null` &&
|
||||
|
||||
# The resulting name must be in the specified directory.
|
||||
case $d in "$destdir_slash_"*) :;; *) false;; esac &&
|
||||
|
||||
# It must have created the directory.
|
||||
test -d "$d" &&
|
||||
|
||||
# It must have 0700 permissions. Handle sticky "S" bits.
|
||||
perms=`ls -dgo "$d" 2>/dev/null` &&
|
||||
case $perms in drwx--[-S]---*) :;; *) false;; esac && {
|
||||
echo "$d"
|
||||
return
|
||||
}
|
||||
|
||||
# If we reach this point, we'll have to create a directory manually.
|
||||
|
||||
# Get a copy of the template without its suffix of X's.
|
||||
base_template_=`echo "$template_"|sed 's/XX*$//'`
|
||||
|
||||
# Calculate how many X's we've just removed.
|
||||
template_length_=`echo "$template_" | wc -c`
|
||||
nx_=`echo "$base_template_" | wc -c`
|
||||
nx_=`expr $template_length_ - $nx_`
|
||||
|
||||
err_=
|
||||
i_=1
|
||||
while :; do
|
||||
X_=`rand_bytes_ $nx_`
|
||||
candidate_dir_="$destdir_slash_$base_template_$X_"
|
||||
err_=`mkdir -m 0700 "$candidate_dir_" 2>&1` \
|
||||
&& { echo "$candidate_dir_"; return; }
|
||||
test $MAX_TRIES_ -le $i_ && break;
|
||||
i_=`expr $i_ + 1`
|
||||
done
|
||||
fail_ "$err_"
|
||||
}
|
||||
|
||||
# If you want to override the testdir_prefix_ function,
|
||||
# or to add more utility functions, use this file.
|
||||
test -f "$srcdir/init.cfg" \
|
||||
&& . "$srcdir/init.cfg"
|
||||
|
||||
setup_ "$@"
|
||||
# This trap is here, rather than in the setup_ function, because some
|
||||
# shells run the exit trap at shell function exit, rather than script exit.
|
||||
trap remove_tmp_ 0
|
||||
@ -7,7 +7,7 @@ fail=0
|
||||
|
||||
echo > a || framework_failure_
|
||||
echo b > b || framework_failure_
|
||||
echo 'diff: \: Trailing backslash' > exp-err || framework_failure_
|
||||
echo "diff: '\': Trailing backslash" > exp-err || framework_failure_
|
||||
|
||||
# This must fail with an exit status of 2:
|
||||
returns_ 2 diff -Ix -I\\ a b > out 2> err || fail=1
|
||||
|
||||
27
tests/large-subopt
Normal file
27
tests/large-subopt
Normal file
@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# This test is expected to fail at least with diffutils-3.6.
|
||||
# Demonstrate how diff can produce suboptimal output.
|
||||
# With these two files, diff -u prints output including this:
|
||||
# -2
|
||||
# +L: 361
|
||||
# +L: 361
|
||||
# +2
|
||||
# The trouble is that "2" line that is both added and removed.
|
||||
# This smaller patch could induce the same change:
|
||||
# +L: 361
|
||||
# +L: 361
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
require_perl_
|
||||
|
||||
fail=0
|
||||
|
||||
diff -u \
|
||||
"$abs_top_srcdir/tests/large-subopt.in1" \
|
||||
"$abs_top_srcdir/tests/large-subopt.in2" \
|
||||
| $PERL -n0 -e \
|
||||
'/\n-2\n(\+L: 361\n){2}\+2\n/ and do {$e=1; last}; END{exit !$e}' \
|
||||
&& fail=1
|
||||
|
||||
Exit $fail
|
||||
6706
tests/large-subopt.in1
Normal file
6706
tests/large-subopt.in1
Normal file
File diff suppressed because it is too large
Load Diff
5777
tests/large-subopt.in2
Normal file
5777
tests/large-subopt.in2
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,10 +10,7 @@ echo a > a || fail=1
|
||||
echo '0a1
|
||||
> a' > exp || fail=1
|
||||
|
||||
returns_ 1 diff -N - a <&- > out || fail=1
|
||||
compare exp out || fail=1
|
||||
|
||||
returns_ 1 diff --unidirectional-new-file - a <&- > out || fail=1
|
||||
returns_ 1 diff -N b a > out || fail=1
|
||||
compare exp out || fail=1
|
||||
|
||||
returns_ 1 diff -N b - < a > out || fail=1
|
||||
@ -25,14 +22,6 @@ compare exp out || fail=1
|
||||
echo '1d0
|
||||
< a' > exp || fail=1
|
||||
|
||||
returns_ 1 diff -N a - <&- > out || fail=1
|
||||
compare exp out || fail=1
|
||||
|
||||
# With closed standard input, require an exit status of 2
|
||||
# and empty stdout.
|
||||
returns_ 2 diff --unidirectional-new-file a - <&- > out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
returns_ 1 diff -N - b < a > out || fail=1
|
||||
compare exp out || fail=1
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ ln -s regular3 symlink3
|
||||
|
||||
# Non-recursive comparisons.
|
||||
|
||||
# Test case 3: Compare regular file with regular file.
|
||||
# Compare regular file with regular file.
|
||||
returns_ 1 diff --no-dereference regular1 regular2 > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
1c1
|
||||
@ -23,43 +23,47 @@ cat <<EOF > expected || framework_failure_
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 4: Compare regular file with symbolic link.
|
||||
# Compare regular file with symbolic link.
|
||||
returns_ 1 diff --no-dereference regular1 symlink1 > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
File regular1 is a regular file while file symlink1 is a symbolic link
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 5: Compare symbolic link with regular file.
|
||||
# Compare symbolic link with regular file.
|
||||
returns_ 1 diff --no-dereference symlink1 regular1 > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
File symlink1 is a symbolic link while file regular1 is a regular file
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 6: Compare symbolic links with same value.
|
||||
# Compare symbolic links with same value.
|
||||
diff --no-dereference symlink1 symlink1bis > out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# Test case 7: Compare symbolic links with different value and different target
|
||||
# contents.
|
||||
returns_ 1 diff --no-dereference symlink1 symlink2 > out || fail=1
|
||||
# Compare symbolic links with different value and different target contents.
|
||||
LC_ALL=C returns_ 1 diff --no-dereference symlink1 symlink2 > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
Symbolic links symlink1 and symlink2 differ
|
||||
Symbolic links 'symlink1' -> 'regular1' and 'symlink2' -> 'regular2' differ
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 8: Compare symbolic links with different value and same target
|
||||
# contents.
|
||||
returns_ 1 diff --no-dereference symlink2 symlink3 > out || fail=1
|
||||
# Compare symbolic links with different value and same target contents.
|
||||
LC_ALL=C returns_ 1 diff --no-dereference symlink2 symlink3 > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
Symbolic links symlink2 and symlink3 differ
|
||||
Symbolic links 'symlink2' -> 'regular2' and 'symlink3' -> 'regular3' differ
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
mkdir subdir &&
|
||||
ln -s loop loop &&
|
||||
ln -s loop subdir/loop || framework_failure_
|
||||
diff --no-dereference loop subdir > out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# Recursive comparisons.
|
||||
|
||||
# Test case 1: Compare symbolic link with nonexistent file.
|
||||
# Compare symbolic link with nonexistent file.
|
||||
mkdir subdir1a
|
||||
mkdir subdir1b
|
||||
ln -s nonexistent subdir1a/foo
|
||||
@ -71,7 +75,7 @@ Only in subdir1a: foo
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 1: Compare nonexistent file with symbolic link.
|
||||
# Compare nonexistent file with symbolic link.
|
||||
mkdir subdir2a
|
||||
mkdir subdir2b
|
||||
ln -s nonexistent subdir2b/foo
|
||||
@ -83,7 +87,7 @@ Only in subdir2b: foo
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 3: Compare regular file with regular file.
|
||||
# Compare regular file with regular file.
|
||||
mkdir subdir3a
|
||||
mkdir subdir3b
|
||||
cp regular1 subdir3a/foo
|
||||
@ -98,7 +102,7 @@ diff -r --no-dereference subdir3a/foo subdir3b/foo
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 4: Compare regular file with symbolic link.
|
||||
# Compare regular file with symbolic link.
|
||||
mkdir subdir4a
|
||||
mkdir subdir4b
|
||||
cp regular1 subdir4a/foo
|
||||
@ -109,7 +113,7 @@ File subdir4a/foo is a regular file while file subdir4b/foo is a symbolic link
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 5: Compare symbolic link with regular file.
|
||||
# Compare symbolic link with regular file.
|
||||
mkdir subdir5a
|
||||
mkdir subdir5b
|
||||
ln -s ../regular1 subdir5a/foo
|
||||
@ -120,7 +124,7 @@ File subdir5a/foo is a symbolic link while file subdir5b/foo is a regular file
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 6: Compare symbolic links with same value.
|
||||
# Compare symbolic links with same value.
|
||||
mkdir subdir6a
|
||||
mkdir subdir6b
|
||||
ln -s ../regular1 subdir6a/foo
|
||||
@ -128,27 +132,25 @@ ln -s ../regular1 subdir6b/foo
|
||||
diff -r --no-dereference subdir6a subdir6b > out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# Test case 7: Compare symbolic links with different value and different target
|
||||
# contents.
|
||||
# Compare symbolic links with different value and different target contents.
|
||||
mkdir subdir7a
|
||||
mkdir subdir7b
|
||||
ln -s ../regular1 subdir7a/foo
|
||||
ln -s ../regular2 subdir7b/foo
|
||||
returns_ 1 diff -r --no-dereference subdir7a subdir7b > out || fail=1
|
||||
LC_ALL=C returns_ 1 diff -r --no-dereference subdir7a subdir7b > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
Symbolic links subdir7a/foo and subdir7b/foo differ
|
||||
Symbolic links 'subdir7a/foo' -> '../regular1' and 'subdir7b/foo' -> '../regular2' differ
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
# Test case 8: Compare symbolic links with different value and same target
|
||||
# contents.
|
||||
# Compare symbolic links with different value and same target contents.
|
||||
mkdir subdir8a
|
||||
mkdir subdir8b
|
||||
ln -s ../regular2 subdir8a/foo
|
||||
ln -s ../regular3 subdir8b/foo
|
||||
returns_ 1 diff -r --no-dereference subdir8a subdir8b > out || fail=1
|
||||
LC_ALL=C returns_ 1 diff -r --no-dereference subdir8a subdir8b > out || fail=1
|
||||
cat <<EOF > expected || framework_failure_
|
||||
Symbolic links subdir8a/foo and subdir8b/foo differ
|
||||
Symbolic links 'subdir8a/foo' -> '../regular2' and 'subdir8b/foo' -> '../regular3' differ
|
||||
EOF
|
||||
compare expected out || fail=1
|
||||
|
||||
|
||||
52
tests/side-by-side
Executable file
52
tests/side-by-side
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
# Test side-by-side output with non-ASCII characters.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
require_utf8_locale_
|
||||
|
||||
fail=0
|
||||
|
||||
cat >in1 <<\EOF || framework_failure_
|
||||
a
|
||||
ab
|
||||
abc
|
||||
[一天早上,當格雷戈爾·薩姆沙從不安的夢中醒來時,
|
||||
他發現自己在床上變成了一隻可怕的害蟲。
|
||||
]
|
||||
EOF
|
||||
|
||||
cat >in2 <<\EOF || framework_failure_
|
||||
ab
|
||||
abcd
|
||||
<一天早上,當格雷戈爾·薩姆沙從不安的夢中醒來時,
|
||||
他發現自己在床上變成了一隻可怕的害蟲。
|
||||
>
|
||||
EOF
|
||||
|
||||
tr '_@' ' \t' >exp <<'EOF' || framework_failure_
|
||||
a@@_____<
|
||||
ab@@@ab
|
||||
abc@@_____|@abcd
|
||||
[一天早上,當格雷戈__|@<一天早上,當格雷戈
|
||||
_他發現自己在床上變@_他發現自己在床上變
|
||||
]@@_____|@>
|
||||
EOF
|
||||
|
||||
returns_ 1 diff -y -W 44 in1 in2 >out 2>err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
cat >exp <<'EOF' || framework_failure_
|
||||
a <
|
||||
ab ab
|
||||
abc | abcd
|
||||
[一天早上,當格雷戈 | <一天早上,當格雷戈
|
||||
他發現自己在床上變 他發現自己在床上變
|
||||
] | >
|
||||
EOF
|
||||
|
||||
returns_ 1 diff --expand-tabs -y -W 44 in1 in2 >out 2>err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
Exit $fail
|
||||
26
tests/side-by-side-seq
Executable file
26
tests/side-by-side-seq
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Test side-by-side output on sequences.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
seq 1 100 >in1 || framework_failure_
|
||||
(seq 1 49 && seq 51 100) >in2 || framework_failure_
|
||||
awk '
|
||||
BEGIN {
|
||||
for (i = 1; i <= 100; i++) {
|
||||
if (i == 50) {
|
||||
print "50 <"
|
||||
} else {
|
||||
printf "%d\t%d\n", i, i
|
||||
}
|
||||
}
|
||||
}
|
||||
' </dev/null >exp || framework_failure_
|
||||
|
||||
returns_ 1 diff -yW 11 in1 in2 >out 2>err || fail=1
|
||||
compare exp out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
Exit $fail
|
||||
20
tests/starting-file
Executable file
20
tests/starting-file
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Test diff -S.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
mkdir d1 d2 || framework_failure_
|
||||
|
||||
for i in a abc abC aBc aBC z; do
|
||||
echo $i >d1/$i || framework_failure_
|
||||
done
|
||||
|
||||
for i in a ABC ABc AbC Abc; do
|
||||
echo x$i >d2/$i || framework_failure_
|
||||
done
|
||||
echo z >d2/z || framework_failure_
|
||||
|
||||
LC_ALL=C diff -S z d1 d2 || fail=1
|
||||
LC_ALL=C diff -S z --ignore-file-name-case d1 d2 || fail=1
|
||||
|
||||
Exit $fail
|
||||
19
tests/stdin
19
tests/stdin
@ -5,7 +5,9 @@
|
||||
|
||||
fail=0
|
||||
|
||||
cat <<EOF > exp || fail=1
|
||||
echo a > a || framework_failure_
|
||||
echo b > b || framework_failure_
|
||||
cat <<'EOF' > exp || framework_failure_
|
||||
--- -
|
||||
+++ b
|
||||
@@ -1 +1 @@
|
||||
@ -13,12 +15,17 @@ cat <<EOF > exp || fail=1
|
||||
+b
|
||||
EOF
|
||||
|
||||
echo a > a
|
||||
echo b > b
|
||||
|
||||
returns_ 1 diff -u - b < a > out 2> err || fail=1
|
||||
# Remove date and time.
|
||||
sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out > k; mv k out
|
||||
compare exp out || fail=1
|
||||
sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out >outk || framework_failure_
|
||||
compare exp outk || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
mkdir d || framework_failure_
|
||||
echo a >d/a || framework_failure_
|
||||
|
||||
diff -u - a <d >out 2>err || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
Exit $fail
|
||||
|
||||
25
tests/strip-trailing-cr
Executable file
25
tests/strip-trailing-cr
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Before diff-3.7, this would provoke a UMR
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
require_valgrind_
|
||||
|
||||
printf '\r' > r || framework_failure_
|
||||
echo b > b || framework_failure_
|
||||
|
||||
# Use valgrind only when no Sanitizer is in use:
|
||||
ASAN_OPTIONS=help=true diff --help 2>&1 |grep Sanitizer > /dev/null \
|
||||
&& eval 'valgrind() { "$@"; }' \
|
||||
|| eval 'valgrind() { env valgrind --quiet --error-exitcode=3 "$@"; }'
|
||||
|
||||
for opt in '' u c Dfoo; do
|
||||
returns_ 1 valgrind \
|
||||
diff -a$opt --strip-trailing-cr r b > out 2> err || fail=1
|
||||
done
|
||||
|
||||
compare /dev/null err || fail=1
|
||||
|
||||
Exit $fail
|
||||
14
tests/timezone
Executable file
14
tests/timezone
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
# In diff 3.4 through 3.8, this would output the wrong timezone on Solaris.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
echo a >a || fail=1
|
||||
case $(LC_ALL=C TZ=EST5 diff -u /dev/null a) in
|
||||
*' -0500'*) ;;
|
||||
*) fail=1 ;;
|
||||
esac
|
||||
|
||||
Exit $fail
|
||||
13
tests/y2038-vs-32bit
Executable file
13
tests/y2038-vs-32bit
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# dates after Y2K38 are safe
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
|
||||
fail=0
|
||||
|
||||
touch -t 203901010000 in || skip_ 'touch -t 2039... failed'
|
||||
|
||||
cmp in in || fail=1
|
||||
diff in in || fail=1
|
||||
|
||||
Exit $fail
|
||||
Loading…
x
Reference in New Issue
Block a user