Import VX multicall utility and applets
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
build*/
|
||||||
|
references
|
||||||
|
.vscode
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document defines mandatory development rules for contributors and automated agents working on the VX Multicall Utility.
|
||||||
|
|
||||||
|
All code changes must comply with the policies below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. No Stubs
|
||||||
|
|
||||||
|
Stubs are not allowed.
|
||||||
|
|
||||||
|
Do **not**:
|
||||||
|
|
||||||
|
- Add placeholder functions
|
||||||
|
- Leave partially implemented logic
|
||||||
|
- Add empty modules “for later”
|
||||||
|
- Return dummy values to satisfy the compiler
|
||||||
|
|
||||||
|
All submitted code must:
|
||||||
|
|
||||||
|
- Be fully implemented
|
||||||
|
- Compile cleanly
|
||||||
|
- Function as described
|
||||||
|
- Pass relevant tests
|
||||||
|
|
||||||
|
If a feature is not ready, do not merge it.
|
||||||
|
|
||||||
|
## 2. No Dead Code
|
||||||
|
|
||||||
|
Do not introduce:
|
||||||
|
|
||||||
|
- Unused structs
|
||||||
|
- Unused enums
|
||||||
|
- Unused functions
|
||||||
|
- Unused modules
|
||||||
|
- Commented-out code
|
||||||
|
- Remove makefiles etc in freshly copied BSD sources
|
||||||
|
|
||||||
|
Remove obsolete code instead of commenting it out.
|
||||||
|
|
||||||
|
## 3. Deterministic Behavior
|
||||||
|
|
||||||
|
VX Multicall Utility requires reproducibility.
|
||||||
|
|
||||||
|
All logic must:
|
||||||
|
|
||||||
|
- Avoid non-deterministic behavior
|
||||||
|
- Avoid reliance on environment state unless explicitly defined
|
||||||
|
- Avoid hidden global state
|
||||||
|
|
||||||
|
## 4. Testing
|
||||||
|
|
||||||
|
New features must include:
|
||||||
|
|
||||||
|
- add tests for new functionality
|
||||||
|
- ensure existing tests pass
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
VX Multicall Utility is infrastructure.
|
||||||
|
|
||||||
|
Code must be:
|
||||||
|
|
||||||
|
- Complete
|
||||||
|
- Deterministic
|
||||||
|
- Secure
|
||||||
|
- Minimal
|
||||||
|
- Explicit
|
||||||
|
|
||||||
|
No shortcuts.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 SFG545 and ReallyUnusual
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the “Software”), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
# @(#)COPYRIGHT 8.2 (Berkeley) 3/21/94
|
||||||
|
|
||||||
|
The compilation of software known as FreeBSD is distributed under the
|
||||||
|
following terms:
|
||||||
|
|
||||||
|
Copyright (c) 1992-2023 The FreeBSD Project.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
||||||
|
The 4.4BSD and 4.4BSD-Lite software is distributed under the following
|
||||||
|
terms:
|
||||||
|
|
||||||
|
All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite
|
||||||
|
Releases is copyrighted by The Regents of the University of California.
|
||||||
|
|
||||||
|
Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
|
||||||
|
The Regents of the University of California. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. All advertising materials mentioning features or use of this software
|
||||||
|
must display the following acknowledgement:
|
||||||
|
This product includes software developed by the University of
|
||||||
|
California, Berkeley and its contributors.
|
||||||
|
4. Neither the name of the University nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
||||||
|
The Institute of Electrical and Electronics Engineers and the American
|
||||||
|
National Standards Committee X3, on Information Processing Systems have
|
||||||
|
given us permission to reprint portions of their documentation.
|
||||||
|
|
||||||
|
In the following statement, the phrase ``this text'' refers to portions
|
||||||
|
of the system documentation.
|
||||||
|
|
||||||
|
Portions of this text are reprinted and reproduced in electronic form in
|
||||||
|
the second BSD Networking Software Release, from IEEE Std 1003.1-1988, IEEE
|
||||||
|
Standard Portable Operating System Interface for Computer Environments
|
||||||
|
(POSIX), copyright C 1988 by the Institute of Electrical and Electronics
|
||||||
|
Engineers, Inc. In the event of any discrepancy between these versions
|
||||||
|
and the original IEEE Standard, the original IEEE Standard is the referee
|
||||||
|
document.
|
||||||
|
|
||||||
|
In the following statement, the phrase ``This material'' refers to portions
|
||||||
|
of the system documentation.
|
||||||
|
|
||||||
|
This material is reproduced with permission from American National
|
||||||
|
Standards Committee X3, on Information Processing Systems. Computer and
|
||||||
|
Business Equipment Manufacturers Association (CBEMA), 311 First St., NW,
|
||||||
|
Suite 500, Washington, DC 20001-2178. The developmental work of
|
||||||
|
Programming Language C was completed by the X3J11 Technical Committee.
|
||||||
|
|
||||||
|
The views and conclusions contained in the software and documentation are
|
||||||
|
those of the authors and should not be interpreted as representing official
|
||||||
|
policies, either expressed or implied, of the Regents of the University
|
||||||
|
of California.
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: The copyright of UC Berkeley's Berkeley Software Distribution ("BSD")
|
||||||
|
source has been updated. The copyright addendum may be found at
|
||||||
|
ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change and is
|
||||||
|
included below.
|
||||||
|
|
||||||
|
July 22, 1999
|
||||||
|
|
||||||
|
To All Licensees, Distributors of Any Version of BSD:
|
||||||
|
|
||||||
|
As you know, certain of the Berkeley Software Distribution ("BSD") source
|
||||||
|
code files require that further distributions of products containing all or
|
||||||
|
portions of the software, acknowledge within their advertising materials
|
||||||
|
that such products contain software developed by UC Berkeley and its
|
||||||
|
contributors.
|
||||||
|
|
||||||
|
Specifically, the provision reads:
|
||||||
|
|
||||||
|
" * 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors."
|
||||||
|
|
||||||
|
Effective immediately, licensees and distributors are no longer required to
|
||||||
|
include the acknowledgement within advertising materials. Accordingly, the
|
||||||
|
foregoing paragraph of those BSD Unix files containing it is hereby deleted
|
||||||
|
in its entirety.
|
||||||
|
|
||||||
|
William Hoskins
|
||||||
|
Director, Office of Technology Licensing
|
||||||
|
University of California, Berkeley
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# VX
|
||||||
|
|
||||||
|
VX is a BSD-oriented multicall utility for Linux.
|
||||||
|
|
||||||
|
It bundles a growing set of FreeBSD-derived userland tools into a single `vx` binary, with applets selected by `argv[0]` or by subcommand.
|
||||||
|
|
||||||
|
## Included applets
|
||||||
|
|
||||||
|
- `patch`
|
||||||
|
- `diff`, `cmp`, `diff3`, `sdiff`
|
||||||
|
- `which`
|
||||||
|
- `gzip`, `gunzip`, `zcat`, `gzcat`, `uncompress`
|
||||||
|
- `grep`, `egrep`, `fgrep`, `rgrep`
|
||||||
|
- `find`
|
||||||
|
- `xargs`
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
VX uses Meson.
|
||||||
|
|
||||||
|
```text
|
||||||
|
meson setup builddir
|
||||||
|
meson compile -C builddir
|
||||||
|
meson test -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
You can invoke applets either through the multicall binary:
|
||||||
|
|
||||||
|
```text
|
||||||
|
./builddir/vx grep pattern file
|
||||||
|
./builddir/vx find . -name '*.c'
|
||||||
|
```
|
||||||
|
|
||||||
|
or via installed symlinks such as `grep`, `find`, or `patch`.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The project force-includes `src/compat-headers/compat.h` to provide Linux compatibility shims for BSD userland code.
|
||||||
|
- Some behavior is intentionally BSD-flavored, even where GNU tools differ.
|
||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
project('vx', 'c',
|
||||||
|
version : '0.1.0',
|
||||||
|
default_options : [
|
||||||
|
'c_std=c11',
|
||||||
|
'warning_level=2',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Portability shim for building BSD userland on Linux ---
|
||||||
|
compat_inc = include_directories('src/compat-headers')
|
||||||
|
|
||||||
|
compat_src = files(
|
||||||
|
'src/compat-headers/fgetln.c',
|
||||||
|
'src/compat-headers/strlcpy.c',
|
||||||
|
'src/compat-headers/reallocf.c',
|
||||||
|
'src/compat-headers/strtonum.c',
|
||||||
|
'src/compat-headers/expand_number.c',
|
||||||
|
'src/compat-headers/strmode.c',
|
||||||
|
'src/compat-headers/ugid.c',
|
||||||
|
'src/compat-headers/setmode.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Force-include compat.h before every translation unit so that
|
||||||
|
# _GNU_SOURCE and type shims are always available.
|
||||||
|
compat_args = ['-include', 'compat.h']
|
||||||
|
|
||||||
|
patch_src = files(
|
||||||
|
'src/patch/patch.c',
|
||||||
|
'src/patch/pch.c',
|
||||||
|
'src/patch/inp.c',
|
||||||
|
'src/patch/util.c',
|
||||||
|
'src/patch/backupfile.c',
|
||||||
|
'src/patch/mkpath.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
diff_src = files(
|
||||||
|
'src/diff/diff.c',
|
||||||
|
'src/diff/diffdir.c',
|
||||||
|
'src/diff/diffreg.c',
|
||||||
|
'src/diff/pr.c',
|
||||||
|
'src/diff/xmalloc.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
cmp_src = files(
|
||||||
|
'src/cmp/cmp.c',
|
||||||
|
'src/cmp/link.c',
|
||||||
|
'src/cmp/misc.c',
|
||||||
|
'src/cmp/regular.c',
|
||||||
|
'src/cmp/special.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
diff3_src = files(
|
||||||
|
'src/diff3/diff3.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
sdiff_src = files(
|
||||||
|
'src/sdiff/sdiff.c',
|
||||||
|
'src/sdiff/edit.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
which_src = files(
|
||||||
|
'src/which/which.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
gzip_src = files(
|
||||||
|
'src/gzip/gzip.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
gzip_inc = include_directories('src/gzip')
|
||||||
|
|
||||||
|
grep_src = files(
|
||||||
|
'src/grep/grep.c',
|
||||||
|
'src/grep/file.c',
|
||||||
|
'src/grep/queue.c',
|
||||||
|
'src/grep/util.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
grep_inc = include_directories('src/grep')
|
||||||
|
|
||||||
|
# --- yacc generator for find's getdate.y ---
|
||||||
|
yacc = find_program('yacc', 'bison')
|
||||||
|
getdate_gen = custom_target('getdate',
|
||||||
|
input : 'src/find/getdate.y',
|
||||||
|
output : 'getdate.c',
|
||||||
|
command : [yacc, '-o', '@OUTPUT@', '@INPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
find_src = files(
|
||||||
|
'src/find/find.c',
|
||||||
|
'src/find/function.c',
|
||||||
|
'src/find/ls.c',
|
||||||
|
'src/find/main.c',
|
||||||
|
'src/find/misc.c',
|
||||||
|
'src/find/operator.c',
|
||||||
|
'src/find/option.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
find_inc = include_directories('src/find')
|
||||||
|
|
||||||
|
xargs_src = files(
|
||||||
|
'src/xargs/xargs.c',
|
||||||
|
'src/xargs/strnsubst.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
xargs_inc = include_directories('src/xargs')
|
||||||
|
|
||||||
|
gzip_defines = [
|
||||||
|
'-DNO_BZIP2_SUPPORT',
|
||||||
|
'-DNO_ZSTD_SUPPORT',
|
||||||
|
]
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
libm = cc.find_library('m', required : false)
|
||||||
|
libz = dependency('zlib')
|
||||||
|
liblzma = dependency('liblzma')
|
||||||
|
libacl = cc.find_library('acl', required : true)
|
||||||
|
|
||||||
|
vx = executable('vx',
|
||||||
|
'src/main.c',
|
||||||
|
patch_src,
|
||||||
|
diff_src,
|
||||||
|
cmp_src,
|
||||||
|
diff3_src,
|
||||||
|
sdiff_src,
|
||||||
|
which_src,
|
||||||
|
gzip_src,
|
||||||
|
grep_src,
|
||||||
|
find_src,
|
||||||
|
getdate_gen,
|
||||||
|
xargs_src,
|
||||||
|
compat_src,
|
||||||
|
include_directories : [compat_inc, gzip_inc, grep_inc, find_inc, xargs_inc],
|
||||||
|
c_args : compat_args + gzip_defines,
|
||||||
|
dependencies : [libm, libz, liblzma, libacl],
|
||||||
|
install : true,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create symlinks for all applets pointing to the vx binary
|
||||||
|
foreach name : ['patch', 'diff', 'cmp', 'diff3', 'sdiff', 'which', 'gzip', 'gunzip', 'zcat', 'uncompress', 'grep', 'egrep', 'fgrep', 'rgrep', 'find', 'xargs']
|
||||||
|
meson.add_install_script('sh', '-c',
|
||||||
|
'ln -sf vx "$DESTDIR$1/' + name + '"',
|
||||||
|
'_', get_option('bindir'),
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# --- Manpages ---
|
||||||
|
install_man(
|
||||||
|
'src/patch/patch.1',
|
||||||
|
'src/diff/diff.1',
|
||||||
|
'src/cmp/cmp.1',
|
||||||
|
'src/diff3/diff3.1',
|
||||||
|
'src/sdiff/sdiff.1',
|
||||||
|
'src/which/which.1',
|
||||||
|
'src/gzip/gzip.1',
|
||||||
|
'src/gzip/gzexe.1',
|
||||||
|
'src/gzip/zdiff.1',
|
||||||
|
'src/gzip/zforce.1',
|
||||||
|
'src/gzip/zmore.1',
|
||||||
|
'src/gzip/znew.1',
|
||||||
|
'src/gzip/zgrep.1',
|
||||||
|
'src/grep/grep.1',
|
||||||
|
'src/find/find.1',
|
||||||
|
'src/xargs/xargs.1',
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Gzip shell script utilities ---
|
||||||
|
foreach s : ['zdiff', 'zforce', 'zmore', 'znew', 'gzexe', 'zgrep', 'zless']
|
||||||
|
install_data('src/gzip/' + s,
|
||||||
|
install_dir : get_option('bindir'),
|
||||||
|
install_mode : 'rwxr-xr-x',
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# Install symlinks for script aliases
|
||||||
|
foreach pair : [['zcmp', 'zdiff'], ['zegrep', 'zgrep'], ['zfgrep', 'zgrep']]
|
||||||
|
meson.add_install_script('sh', '-c',
|
||||||
|
'ln -sf ' + pair[1] + ' "$DESTDIR$1/' + pair[0] + '"',
|
||||||
|
'_', get_option('bindir'),
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# --- Tests ---
|
||||||
|
test_env = environment()
|
||||||
|
test_env.set('VX', vx.full_path())
|
||||||
|
|
||||||
|
foreach t : ['test_patch', 'test_diff', 'test_cmp', 'test_diff3', 'test_sdiff', 'test_which', 'test_gzip', 'test_grep', 'test_find', 'test_xargs']
|
||||||
|
test(t, find_program('tests/' + t + '.sh'),
|
||||||
|
env : test_env,
|
||||||
|
depends : vx,
|
||||||
|
workdir : meson.project_source_root(),
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
.\" Copyright (c) 1987, 1990, 1993
|
||||||
|
.\" The Regents of the University of California. All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" This code is derived from software contributed to Berkeley by
|
||||||
|
.\" the Institute of Electrical and Electronics Engineers, Inc.
|
||||||
|
.\"
|
||||||
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
|
.\" modification, are permitted provided that the following conditions
|
||||||
|
.\" are met:
|
||||||
|
.\" 1. Redistributions of source code must retain the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer.
|
||||||
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer in the
|
||||||
|
.\" documentation and/or other materials provided with the distribution.
|
||||||
|
.\" 3. Neither the name of the University nor the names of its contributors
|
||||||
|
.\" may be used to endorse or promote products derived from this software
|
||||||
|
.\" without specific prior written permission.
|
||||||
|
.\"
|
||||||
|
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
.\" SUCH DAMAGE.
|
||||||
|
.\"
|
||||||
|
.\" @(#)cmp.1 8.1 (Berkeley) 6/6/93
|
||||||
|
.\"
|
||||||
|
.Dd September 23, 2021
|
||||||
|
.Dt CMP 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm cmp
|
||||||
|
.Nd compare two files
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl l | s | x
|
||||||
|
.Op Fl bhz
|
||||||
|
.Op Fl -ignore-initial Ns Cm = Ns Ar num1 Ns Op :num2
|
||||||
|
.Op Fl -bytes Ns Cm = Ns Ar num
|
||||||
|
.Ar file1 file2
|
||||||
|
.Op Ar skip1 Op Ar skip2
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility compares two files of any type and writes the results
|
||||||
|
to the standard output.
|
||||||
|
By default,
|
||||||
|
.Nm
|
||||||
|
is silent if the files are the same; if they differ, the byte
|
||||||
|
and line number at which the first difference occurred is reported.
|
||||||
|
.Pp
|
||||||
|
Bytes and lines are numbered beginning with one.
|
||||||
|
.Pp
|
||||||
|
The following options are available:
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It Fl b , Fl -print-bytes
|
||||||
|
Print each byte when a difference is found.
|
||||||
|
.It Fl h
|
||||||
|
Do not follow symbolic links.
|
||||||
|
.It Fl i Ar num1 Ns Oo :num2 Oc , Fl -ignore-initial= Ns Ar num1 Ns Op :num2
|
||||||
|
Skip
|
||||||
|
.Ar num1
|
||||||
|
bytes from
|
||||||
|
.Ar file1 ,
|
||||||
|
and optionally skip
|
||||||
|
.Ar num2
|
||||||
|
bytes from
|
||||||
|
.Ar file2 .
|
||||||
|
If
|
||||||
|
.Ar num2
|
||||||
|
is not specified, then
|
||||||
|
.Ar num1
|
||||||
|
is applied for both
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2 .
|
||||||
|
.It Fl l , Fl -verbose
|
||||||
|
Print the byte number (decimal) and the differing
|
||||||
|
byte values (octal) for each difference.
|
||||||
|
.It Fl n Ar num , Fl -bytes= Ns num
|
||||||
|
Only compare up to
|
||||||
|
.Ar num
|
||||||
|
bytes.
|
||||||
|
.It Fl s , Fl -silent , Fl -quiet
|
||||||
|
Print nothing for differing files; return exit
|
||||||
|
status only.
|
||||||
|
.It Fl x
|
||||||
|
Like
|
||||||
|
.Fl l
|
||||||
|
but prints in hexadecimal and using zero as index
|
||||||
|
for the first byte in the files.
|
||||||
|
.It Fl z
|
||||||
|
For regular files compare file sizes first, and fail the comparison if they
|
||||||
|
are not equal.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The optional arguments
|
||||||
|
.Ar skip1
|
||||||
|
and
|
||||||
|
.Ar skip2
|
||||||
|
are the byte offsets from the beginning of
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2 ,
|
||||||
|
respectively, where the comparison will begin.
|
||||||
|
The offset is decimal by default, but may be expressed as a hexadecimal
|
||||||
|
or octal value by preceding it with a leading ``0x'' or ``0''.
|
||||||
|
.Pp
|
||||||
|
.Ar skip1
|
||||||
|
and
|
||||||
|
.Ar skip2
|
||||||
|
may also be specified with SI size suffixes.
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility exits with one of the following values:
|
||||||
|
.Bl -tag -width 4n
|
||||||
|
.It 0
|
||||||
|
The files are identical.
|
||||||
|
.It 1
|
||||||
|
The files are different; this includes the case
|
||||||
|
where one file is identical to the first part of
|
||||||
|
the other.
|
||||||
|
In the latter case, if the
|
||||||
|
.Fl s
|
||||||
|
option has not been specified,
|
||||||
|
.Nm
|
||||||
|
writes to standard error that EOF was reached in the shorter
|
||||||
|
file (before any differences were found).
|
||||||
|
.It >1
|
||||||
|
An error occurred.
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Assuming a file named
|
||||||
|
.Pa example.txt
|
||||||
|
with the following contents:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Compare stdin with
|
||||||
|
.Pa example.txt :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ echo -e "a\\nb\\nc" | cmp - example.txt
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Same as above but introducing a change in the third byte of stdin.
|
||||||
|
Show the byte number (decimal) and differing byte (octal):
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ echo -e "a\\nR\\nc" | cmp -l - example.txt
|
||||||
|
3 122 142
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Compare file sizes of
|
||||||
|
.Pa example.txt
|
||||||
|
and
|
||||||
|
.Pa /boot/loader.conf
|
||||||
|
and return 1 if they are not equal.
|
||||||
|
Note that
|
||||||
|
.Fl z
|
||||||
|
can only be used with regular files:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ cmp -z example.txt /boot/loader.conf
|
||||||
|
example.txt /boot/loader.conf differ: size
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Compare stdin with
|
||||||
|
.Pa example.txt
|
||||||
|
omitting the first 4 bytes from stdin and the first 2 bytes from
|
||||||
|
.Pa example.txt :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ echo -e "a\\nR\\nb\\nc" | cmp - example.txt 4 2
|
||||||
|
.Ed
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr diff 1 ,
|
||||||
|
.Xr diff3 1
|
||||||
|
.Sh STANDARDS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility is expected to be
|
||||||
|
.St -p1003.2
|
||||||
|
compatible.
|
||||||
|
The
|
||||||
|
.Fl b ,
|
||||||
|
.Fl h ,
|
||||||
|
.Fl i ,
|
||||||
|
.Fl n ,
|
||||||
|
.Fl x ,
|
||||||
|
and
|
||||||
|
.Fl z
|
||||||
|
options are extensions to the standard.
|
||||||
|
.Ar skip1
|
||||||
|
and
|
||||||
|
.Ar skip2
|
||||||
|
arguments are extensions to the standard.
|
||||||
|
.Sh HISTORY
|
||||||
|
A
|
||||||
|
.Nm
|
||||||
|
command appeared in
|
||||||
|
.At v1 .
|
||||||
|
.Sh BUGS
|
||||||
|
The phrase
|
||||||
|
.Dq SI size suffixes
|
||||||
|
above refers to the traditional power of two convention, as described in
|
||||||
|
.Xr expand_number 3 .
|
||||||
+266
@@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1987, 1990, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <nl_types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
bool bflag, lflag, sflag, xflag, zflag;
|
||||||
|
|
||||||
|
static const struct option long_opts[] =
|
||||||
|
{
|
||||||
|
{"print-bytes", no_argument, NULL, 'b'},
|
||||||
|
{"ignore-initial", required_argument, NULL, 'i'},
|
||||||
|
{"verbose", no_argument, NULL, 'l'},
|
||||||
|
{"bytes", required_argument, NULL, 'n'},
|
||||||
|
{"silent", no_argument, NULL, 's'},
|
||||||
|
{"quiet", no_argument, NULL, 's'},
|
||||||
|
{NULL, no_argument, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef SIGINFO
|
||||||
|
volatile sig_atomic_t info;
|
||||||
|
|
||||||
|
static void
|
||||||
|
siginfo(int signo)
|
||||||
|
{
|
||||||
|
info = signo;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void usage(void) __dead2;
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_iskipspec(char *spec, off_t *skip1, off_t *skip2)
|
||||||
|
{
|
||||||
|
char *colon;
|
||||||
|
|
||||||
|
colon = strchr(spec, ':');
|
||||||
|
if (colon != NULL)
|
||||||
|
*colon++ = '\0';
|
||||||
|
|
||||||
|
if (expand_number(spec, skip1) < 0)
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
if (colon != NULL)
|
||||||
|
return (expand_number(colon, skip2) == 0);
|
||||||
|
|
||||||
|
*skip2 = *skip1;
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cmp_main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct stat sb1, sb2;
|
||||||
|
off_t skip1, skip2, limit;
|
||||||
|
int ch, fd1, fd2, oflag;
|
||||||
|
bool special;
|
||||||
|
const char *file1, *file2;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
limit = skip1 = skip2 = ret = 0;
|
||||||
|
oflag = O_RDONLY;
|
||||||
|
while ((ch = getopt_long(argc, argv, "+bhi:ln:sxz", long_opts, NULL)) != -1)
|
||||||
|
switch (ch) {
|
||||||
|
case 'b': /* Print bytes */
|
||||||
|
bflag = true;
|
||||||
|
break;
|
||||||
|
case 'h': /* Don't follow symlinks */
|
||||||
|
oflag |= O_NOFOLLOW;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (!parse_iskipspec(optarg, &skip1, &skip2)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Invalid --ignore-initial: %s\n",
|
||||||
|
optarg);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l': /* print all differences */
|
||||||
|
lflag = true;
|
||||||
|
break;
|
||||||
|
case 'n': /* Limit */
|
||||||
|
if (expand_number(optarg, &limit) < 0 || limit < 0) {
|
||||||
|
fprintf(stderr, "Invalid --bytes: %s\n",
|
||||||
|
optarg);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 's': /* silent run */
|
||||||
|
sflag = true;
|
||||||
|
break;
|
||||||
|
case 'x': /* hex output */
|
||||||
|
lflag = true;
|
||||||
|
xflag = true;
|
||||||
|
break;
|
||||||
|
case 'z': /* compare size first */
|
||||||
|
zflag = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
argv += optind;
|
||||||
|
argc -= optind;
|
||||||
|
|
||||||
|
if (lflag && sflag)
|
||||||
|
errx(ERR_EXIT, "specifying -s with -l or -x is not permitted");
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 4)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
/* Don't limit rights on stdin since it may be one of the inputs. */
|
||||||
|
if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
|
||||||
|
err(ERR_EXIT, "unable to limit rights on stdout");
|
||||||
|
if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | CAPH_IGNORE_EBADF))
|
||||||
|
err(ERR_EXIT, "unable to limit rights on stderr");
|
||||||
|
|
||||||
|
/* Backward compatibility -- handle "-" meaning stdin. */
|
||||||
|
special = false;
|
||||||
|
if (strcmp(file1 = argv[0], "-") == 0) {
|
||||||
|
special = true;
|
||||||
|
fd1 = STDIN_FILENO;
|
||||||
|
file1 = "stdin";
|
||||||
|
} else if ((fd1 = open(file1, oflag, 0)) < 0 && errno != EMLINK) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file1);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
if (strcmp(file2 = argv[1], "-") == 0) {
|
||||||
|
if (special)
|
||||||
|
errx(ERR_EXIT,
|
||||||
|
"standard input may only be specified once");
|
||||||
|
special = true;
|
||||||
|
fd2 = STDIN_FILENO;
|
||||||
|
file2 = "stdin";
|
||||||
|
} else if ((fd2 = open(file2, oflag, 0)) < 0 && errno != EMLINK) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file2);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 2 && expand_number(argv[2], &skip1) < 0) {
|
||||||
|
fprintf(stderr, "Invalid skip1: %s\n", argv[2]);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 4 && expand_number(argv[3], &skip2) < 0) {
|
||||||
|
fprintf(stderr, "Invalid skip2: %s\n", argv[3]);
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sflag && skip1 == 0 && skip2 == 0)
|
||||||
|
zflag = true;
|
||||||
|
|
||||||
|
if (fd1 == -1) {
|
||||||
|
if (fd2 == -1) {
|
||||||
|
ret = c_link(file1, skip1, file2, skip2, limit);
|
||||||
|
goto end;
|
||||||
|
} else if (!sflag)
|
||||||
|
errx(ERR_EXIT, "%s: Not a symbolic link", file2);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
} else if (fd2 == -1) {
|
||||||
|
if (!sflag)
|
||||||
|
errx(ERR_EXIT, "%s: Not a symbolic link", file1);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FD rights are limited in c_special() and c_regular(). */
|
||||||
|
caph_cache_catpages();
|
||||||
|
|
||||||
|
if (!special) {
|
||||||
|
if (fstat(fd1, &sb1)) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file1);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
if (!S_ISREG(sb1.st_mode))
|
||||||
|
special = true;
|
||||||
|
else {
|
||||||
|
if (fstat(fd2, &sb2)) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file2);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
if (!S_ISREG(sb2.st_mode))
|
||||||
|
special = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SIGINFO
|
||||||
|
(void)signal(SIGINFO, siginfo);
|
||||||
|
#endif
|
||||||
|
if (special) {
|
||||||
|
ret = c_special(fd1, file1, skip1, fd2, file2, skip2, limit);
|
||||||
|
} else {
|
||||||
|
if (zflag && sb1.st_size != sb2.st_size) {
|
||||||
|
if (!sflag)
|
||||||
|
(void)printf("%s %s differ: size\n",
|
||||||
|
file1, file2);
|
||||||
|
ret = DIFF_EXIT;
|
||||||
|
} else {
|
||||||
|
ret = c_regular(fd1, file1, skip1, sb1.st_size,
|
||||||
|
fd2, file2, skip2, sb2.st_size, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if (!sflag && fflush(stdout) != 0)
|
||||||
|
err(ERR_EXIT, "stdout");
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)fprintf(stderr,
|
||||||
|
"usage: cmp [-l | -s | -x] [-hz] file1 file2 [skip1 [skip2]]\n");
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)extern.h 8.3 (Berkeley) 4/2/94
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Namespace flag globals to avoid collisions in the vx multicall binary.
|
||||||
|
* diff/diff.h defines identically-named globals.
|
||||||
|
*/
|
||||||
|
#define lflag cmp_lflag
|
||||||
|
#define sflag cmp_sflag
|
||||||
|
|
||||||
|
#define OK_EXIT 0
|
||||||
|
#define DIFF_EXIT 1
|
||||||
|
#define ERR_EXIT 2 /* error exit code */
|
||||||
|
|
||||||
|
int c_link(const char *, off_t, const char *, off_t, off_t);
|
||||||
|
int c_regular(int, const char *, off_t, off_t, int, const char *, off_t,
|
||||||
|
off_t, off_t);
|
||||||
|
int c_special(int, const char *, off_t, int, const char *, off_t, off_t);
|
||||||
|
void diffmsg(const char *, const char *, off_t, off_t, int, int);
|
||||||
|
void eofmsg(const char *);
|
||||||
|
|
||||||
|
extern bool bflag, lflag, sflag, xflag, zflag;
|
||||||
|
|
||||||
|
#ifdef SIGINFO
|
||||||
|
extern volatile sig_atomic_t info;
|
||||||
|
#endif
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 Brian Somers <brian@FreeBSD.org>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
c_link(const char *file1, off_t skip1, const char *file2, off_t skip2,
|
||||||
|
off_t limit)
|
||||||
|
{
|
||||||
|
char buf1[PATH_MAX], *p1;
|
||||||
|
char buf2[PATH_MAX], *p2;
|
||||||
|
ssize_t len1, len2;
|
||||||
|
int dfound;
|
||||||
|
off_t byte;
|
||||||
|
u_char ch;
|
||||||
|
|
||||||
|
if ((len1 = readlink(file1, buf1, sizeof(buf1) - 1)) < 0) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file1);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((len2 = readlink(file2, buf2, sizeof(buf2) - 1)) < 0) {
|
||||||
|
if (!sflag)
|
||||||
|
err(ERR_EXIT, "%s", file2);
|
||||||
|
else
|
||||||
|
exit(ERR_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip1 > len1)
|
||||||
|
skip1 = len1;
|
||||||
|
buf1[len1] = '\0';
|
||||||
|
|
||||||
|
if (skip2 > len2)
|
||||||
|
skip2 = len2;
|
||||||
|
buf2[len2] = '\0';
|
||||||
|
|
||||||
|
dfound = 0;
|
||||||
|
byte = 1;
|
||||||
|
for (p1 = buf1 + skip1, p2 = buf2 + skip2;
|
||||||
|
*p1 && *p2 && (limit == 0 || byte <= limit); p1++, p2++) {
|
||||||
|
if ((ch = *p1) != *p2) {
|
||||||
|
if (xflag) {
|
||||||
|
dfound = 1;
|
||||||
|
(void)printf("%08llx %02x %02x\n",
|
||||||
|
(long long)byte - 1, ch, *p2);
|
||||||
|
} else if (lflag) {
|
||||||
|
dfound = 1;
|
||||||
|
if (bflag)
|
||||||
|
(void)printf("%6lld %3o %c %3o %c\n",
|
||||||
|
(long long)byte, ch, ch, *p2, *p2);
|
||||||
|
else
|
||||||
|
(void)printf("%6lld %3o %3o\n",
|
||||||
|
(long long)byte, ch, *p2);
|
||||||
|
} else {
|
||||||
|
diffmsg(file1, file2, byte, 1, ch, *p2);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p1 || *p2) {
|
||||||
|
eofmsg (*p1 ? file2 : file1);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
return (dfound ? DIFF_EXIT : 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#ifndef lint
|
||||||
|
static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
eofmsg(const char *file)
|
||||||
|
{
|
||||||
|
if (!sflag)
|
||||||
|
warnx("EOF on %s", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diffmsg(const char *file1, const char *file2, off_t byte, off_t line,
|
||||||
|
int b1, int b2)
|
||||||
|
{
|
||||||
|
if (sflag) {
|
||||||
|
/* nothing */
|
||||||
|
} else if (bflag) {
|
||||||
|
(void)printf("%s %s differ: char %lld, line %lld is %3o %c %3o %c\n",
|
||||||
|
file1, file2, (long long)byte, (long long)line, b1, b1,
|
||||||
|
b2, b2);
|
||||||
|
} else {
|
||||||
|
(void)printf("%s %s differ: char %lld, line %lld\n",
|
||||||
|
file1, file2, (long long)byte, (long long)line);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#ifndef lint
|
||||||
|
static char sccsid[] = "@(#)regular.c 8.3 (Berkeley) 4/2/94";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
static u_char *remmap(u_char *, int, off_t);
|
||||||
|
static void segv_handler(int);
|
||||||
|
#define MMAP_CHUNK (8*1024*1024)
|
||||||
|
|
||||||
|
#define ROUNDPAGE(i) ((i) & ~pagemask)
|
||||||
|
|
||||||
|
int
|
||||||
|
c_regular(int fd1, const char *file1, off_t skip1, off_t len1,
|
||||||
|
int fd2, const char *file2, off_t skip2, off_t len2, off_t limit)
|
||||||
|
{
|
||||||
|
struct sigaction act, oact;
|
||||||
|
u_char ch, *p1, *p2, *m1, *m2, *e1, *e2;
|
||||||
|
off_t byte, length, line;
|
||||||
|
off_t pagemask, off1, off2;
|
||||||
|
size_t pagesize;
|
||||||
|
int dfound;
|
||||||
|
|
||||||
|
if (skip1 > len1) {
|
||||||
|
eofmsg(file1);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
len1 -= skip1;
|
||||||
|
if (skip2 > len2) {
|
||||||
|
eofmsg(file2);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
len2 -= skip2;
|
||||||
|
|
||||||
|
if (sflag && len1 != len2)
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
|
||||||
|
pagesize = getpagesize();
|
||||||
|
pagemask = (off_t)pagesize - 1;
|
||||||
|
off1 = ROUNDPAGE(skip1);
|
||||||
|
off2 = ROUNDPAGE(skip2);
|
||||||
|
|
||||||
|
length = MIN(len1, len2);
|
||||||
|
if (limit > 0)
|
||||||
|
length = MIN(length, limit);
|
||||||
|
|
||||||
|
if ((m1 = remmap(NULL, fd1, off1)) == NULL) {
|
||||||
|
return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m2 = remmap(NULL, fd2, off2)) == NULL) {
|
||||||
|
munmap(m1, MMAP_CHUNK);
|
||||||
|
return (c_special(fd1, file1, skip1, fd2, file2, skip2, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caph_rights_limit(fd1, cap_rights_init(&rights, CAP_MMAP_R)) < 0)
|
||||||
|
err(1, "unable to limit rights for %s", file1);
|
||||||
|
if (caph_rights_limit(fd2, cap_rights_init(&rights, CAP_MMAP_R)) < 0)
|
||||||
|
err(1, "unable to limit rights for %s", file2);
|
||||||
|
if (caph_enter() < 0)
|
||||||
|
err(ERR_EXIT, "unable to enter capability mode");
|
||||||
|
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_flags = SA_NODEFER;
|
||||||
|
act.sa_handler = segv_handler;
|
||||||
|
if (sigaction(SIGSEGV, &act, &oact))
|
||||||
|
err(ERR_EXIT, "sigaction()");
|
||||||
|
|
||||||
|
dfound = 0;
|
||||||
|
e1 = m1 + MMAP_CHUNK;
|
||||||
|
e2 = m2 + MMAP_CHUNK;
|
||||||
|
p1 = m1 + (skip1 - off1);
|
||||||
|
p2 = m2 + (skip2 - off2);
|
||||||
|
|
||||||
|
for (byte = line = 1; length--; ++byte) {
|
||||||
|
#ifdef SIGINFO
|
||||||
|
if (info) {
|
||||||
|
(void)fprintf(stderr, "%s %s char %zu line %zu\n",
|
||||||
|
file1, file2, (size_t)byte, (size_t)line);
|
||||||
|
info = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if ((ch = *p1) != *p2) {
|
||||||
|
dfound = 1;
|
||||||
|
if (xflag) {
|
||||||
|
(void)printf("%08llx %02x %02x\n",
|
||||||
|
(long long)byte - 1, ch, *p2);
|
||||||
|
} else if (lflag) {
|
||||||
|
if (bflag)
|
||||||
|
(void)printf("%6lld %3o %c %3o %c\n",
|
||||||
|
(long long)byte, ch, ch, *p2, *p2);
|
||||||
|
else
|
||||||
|
(void)printf("%6lld %3o %3o\n",
|
||||||
|
(long long)byte, ch, *p2);
|
||||||
|
} else {
|
||||||
|
diffmsg(file1, file2, byte, line, ch, *p2);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ch == '\n')
|
||||||
|
++line;
|
||||||
|
if (++p1 == e1) {
|
||||||
|
off1 += MMAP_CHUNK;
|
||||||
|
if ((p1 = m1 = remmap(m1, fd1, off1)) == NULL) {
|
||||||
|
munmap(m2, MMAP_CHUNK);
|
||||||
|
err(ERR_EXIT, "remmap %s", file1);
|
||||||
|
}
|
||||||
|
e1 = m1 + MMAP_CHUNK;
|
||||||
|
}
|
||||||
|
if (++p2 == e2) {
|
||||||
|
off2 += MMAP_CHUNK;
|
||||||
|
if ((p2 = m2 = remmap(m2, fd2, off2)) == NULL) {
|
||||||
|
munmap(m1, MMAP_CHUNK);
|
||||||
|
err(ERR_EXIT, "remmap %s", file2);
|
||||||
|
}
|
||||||
|
e2 = m2 + MMAP_CHUNK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
munmap(m1, MMAP_CHUNK);
|
||||||
|
munmap(m2, MMAP_CHUNK);
|
||||||
|
|
||||||
|
if (sigaction(SIGSEGV, &oact, NULL))
|
||||||
|
err(ERR_EXIT, "sigaction()");
|
||||||
|
|
||||||
|
if (len1 != len2) {
|
||||||
|
eofmsg(len1 > len2 ? file2 : file1);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
return (dfound ? DIFF_EXIT : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u_char *
|
||||||
|
remmap(u_char *mem, int fd, off_t offset)
|
||||||
|
{
|
||||||
|
if (mem != NULL)
|
||||||
|
munmap(mem, MMAP_CHUNK);
|
||||||
|
mem = mmap(NULL, MMAP_CHUNK, PROT_READ, MAP_SHARED, fd, offset);
|
||||||
|
if (mem == MAP_FAILED)
|
||||||
|
return (NULL);
|
||||||
|
madvise(mem, MMAP_CHUNK, MADV_SEQUENTIAL);
|
||||||
|
return (mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
segv_handler(int sig __unused) {
|
||||||
|
static const char msg[] = "cmp: Input/output error (caught SIGSEGV)\n";
|
||||||
|
|
||||||
|
write(STDERR_FILENO, msg, sizeof(msg));
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#ifndef lint
|
||||||
|
static char sccsid[] = "@(#)special.c 8.3 (Berkeley) 4/2/94";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
c_special(int fd1, const char *file1, off_t skip1,
|
||||||
|
int fd2, const char *file2, off_t skip2, off_t limit)
|
||||||
|
{
|
||||||
|
int ch1, ch2;
|
||||||
|
off_t byte, line;
|
||||||
|
FILE *fp1, *fp2;
|
||||||
|
int dfound;
|
||||||
|
|
||||||
|
if (caph_limit_stream(fd1, CAPH_READ) < 0)
|
||||||
|
err(ERR_EXIT, "caph_limit_stream(%s)", file1);
|
||||||
|
if (caph_limit_stream(fd2, CAPH_READ) < 0)
|
||||||
|
err(ERR_EXIT, "caph_limit_stream(%s)", file2);
|
||||||
|
if (caph_enter() < 0)
|
||||||
|
err(ERR_EXIT, "unable to enter capability mode");
|
||||||
|
|
||||||
|
if ((fp1 = fdopen(fd1, "r")) == NULL)
|
||||||
|
err(ERR_EXIT, "%s", file1);
|
||||||
|
(void)setvbuf(fp1, NULL, _IOFBF, 65536);
|
||||||
|
if ((fp2 = fdopen(fd2, "r")) == NULL)
|
||||||
|
err(ERR_EXIT, "%s", file2);
|
||||||
|
(void)setvbuf(fp2, NULL, _IOFBF, 65536);
|
||||||
|
|
||||||
|
dfound = 0;
|
||||||
|
while (skip1--)
|
||||||
|
if (getc(fp1) == EOF)
|
||||||
|
goto eof;
|
||||||
|
while (skip2--)
|
||||||
|
if (getc(fp2) == EOF)
|
||||||
|
goto eof;
|
||||||
|
|
||||||
|
for (byte = line = 1; limit == 0 || byte <= limit; ++byte) {
|
||||||
|
#ifdef SIGINFO
|
||||||
|
if (info) {
|
||||||
|
(void)fprintf(stderr, "%s %s char %zu line %zu\n",
|
||||||
|
file1, file2, (size_t)byte, (size_t)line);
|
||||||
|
info = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ch1 = getc(fp1);
|
||||||
|
ch2 = getc(fp2);
|
||||||
|
if (ch1 == EOF || ch2 == EOF)
|
||||||
|
break;
|
||||||
|
if (ch1 != ch2) {
|
||||||
|
if (xflag) {
|
||||||
|
dfound = 1;
|
||||||
|
(void)printf("%08llx %02x %02x\n",
|
||||||
|
(long long)byte - 1, ch1, ch2);
|
||||||
|
} else if (lflag) {
|
||||||
|
dfound = 1;
|
||||||
|
if (bflag)
|
||||||
|
(void)printf("%6lld %3o %c %3o %c\n",
|
||||||
|
(long long)byte, ch1, ch1, ch2,
|
||||||
|
ch2);
|
||||||
|
else
|
||||||
|
(void)printf("%6lld %3o %3o\n",
|
||||||
|
(long long)byte, ch1, ch2);
|
||||||
|
} else {
|
||||||
|
diffmsg(file1, file2, byte, line, ch1, ch2);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ch1 == '\n')
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
|
||||||
|
eof: if (ferror(fp1))
|
||||||
|
err(ERR_EXIT, "%s", file1);
|
||||||
|
if (ferror(fp2))
|
||||||
|
err(ERR_EXIT, "%s", file2);
|
||||||
|
if (feof(fp1)) {
|
||||||
|
if (!feof(fp2)) {
|
||||||
|
eofmsg(file1);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (feof(fp2)) {
|
||||||
|
eofmsg(file2);
|
||||||
|
return (DIFF_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp2);
|
||||||
|
fclose(fp1);
|
||||||
|
return (dfound ? DIFF_EXIT : 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
PACKAGE= tests
|
||||||
|
|
||||||
|
ATF_TESTS_SH+= cmp_test2
|
||||||
|
NETBSD_ATF_TESTS_SH= cmp_test
|
||||||
|
|
||||||
|
${PACKAGE}FILES+= b_flag.out
|
||||||
|
${PACKAGE}FILES+= bl_flag.out
|
||||||
|
|
||||||
|
.include <netbsd-tests.test.mk>
|
||||||
|
|
||||||
|
.include <bsd.test.mk>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Autogenerated - do NOT edit!
|
||||||
|
|
||||||
|
DIRDEPS = \
|
||||||
|
|
||||||
|
|
||||||
|
.include <dirdeps.mk>
|
||||||
|
|
||||||
|
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||||
|
# local dependencies - needed for -jN in clean tree
|
||||||
|
.endif
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
a b differ: char 3, line 1 is 143 c 144 d
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3 143 c 144 d
|
||||||
Executable
+175
@@ -0,0 +1,175 @@
|
|||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 Alan Somers
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||||
|
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||||
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
|
||||||
|
atf_test_case special
|
||||||
|
special_head() {
|
||||||
|
atf_set "descr" "Test cmp(1)'s handling of non-regular files"
|
||||||
|
}
|
||||||
|
special_body() {
|
||||||
|
echo 0123456789abcdef > a
|
||||||
|
echo 0123456789abcdeg > b
|
||||||
|
atf_check -s exit:0 -o empty -e empty cmp a - <a
|
||||||
|
atf_check -s exit:0 -o empty -e empty cmp - a <a
|
||||||
|
atf_check -s exit:1 -o not-empty -e empty cmp a - <b
|
||||||
|
atf_check -s exit:1 -o not-empty -e empty cmp - a <b
|
||||||
|
|
||||||
|
atf_check -s exit:0 -o empty -e empty cmp a a <&-
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case symlink
|
||||||
|
symlink_head() {
|
||||||
|
atf_set "descr" "Test cmp(1)'s handling of symlinks"
|
||||||
|
}
|
||||||
|
symlink_body() {
|
||||||
|
echo 0123456789abcdef > a
|
||||||
|
echo 0123456789abcdeg > b
|
||||||
|
ln -s a a.lnk
|
||||||
|
ln -s b b.lnk
|
||||||
|
ln -s a a2.lnk
|
||||||
|
cp a adup
|
||||||
|
ln -s adup adup.lnk
|
||||||
|
atf_check -s exit:0 cmp a a.lnk
|
||||||
|
atf_check -s exit:0 cmp a.lnk a
|
||||||
|
atf_check -s not-exit:0 -o ignore cmp a b.lnk
|
||||||
|
atf_check -s not-exit:0 -o ignore cmp b.lnk a
|
||||||
|
atf_check -s not-exit:0 -o ignore -e ignore cmp -h a a.lnk
|
||||||
|
atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk a
|
||||||
|
atf_check -s exit:0 cmp -h a.lnk a2.lnk
|
||||||
|
atf_check -s not-exit:0 -o ignore -e ignore cmp -h a.lnk adup.lnk
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case pr252542
|
||||||
|
pr252542_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Test cmp(1) -s with file offset skips"
|
||||||
|
}
|
||||||
|
pr252542_body()
|
||||||
|
{
|
||||||
|
echo -n '1234567890' > a
|
||||||
|
echo -n 'abc567890' > b
|
||||||
|
echo -n 'xbc567890' > c
|
||||||
|
atf_check -s exit:0 cmp -s a b 4 3
|
||||||
|
atf_check -s exit:0 cmp -i 4:3 -s a b
|
||||||
|
atf_check -s exit:0 cmp -i 1 -s b c
|
||||||
|
atf_check -s exit:1 -o ignore cmp -z a b 4 3
|
||||||
|
atf_check -s exit:1 -o ignore cmp -i 4:3 -z a b
|
||||||
|
atf_check -s exit:1 -o ignore cmp -i 1 -z a b
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case skipsuff
|
||||||
|
skipsuff_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Test cmp(1) accepting SI suffixes on skips"
|
||||||
|
}
|
||||||
|
skipsuff_body()
|
||||||
|
{
|
||||||
|
|
||||||
|
jot -nb a -s '' 1028 > a
|
||||||
|
jot -nb b -s '' 1024 > b
|
||||||
|
jot -nb a -s '' 4 >> b
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o ignore cmp -s a b
|
||||||
|
atf_check -s exit:0 cmp -s a b 1k 1k
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case limit
|
||||||
|
limit_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Test cmp(1) -n (limit)"
|
||||||
|
}
|
||||||
|
limit_body()
|
||||||
|
{
|
||||||
|
echo -n "aaaabbbb" > a
|
||||||
|
echo -n "aaaaxxxx" > b
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o ignore cmp -s a b
|
||||||
|
atf_check -s exit:0 cmp -sn 4 a b
|
||||||
|
atf_check -s exit:0 cmp -sn 3 a b
|
||||||
|
atf_check -s exit:1 -o ignore cmp -sn 5 a b
|
||||||
|
|
||||||
|
# Test special, too. The implementation for link is effectively
|
||||||
|
# identical.
|
||||||
|
atf_check -s exit:0 -e empty cmp -sn 4 b - <a
|
||||||
|
atf_check -s exit:0 -e empty cmp -sn 3 b - <a
|
||||||
|
atf_check -s exit:1 -o ignore cmp -sn 5 b - <a
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case bflag
|
||||||
|
bflag_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Test cmp(1) -b (print bytes)"
|
||||||
|
}
|
||||||
|
bflag_body()
|
||||||
|
{
|
||||||
|
echo -n "abcd" > a
|
||||||
|
echo -n "abdd" > b
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/b_flag.out \
|
||||||
|
cmp -b a b
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/bl_flag.out \
|
||||||
|
cmp -bl a b
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper for stdout test case
|
||||||
|
atf_check_stdout()
|
||||||
|
{
|
||||||
|
(
|
||||||
|
trap "" PIPE
|
||||||
|
sleep 1
|
||||||
|
cmp "$@" 2>stderr
|
||||||
|
echo $? >result
|
||||||
|
) | true
|
||||||
|
atf_check -o inline:"2\n" cat result
|
||||||
|
atf_check -o match:"stdout" cat stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_test_case stdout
|
||||||
|
stdout_head()
|
||||||
|
{
|
||||||
|
atf_set descr "Failure to write to stdout"
|
||||||
|
}
|
||||||
|
stdout_body()
|
||||||
|
{
|
||||||
|
echo a >a
|
||||||
|
echo b >b
|
||||||
|
atf_check_stdout a b
|
||||||
|
atf_check_stdout - b <a
|
||||||
|
atf_check_stdout a - <b
|
||||||
|
ln -s a alnk
|
||||||
|
ln -s b blnk
|
||||||
|
atf_check_stdout -h alnk blnk
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_init_test_cases()
|
||||||
|
{
|
||||||
|
atf_add_test_case special
|
||||||
|
atf_add_test_case symlink
|
||||||
|
atf_add_test_case pr252542
|
||||||
|
atf_add_test_case skipsuff
|
||||||
|
atf_add_test_case limit
|
||||||
|
atf_add_test_case bflag
|
||||||
|
atf_add_test_case stdout
|
||||||
|
}
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* compat.h - Portability shim for building BSD userland on Linux.
|
||||||
|
*
|
||||||
|
* Must be included before any system headers (either directly or via
|
||||||
|
* the compiler's -include flag).
|
||||||
|
*/
|
||||||
|
#ifndef VX_COMPAT_H
|
||||||
|
#define VX_COMPAT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Feature-test macros -- unlock POSIX/GNU extensions in glibc headers.
|
||||||
|
* This exposes: strdup, asprintf, mkstemp, reallocarray, madvise,
|
||||||
|
* MADV_SEQUENTIAL, isascii, PATH_MAX, among others.
|
||||||
|
*/
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* sys/cdefs.h compat -- BSD compiler attribute macros */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#ifndef __dead2
|
||||||
|
#define __dead2 __attribute__((noreturn))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __unused
|
||||||
|
#define __unused __attribute__((unused))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __DECONST
|
||||||
|
#define __DECONST(type, var) ((type)(unsigned long)(const void *)(var))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Capsicum / sandbox stubs -- Linux has no Capsicum */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef __FreeBSD__
|
||||||
|
typedef struct { int cr_rights; } cap_rights_t;
|
||||||
|
#define cap_rights_init(r, ...) ((void)0)
|
||||||
|
#define cap_rights_set(r, ...) ((void)0)
|
||||||
|
#define caph_rights_limit(fd, r) (0)
|
||||||
|
#define caph_limit_stream(fd, r) (0)
|
||||||
|
#define caph_limit_stdio() (0)
|
||||||
|
#define caph_limit_stdin() (0)
|
||||||
|
#define caph_limit_stdout() (0)
|
||||||
|
#define caph_limit_stderr() (0)
|
||||||
|
#define caph_cache_catpages() (0)
|
||||||
|
#define caph_cache_tzdata() (0)
|
||||||
|
#define caph_enter() (0)
|
||||||
|
#define CAP_READ 0
|
||||||
|
#define CAP_WRITE 0
|
||||||
|
#define CAP_FSTAT 0
|
||||||
|
#define CAP_SEEK 0
|
||||||
|
#define CAP_MMAP_R 0
|
||||||
|
#define CAP_FCNTL 0
|
||||||
|
#define CAP_EVENT 0
|
||||||
|
#define CAPH_READ 0
|
||||||
|
#define CAPH_WRITE 0
|
||||||
|
#define CAPH_IGNORE_EBADF 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* SIGINFO -- BSD-only signal; not available on Linux */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef SIGINFO
|
||||||
|
#define SIGINFO SIGUSR1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* sig_t -- BSD typedef for signal handler function pointer */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
#include <signal.h>
|
||||||
|
#ifndef __FreeBSD__
|
||||||
|
typedef void (*sig_t)(int);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* d_namlen -- BSD struct dirent member; Linux lacks it */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#ifndef _DIRENT_HAVE_D_NAMLEN
|
||||||
|
#define DIRENT_NAMLEN(dp) strlen((dp)->d_name)
|
||||||
|
#else
|
||||||
|
#define DIRENT_NAMLEN(dp) ((dp)->d_namlen)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* d_fileno -- BSD name for inode number in struct dirent.
|
||||||
|
* Linux uses d_ino.
|
||||||
|
*/
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
#define d_fileno d_ino
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* fgetln -- BSD line-reading function; see fgetln.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
char *fgetln(FILE *fp, size_t *lenp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* strlcpy / strlcat -- bounded string ops from OpenBSD */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
#if !defined(__GLIBC__) || (__GLIBC__ < 2) || \
|
||||||
|
(__GLIBC__ == 2 && __GLIBC_MINOR__ < 38)
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t dsize);
|
||||||
|
size_t strlcat(char *dst, const char *src, size_t dsize);
|
||||||
|
#define VX_NEED_STRLCPY 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* reallocf -- frees original on failure; see reallocf.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
void *reallocf(void *ptr, size_t size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* strtonum -- safe string-to-number; see strtonum.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
long long strtonum(const char *nptr, long long minval,
|
||||||
|
long long maxval, const char **errstrp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* errc / warnc -- BSD err.h variants with explicit errno */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#if !defined(__FreeBSD__)
|
||||||
|
#define errc(eval, code, ...) do { errno = (code); err((eval), __VA_ARGS__); } while (0)
|
||||||
|
#define warnc(code, ...) do { errno = (code); warn(__VA_ARGS__); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* expand_number -- FreeBSD libutil; see expand_number.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__)
|
||||||
|
#include <stdint.h>
|
||||||
|
int expand_number(const char *buf, uint64_t *num);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* stdckdint.h -- C23 checked integer arithmetic */
|
||||||
|
/* Provide fallback using GCC/Clang builtins. */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if defined(__has_include)
|
||||||
|
#if __has_include(<stdckdint.h>)
|
||||||
|
#include <stdckdint.h>
|
||||||
|
#define VX_HAVE_STDCKDINT 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef VX_HAVE_STDCKDINT
|
||||||
|
#define ckd_add(result, a, b) __builtin_add_overflow((a), (b), (result))
|
||||||
|
#define ckd_sub(result, a, b) __builtin_sub_overflow((a), (b), (result))
|
||||||
|
#define ckd_mul(result, a, b) __builtin_mul_overflow((a), (b), (result))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* roundup macro -- BSD sys/param.h; may be missing */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef roundup
|
||||||
|
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* optreset -- BSD getopt extension */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
#include <getopt.h>
|
||||||
|
static int vx_optreset_dummy_ __attribute__((unused));
|
||||||
|
#define optreset vx_optreset_dummy_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* strmode -- convert mode_t to ls-style string; see strmode.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
void strmode(mode_t mode, char *p);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* user_from_uid / group_from_gid -- cached passwd/group lookups */
|
||||||
|
/* see ugid.c */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
const char *user_from_uid(uid_t uid, int nouser);
|
||||||
|
const char *group_from_gid(gid_t gid, int nogroup);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* MAXLOGNAME -- BSD login name limit; Linux uses LOGIN_NAME_MAX */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef MAXLOGNAME
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef LOGIN_NAME_MAX
|
||||||
|
#define MAXLOGNAME LOGIN_NAME_MAX
|
||||||
|
#else
|
||||||
|
#define MAXLOGNAME 17
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* D_MD_ORDER -- BSD langinfo constant for month/day ordering */
|
||||||
|
/* Not available on Linux; define a harmless fallback. */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#include <langinfo.h>
|
||||||
|
#ifndef D_MD_ORDER
|
||||||
|
#define D_MD_ORDER D_FMT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* MAXPATHLEN -- BSD path limit constant; Linux uses PATH_MAX */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef MAXPATHLEN
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef PATH_MAX
|
||||||
|
#define MAXPATHLEN PATH_MAX
|
||||||
|
#else
|
||||||
|
#define MAXPATHLEN 4096
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* REG_BASIC -- BSD regex constant; 0 on Linux (default) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef REG_BASIC
|
||||||
|
#define REG_BASIC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* S_ISTXT -- BSD sticky bit name; Linux uses S_ISVTX */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef S_ISTXT
|
||||||
|
#define S_ISTXT S_ISVTX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* QUAD_MAX -- BSD quad_t limit; use LLONG_MAX */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifndef QUAD_MAX
|
||||||
|
#include <limits.h>
|
||||||
|
#define QUAD_MAX LLONG_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* setmode / getmode -- BSD file mode parsing */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
void *setmode(const char *p);
|
||||||
|
mode_t getmode(const void *set, mode_t omode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* makedev -- Linux needs sys/sysmacros.h */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VX_COMPAT_H */
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* expand_number.c - FreeBSD libutil expand_number() for Linux.
|
||||||
|
*
|
||||||
|
* expand_number(buf, num)
|
||||||
|
* Parses a string containing a number with an optional suffix
|
||||||
|
* denoting a multiplier. Supported suffixes:
|
||||||
|
* k/K = 1024, m/M = 1048576, g/G = 2^30, t/T = 2^40,
|
||||||
|
* p/P = 2^50, e/E = 2^60
|
||||||
|
* Returns 0 on success, -1 on error (with errno set).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if !defined(__FreeBSD__)
|
||||||
|
|
||||||
|
int
|
||||||
|
expand_number(const char *buf, uint64_t *num)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
unsigned long long val;
|
||||||
|
uint64_t mult;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val = strtoull(buf, &endptr, 0);
|
||||||
|
if (buf == endptr || errno != 0) {
|
||||||
|
if (errno == 0)
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mult = 1;
|
||||||
|
switch (*endptr) {
|
||||||
|
case 'e': case 'E':
|
||||||
|
mult = (uint64_t)1 << 60;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case 'p': case 'P':
|
||||||
|
mult = (uint64_t)1 << 50;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case 't': case 'T':
|
||||||
|
mult = (uint64_t)1 << 40;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case 'g': case 'G':
|
||||||
|
mult = (uint64_t)1 << 30;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case 'm': case 'M':
|
||||||
|
mult = (uint64_t)1 << 20;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case 'k': case 'K':
|
||||||
|
mult = (uint64_t)1 << 10;
|
||||||
|
endptr++;
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*endptr != '\0') {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val > UINT64_MAX / mult) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*num = val * mult;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__FreeBSD__ */
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* fgetln.c - compatibility implementation for Linux.
|
||||||
|
*
|
||||||
|
* fgetln() is a BSD extension (OpenBSD, FreeBSD, macOS) that reads the
|
||||||
|
* next line from a stream and returns a pointer to an internal buffer.
|
||||||
|
* The caller must NOT free the returned pointer. The buffer is only
|
||||||
|
* valid until the next fgetln() call on the same stream or until the
|
||||||
|
* stream is closed.
|
||||||
|
*
|
||||||
|
* Semantics:
|
||||||
|
* char *fgetln(FILE *fp, size_t *lenp);
|
||||||
|
*
|
||||||
|
* - Returns a pointer to the next line, INCLUDING the trailing '\n'
|
||||||
|
* (if present; the last line of a file may lack one).
|
||||||
|
* - *lenp is set to the number of bytes in the returned line.
|
||||||
|
* - The returned buffer is NOT NUL-terminated by the original BSD
|
||||||
|
* contract, but our implementation NUL-terminates for safety.
|
||||||
|
* - Returns NULL on EOF or error.
|
||||||
|
*
|
||||||
|
* This implementation uses POSIX getline() internally.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
|
||||||
|
char *
|
||||||
|
fgetln(FILE *fp, size_t *lenp)
|
||||||
|
{
|
||||||
|
static char *buf = NULL;
|
||||||
|
static size_t bufsz = 0;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
n = getline(&buf, &bufsz, fp);
|
||||||
|
if (n < 0) {
|
||||||
|
*lenp = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*lenp = (size_t)n;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__OpenBSD__ */
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* reallocf.c - FreeBSD/OpenBSD reallocf() for Linux.
|
||||||
|
*
|
||||||
|
* reallocf(ptr, size)
|
||||||
|
* Identical to realloc(), except that on failure it frees the
|
||||||
|
* original allocation before returning NULL. This prevents
|
||||||
|
* memory leaks in the common pattern:
|
||||||
|
* p = realloc(p, newsize); // leaks old p on failure
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
|
||||||
|
void *
|
||||||
|
reallocf(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
void *nptr;
|
||||||
|
|
||||||
|
nptr = realloc(ptr, size);
|
||||||
|
if (nptr == NULL && size != 0)
|
||||||
|
free(ptr);
|
||||||
|
return nptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__OpenBSD__ */
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* setmode / getmode - BSD file mode string parsing.
|
||||||
|
*
|
||||||
|
* Derived from FreeBSD's lib/libc/gen/setmode.c.
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* setmode() parses a symbolic mode string (e.g., "u+rw,g+r,o-w")
|
||||||
|
* and returns an opaque set. getmode() applies that set to a base mode.
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define SET_LEN 64
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mode_t bits; /* mode bits to set/clear */
|
||||||
|
char cmd; /* '+', '-', '=' */
|
||||||
|
char who; /* 'u', 'g', 'o', 'a' (mask selector) */
|
||||||
|
} BITCMD;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a symbolic mode string, compile into an array of BITCMD.
|
||||||
|
* Returns a malloc'd opaque pointer, or NULL on error.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
setmode(const char *p)
|
||||||
|
{
|
||||||
|
BITCMD *set, *saveset;
|
||||||
|
char op, who;
|
||||||
|
mode_t mask, bits;
|
||||||
|
int nsets;
|
||||||
|
|
||||||
|
if (!p || *p == '\0') {
|
||||||
|
/* Numeric mode */
|
||||||
|
if (p && *p >= '0' && *p <= '7') {
|
||||||
|
goto numeric;
|
||||||
|
}
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for numeric mode */
|
||||||
|
if (*p >= '0' && *p <= '7') {
|
||||||
|
numeric:;
|
||||||
|
unsigned long val;
|
||||||
|
char *ep;
|
||||||
|
val = strtoul(p, &ep, 8);
|
||||||
|
if (*ep != '\0' || val > 07777) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
set = calloc(2, sizeof(BITCMD));
|
||||||
|
if (set == NULL)
|
||||||
|
return NULL;
|
||||||
|
set[0].bits = (mode_t)val;
|
||||||
|
set[0].cmd = '=';
|
||||||
|
set[0].who = 'a';
|
||||||
|
set[1].cmd = '\0';
|
||||||
|
return (void *)set;
|
||||||
|
}
|
||||||
|
|
||||||
|
set = calloc(SET_LEN, sizeof(BITCMD));
|
||||||
|
if (set == NULL)
|
||||||
|
return NULL;
|
||||||
|
saveset = set;
|
||||||
|
nsets = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
/* Parse "who" part */
|
||||||
|
who = 'a';
|
||||||
|
mask = S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX;
|
||||||
|
for (;; ++p) {
|
||||||
|
switch (*p) {
|
||||||
|
case 'u':
|
||||||
|
who = 'u';
|
||||||
|
mask = S_IRWXU | S_ISUID;
|
||||||
|
continue;
|
||||||
|
case 'g':
|
||||||
|
who = 'g';
|
||||||
|
mask = S_IRWXG | S_ISGID;
|
||||||
|
continue;
|
||||||
|
case 'o':
|
||||||
|
who = 'o';
|
||||||
|
mask = S_IRWXO | S_ISVTX;
|
||||||
|
continue;
|
||||||
|
case 'a':
|
||||||
|
who = 'a';
|
||||||
|
mask = S_IRWXU | S_IRWXG | S_IRWXO |
|
||||||
|
S_ISUID | S_ISGID | S_ISVTX;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse operator */
|
||||||
|
op = *p++;
|
||||||
|
if (op != '+' && op != '-' && op != '=') {
|
||||||
|
free(saveset);
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse permissions */
|
||||||
|
bits = 0;
|
||||||
|
for (;; ++p) {
|
||||||
|
switch (*p) {
|
||||||
|
case 'r':
|
||||||
|
if (who == 'u' || who == 'a')
|
||||||
|
bits |= S_IRUSR;
|
||||||
|
if (who == 'g' || who == 'a')
|
||||||
|
bits |= S_IRGRP;
|
||||||
|
if (who == 'o' || who == 'a')
|
||||||
|
bits |= S_IROTH;
|
||||||
|
continue;
|
||||||
|
case 'w':
|
||||||
|
if (who == 'u' || who == 'a')
|
||||||
|
bits |= S_IWUSR;
|
||||||
|
if (who == 'g' || who == 'a')
|
||||||
|
bits |= S_IWGRP;
|
||||||
|
if (who == 'o' || who == 'a')
|
||||||
|
bits |= S_IWOTH;
|
||||||
|
continue;
|
||||||
|
case 'x':
|
||||||
|
if (who == 'u' || who == 'a')
|
||||||
|
bits |= S_IXUSR;
|
||||||
|
if (who == 'g' || who == 'a')
|
||||||
|
bits |= S_IXGRP;
|
||||||
|
if (who == 'o' || who == 'a')
|
||||||
|
bits |= S_IXOTH;
|
||||||
|
continue;
|
||||||
|
case 'X':
|
||||||
|
/* Execute if directory or any exec bit set */
|
||||||
|
bits |= S_IXUSR | S_IXGRP | S_IXOTH;
|
||||||
|
continue;
|
||||||
|
case 's':
|
||||||
|
if (who == 'u' || who == 'a')
|
||||||
|
bits |= S_ISUID;
|
||||||
|
if (who == 'g' || who == 'a')
|
||||||
|
bits |= S_ISGID;
|
||||||
|
continue;
|
||||||
|
case 't':
|
||||||
|
bits |= S_ISVTX;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsets >= SET_LEN - 1) {
|
||||||
|
free(saveset);
|
||||||
|
errno = ERANGE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
set->bits = bits & mask;
|
||||||
|
set->cmd = op;
|
||||||
|
set->who = who;
|
||||||
|
set++;
|
||||||
|
nsets++;
|
||||||
|
|
||||||
|
if (*p == ',') {
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*p == '\0')
|
||||||
|
break;
|
||||||
|
free(saveset);
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
set->cmd = '\0';
|
||||||
|
return (void *)saveset;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t
|
||||||
|
getmode(const void *bbox, mode_t omode)
|
||||||
|
{
|
||||||
|
const BITCMD *set = (const BITCMD *)bbox;
|
||||||
|
mode_t newmode = omode;
|
||||||
|
|
||||||
|
for (; set->cmd; set++) {
|
||||||
|
switch (set->cmd) {
|
||||||
|
case '+':
|
||||||
|
newmode |= set->bits;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
newmode &= ~set->bits;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (set->who == 'a') {
|
||||||
|
newmode = set->bits;
|
||||||
|
} else {
|
||||||
|
mode_t mask;
|
||||||
|
switch (set->who) {
|
||||||
|
case 'u': mask = S_IRWXU | S_ISUID; break;
|
||||||
|
case 'g': mask = S_IRWXG | S_ISGID; break;
|
||||||
|
case 'o': mask = S_IRWXO | S_ISVTX; break;
|
||||||
|
default: mask = ~(mode_t)0; break;
|
||||||
|
}
|
||||||
|
newmode = (newmode & ~mask) | (set->bits & mask);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newmode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* strlcpy.c / strlcat.c - OpenBSD-compatible bounded string operations.
|
||||||
|
*
|
||||||
|
* Origin: Todd C. Miller's canonical implementation, placed in the
|
||||||
|
* public domain. These are the reference implementations used by
|
||||||
|
* OpenBSD, FreeBSD, and many other projects.
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* strlcpy(dst, src, dsize)
|
||||||
|
* Copy string src to buffer dst of size dsize. At most dsize-1 chars
|
||||||
|
* will be copied; dst is always NUL-terminated (unless dsize == 0).
|
||||||
|
* Returns strlen(src) -- if retval >= dsize, truncation occurred.
|
||||||
|
*
|
||||||
|
* strlcat(dst, src, dsize)
|
||||||
|
* Appends string src to buffer dst of size dsize (NUL-terminated).
|
||||||
|
* Appends at most dsize - strlen(dst) - 1 chars, NUL-terminating
|
||||||
|
* the result. Returns strlen(src) + MIN(dsize, strlen(dst)) -- if
|
||||||
|
* retval >= dsize, truncation occurred.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#ifdef VX_NEED_STRLCPY
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
size_t
|
||||||
|
strlcpy(char *dst, const char *src, size_t dsize)
|
||||||
|
{
|
||||||
|
const char *osrc = src;
|
||||||
|
size_t nleft = dsize;
|
||||||
|
|
||||||
|
/* Copy as many bytes as will fit. */
|
||||||
|
if (nleft != 0) {
|
||||||
|
while (--nleft != 0) {
|
||||||
|
if ((*dst++ = *src++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not enough room -- NUL-terminate and traverse rest of src. */
|
||||||
|
if (nleft == 0) {
|
||||||
|
if (dsize != 0)
|
||||||
|
*dst = '\0'; /* NUL-terminate dst */
|
||||||
|
while (*src++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size_t)(src - osrc - 1); /* count excludes NUL */
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
strlcat(char *dst, const char *src, size_t dsize)
|
||||||
|
{
|
||||||
|
const char *odst = dst;
|
||||||
|
const char *osrc = src;
|
||||||
|
size_t n = dsize;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
/* Find the end of dst and adjust bytes left but don't go past end. */
|
||||||
|
while (n-- != 0 && *dst != '\0')
|
||||||
|
dst++;
|
||||||
|
dlen = (size_t)(dst - odst);
|
||||||
|
n = dsize - dlen;
|
||||||
|
|
||||||
|
if (n-- == 0)
|
||||||
|
return dlen + strlen(src);
|
||||||
|
while (*src != '\0') {
|
||||||
|
if (n != 0) {
|
||||||
|
*dst++ = *src;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
|
||||||
|
return dlen + (size_t)(src - osrc); /* count excludes NUL */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VX_NEED_STRLCPY */
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* strmode - convert file mode to ls-style string.
|
||||||
|
*
|
||||||
|
* Derived from FreeBSD's lib/libc/string/strmode.c.
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
strmode(mode_t mode, char *p)
|
||||||
|
{
|
||||||
|
/* file type */
|
||||||
|
switch (mode & S_IFMT) {
|
||||||
|
case S_IFDIR: *p++ = 'd'; break;
|
||||||
|
case S_IFCHR: *p++ = 'c'; break;
|
||||||
|
case S_IFBLK: *p++ = 'b'; break;
|
||||||
|
case S_IFREG: *p++ = '-'; break;
|
||||||
|
case S_IFLNK: *p++ = 'l'; break;
|
||||||
|
case S_IFSOCK: *p++ = 's'; break;
|
||||||
|
#ifdef S_IFIFO
|
||||||
|
case S_IFIFO: *p++ = 'p'; break;
|
||||||
|
#endif
|
||||||
|
#ifdef S_IFWHT
|
||||||
|
case S_IFWHT: *p++ = 'w'; break;
|
||||||
|
#endif
|
||||||
|
default: *p++ = '?'; break;
|
||||||
|
}
|
||||||
|
/* owner */
|
||||||
|
*p++ = (mode & S_IRUSR) ? 'r' : '-';
|
||||||
|
*p++ = (mode & S_IWUSR) ? 'w' : '-';
|
||||||
|
if (mode & S_ISUID)
|
||||||
|
*p++ = (mode & S_IXUSR) ? 's' : 'S';
|
||||||
|
else
|
||||||
|
*p++ = (mode & S_IXUSR) ? 'x' : '-';
|
||||||
|
/* group */
|
||||||
|
*p++ = (mode & S_IRGRP) ? 'r' : '-';
|
||||||
|
*p++ = (mode & S_IWGRP) ? 'w' : '-';
|
||||||
|
if (mode & S_ISGID)
|
||||||
|
*p++ = (mode & S_IXGRP) ? 's' : 'S';
|
||||||
|
else
|
||||||
|
*p++ = (mode & S_IXGRP) ? 'x' : '-';
|
||||||
|
/* other */
|
||||||
|
*p++ = (mode & S_IROTH) ? 'r' : '-';
|
||||||
|
*p++ = (mode & S_IWOTH) ? 'w' : '-';
|
||||||
|
if (mode & S_ISVTX)
|
||||||
|
*p++ = (mode & S_IXOTH) ? 't' : 'T';
|
||||||
|
else
|
||||||
|
*p++ = (mode & S_IXOTH) ? 'x' : '-';
|
||||||
|
*p++ = ' ';
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* strtonum.c - OpenBSD strtonum() for Linux.
|
||||||
|
*
|
||||||
|
* strtonum(nptr, minval, maxval, errstrp)
|
||||||
|
* Converts the string nptr to a long long integer in [minval, maxval].
|
||||||
|
*
|
||||||
|
* On success, *errstrp is set to NULL and the converted value is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* On failure, 0 is returned and *errstrp points to one of:
|
||||||
|
* "invalid" -- the string is not a valid number
|
||||||
|
* "too small" -- the value is less than minval
|
||||||
|
* "too large" -- the value is greater than maxval
|
||||||
|
*
|
||||||
|
* Unlike strtol, strtonum rejects trailing garbage, leading
|
||||||
|
* whitespace past the first char, octal, and hex prefixes.
|
||||||
|
*
|
||||||
|
* Based on the OpenBSD reference implementation (ISC license).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef __OpenBSD__
|
||||||
|
|
||||||
|
long long
|
||||||
|
strtonum(const char *nptr, long long minval, long long maxval,
|
||||||
|
const char **errstrp)
|
||||||
|
{
|
||||||
|
long long ll;
|
||||||
|
char *ep;
|
||||||
|
int saved_errno = errno;
|
||||||
|
|
||||||
|
if (minval > maxval) {
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = "invalid";
|
||||||
|
errno = EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
ll = strtoll(nptr, &ep, 10);
|
||||||
|
if (nptr == ep || *ep != '\0') {
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = "invalid";
|
||||||
|
errno = EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((errno == ERANGE && (ll == LLONG_MIN || ll == LLONG_MAX)) ||
|
||||||
|
ll < minval || ll > maxval) {
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = (ll < minval) ? "too small" : "too large";
|
||||||
|
if (errno != ERANGE)
|
||||||
|
errno = ERANGE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = NULL;
|
||||||
|
errno = saved_errno;
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__OpenBSD__ */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* user_from_uid / group_from_gid - cached UID/GID to name lookups.
|
||||||
|
*
|
||||||
|
* BSD libc provides these; Linux does not.
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
user_from_uid(uid_t uid, int nouser)
|
||||||
|
{
|
||||||
|
static char buf[32];
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
pw = getpwuid(uid);
|
||||||
|
if (pw != NULL)
|
||||||
|
return pw->pw_name;
|
||||||
|
if (nouser)
|
||||||
|
return NULL;
|
||||||
|
(void)snprintf(buf, sizeof(buf), "%u", (unsigned)uid);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
group_from_gid(gid_t gid, int nogroup)
|
||||||
|
{
|
||||||
|
static char buf[32];
|
||||||
|
struct group *gr;
|
||||||
|
|
||||||
|
gr = getgrgid(gid);
|
||||||
|
if (gr != NULL)
|
||||||
|
return gr->gr_name;
|
||||||
|
if (nogroup)
|
||||||
|
return NULL;
|
||||||
|
(void)snprintf(buf, sizeof(buf), "%u", (unsigned)gid);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
+760
@@ -0,0 +1,760 @@
|
|||||||
|
.\" $OpenBSD: diff.1,v 1.47 2015/11/24 19:35:41 jmc Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1980, 1990, 1993
|
||||||
|
.\" The Regents of the University of California. All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
|
.\" modification, are permitted provided that the following conditions
|
||||||
|
.\" are met:
|
||||||
|
.\" 1. Redistributions of source code must retain the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer.
|
||||||
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer in the
|
||||||
|
.\" documentation and/or other materials provided with the distribution.
|
||||||
|
.\" 3. Neither the name of the University nor the names of its contributors
|
||||||
|
.\" may be used to endorse or promote products derived from this software
|
||||||
|
.\" without specific prior written permission.
|
||||||
|
.\"
|
||||||
|
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
.\" SUCH DAMAGE.
|
||||||
|
.\"
|
||||||
|
.\" @(#)diff.1 8.1 (Berkeley) 6/30/93
|
||||||
|
.\"
|
||||||
|
.Dd July 26, 2024
|
||||||
|
.Dt DIFF 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm diff
|
||||||
|
.Nd differential file and directory comparator
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbdipTtw
|
||||||
|
.Oo
|
||||||
|
.Fl c | e | f |
|
||||||
|
.Fl n | q | u | y
|
||||||
|
.Oc
|
||||||
|
.Op Fl -brief
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -changed-group-format Ar GFMT
|
||||||
|
.Op Fl -ed
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -forward-ed
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -ignore-space-change
|
||||||
|
.Op Fl -initial-tab
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -normal
|
||||||
|
.Op Fl -rcs
|
||||||
|
.Op Fl -show-c-function
|
||||||
|
.Op Fl -starting-file
|
||||||
|
.Op Fl -speed-large-files
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Op Fl -unified
|
||||||
|
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
|
||||||
|
.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
|
||||||
|
.Op Fl L Ar label | Fl -label Ar label
|
||||||
|
.Ar file1 file2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbdilpTtw
|
||||||
|
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
|
||||||
|
.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
|
||||||
|
.Op Fl L Ar label | Fl -label Ar label
|
||||||
|
.Op Fl -brief
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -changed-group-format Ar GFMT
|
||||||
|
.Op Fl -ed
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -forward-ed
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -ignore-space-change
|
||||||
|
.Op Fl -initial-tab
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -normal
|
||||||
|
.Op Fl -paginate
|
||||||
|
.Op Fl -rcs
|
||||||
|
.Op Fl -show-c-function
|
||||||
|
.Op Fl -speed-large-files
|
||||||
|
.Op Fl -starting-file
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Fl C Ar number | -context Ar number
|
||||||
|
.Ar file1 file2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbdiltw
|
||||||
|
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
|
||||||
|
.Op Fl -brief
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -changed-group-format Ar GFMT
|
||||||
|
.Op Fl -ed
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -forward-ed
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -ignore-space-change
|
||||||
|
.Op Fl -initial-tab
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -normal
|
||||||
|
.Op Fl -paginate
|
||||||
|
.Op Fl -rcs
|
||||||
|
.Op Fl -show-c-function
|
||||||
|
.Op Fl -speed-large-files
|
||||||
|
.Op Fl -starting-file
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Fl D Ar string | Fl -ifdef Ar string
|
||||||
|
.Ar file1 file2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbdilpTtw
|
||||||
|
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
|
||||||
|
.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
|
||||||
|
.Op Fl L Ar label | Fl -label Ar label
|
||||||
|
.Op Fl -brief
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -changed-group-format Ar GFMT
|
||||||
|
.Op Fl -ed
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -forward-ed
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -ignore-space-change
|
||||||
|
.Op Fl -initial-tab
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -normal
|
||||||
|
.Op Fl -paginate
|
||||||
|
.Op Fl -rcs
|
||||||
|
.Op Fl -show-c-function
|
||||||
|
.Op Fl -speed-large-files
|
||||||
|
.Op Fl -starting-file
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Fl U Ar number | Fl -unified Ar number
|
||||||
|
.Ar file1 file2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbdilNPprsTtw
|
||||||
|
.Oo
|
||||||
|
.Fl c | e | f |
|
||||||
|
.Fl n | q | u
|
||||||
|
.Oc
|
||||||
|
.Op Fl -brief
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -changed-group-format Ar GFMT
|
||||||
|
.Op Fl -context
|
||||||
|
.Op Fl -ed
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -forward-ed
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -ignore-space-change
|
||||||
|
.Op Fl -initial-tab
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -new-file
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -normal
|
||||||
|
.Op Fl -paginate
|
||||||
|
.Op Fl -rcs
|
||||||
|
.Op Fl -recursive
|
||||||
|
.Op Fl -report-identical-files
|
||||||
|
.Op Fl -show-c-function
|
||||||
|
.Op Fl -speed-large-files
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Op Fl -unidirectional-new-file
|
||||||
|
.Op Fl -unified
|
||||||
|
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
|
||||||
|
.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
|
||||||
|
.Bk -words
|
||||||
|
.Op Fl L Ar label | Fl -label Ar label
|
||||||
|
.Op Fl S Ar name | Fl -starting-file Ar name
|
||||||
|
.Op Fl X Ar file | Fl -exclude-from Ar file
|
||||||
|
.Op Fl x Ar pattern | Fl -exclude Ar pattern
|
||||||
|
.Ek
|
||||||
|
.Ar dir1 dir2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl aBbditwW
|
||||||
|
.Op Fl -color Ns = Ns Ar when
|
||||||
|
.Op Fl -expand-tabs
|
||||||
|
.Op Fl -ignore-all-space
|
||||||
|
.Op Fl -ignore-blank-lines
|
||||||
|
.Op Fl -ignore-case
|
||||||
|
.Op Fl -minimal
|
||||||
|
.Op Fl -no-dereference
|
||||||
|
.Op Fl -no-ignore-file-name-case
|
||||||
|
.Op Fl -strip-trailing-cr
|
||||||
|
.Op Fl -suppress-common-lines
|
||||||
|
.Op Fl -tabsize Ar number
|
||||||
|
.Op Fl -text
|
||||||
|
.Op Fl -width
|
||||||
|
.Fl y | Fl -side-by-side
|
||||||
|
.Ar file1 file2
|
||||||
|
.Nm diff
|
||||||
|
.Op Fl -help
|
||||||
|
.Op Fl -version
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility compares the contents of
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2
|
||||||
|
and writes to the standard output the list of changes necessary to
|
||||||
|
convert one file into the other.
|
||||||
|
No output is produced if the files are identical.
|
||||||
|
.Pp
|
||||||
|
Output options (mutually exclusive):
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl C Ar number Fl -context Ar number
|
||||||
|
Like
|
||||||
|
.Fl c
|
||||||
|
but produces a diff with
|
||||||
|
.Ar number
|
||||||
|
lines of context.
|
||||||
|
.It Fl c
|
||||||
|
Produces a diff with 3 lines of context.
|
||||||
|
With
|
||||||
|
.Fl c
|
||||||
|
the output format is modified slightly:
|
||||||
|
the output begins with identification of the files involved and
|
||||||
|
their creation dates and then each change is separated
|
||||||
|
by a line with fifteen
|
||||||
|
.Li * Ns 's .
|
||||||
|
The lines removed from
|
||||||
|
.Ar file1
|
||||||
|
are marked with
|
||||||
|
.Sq \&-\ \& ;
|
||||||
|
those added to
|
||||||
|
.Ar file2
|
||||||
|
are marked
|
||||||
|
.Sq +\ \& .
|
||||||
|
Lines which are changed from one file to the other are marked in
|
||||||
|
both files with
|
||||||
|
.Sq !\ \& .
|
||||||
|
Changes which lie within 3 lines of each other are grouped together on
|
||||||
|
output.
|
||||||
|
.It Fl D Ar string Fl -ifdef Ar string
|
||||||
|
Creates a merged version of
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2
|
||||||
|
on the standard output, with C preprocessor controls included so that
|
||||||
|
a compilation of the result without defining
|
||||||
|
.Ar string
|
||||||
|
is equivalent to compiling
|
||||||
|
.Ar file1 ,
|
||||||
|
while defining
|
||||||
|
.Ar string
|
||||||
|
will yield
|
||||||
|
.Ar file2 .
|
||||||
|
.It Fl e -ed
|
||||||
|
Produces output in a form suitable as input for the editor utility,
|
||||||
|
.Xr ed 1 ,
|
||||||
|
which can then be used to convert file1 into file2.
|
||||||
|
.Pp
|
||||||
|
Extra commands are added to the output when comparing directories with
|
||||||
|
.Fl e ,
|
||||||
|
so that the result is a
|
||||||
|
.Xr sh 1
|
||||||
|
script for converting text files which are common to the two directories
|
||||||
|
from their state in
|
||||||
|
.Ar dir1
|
||||||
|
to their state in
|
||||||
|
.Ar dir2 .
|
||||||
|
.It Fl f -forward-ed
|
||||||
|
Identical output to that of the
|
||||||
|
.Fl e
|
||||||
|
flag, but in reverse order.
|
||||||
|
It cannot be digested by
|
||||||
|
.Xr ed 1 .
|
||||||
|
.It Fl -help
|
||||||
|
This option prints a summary to stdout and exits with status 0.
|
||||||
|
.It Fl n
|
||||||
|
Produces a script similar to that of
|
||||||
|
.Fl e ,
|
||||||
|
but in the opposite order and with a count of changed lines on each
|
||||||
|
insert or delete command.
|
||||||
|
This is the form used by rcsdiff.
|
||||||
|
.It Fl q -brief
|
||||||
|
Just print a line when the files differ.
|
||||||
|
Does not output a list of changes.
|
||||||
|
.It Fl U Ar number Fl -unified Ar number
|
||||||
|
Like
|
||||||
|
.Fl u
|
||||||
|
but produces a diff with
|
||||||
|
.Ar number
|
||||||
|
lines of context.
|
||||||
|
.It Fl u
|
||||||
|
Produces a
|
||||||
|
.Em unified
|
||||||
|
diff with 3 lines of context.
|
||||||
|
A unified diff is similar to the context diff produced by the
|
||||||
|
.Fl c
|
||||||
|
option.
|
||||||
|
However, unlike with
|
||||||
|
.Fl c ,
|
||||||
|
all lines to be changed (added and/or removed) are present in
|
||||||
|
a single section.
|
||||||
|
.It Fl -version
|
||||||
|
This option prints a version string to stdout and exits with status 0.
|
||||||
|
.It Fl y Fl -side-by-side
|
||||||
|
Output in two columns with a marker between them.
|
||||||
|
The marker can be one
|
||||||
|
of the following:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width Ds -offset indent -compact
|
||||||
|
.It space
|
||||||
|
Corresponding lines are identical.
|
||||||
|
.It '|'
|
||||||
|
Corresponding lines are different.
|
||||||
|
.It '<'
|
||||||
|
Files differ and only the first file contains the line.
|
||||||
|
.It '>'
|
||||||
|
Files differ and only the second file contains the line.
|
||||||
|
.El
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Comparison options:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl a -text
|
||||||
|
Treat all files as ASCII text.
|
||||||
|
Normally
|
||||||
|
.Nm
|
||||||
|
will simply print
|
||||||
|
.Dq Binary files ... differ
|
||||||
|
if files contain binary characters.
|
||||||
|
Use of this option forces
|
||||||
|
.Nm
|
||||||
|
to produce a diff.
|
||||||
|
.It Fl B Fl -ignore-blank-lines
|
||||||
|
Causes chunks that include only blank lines to be ignored.
|
||||||
|
.It Fl b -ignore-space-change
|
||||||
|
Causes trailing blanks (spaces and tabs) to be ignored, and other
|
||||||
|
strings of blanks to compare equal.
|
||||||
|
.It Fl -color= Ns Oo Ar when Oc
|
||||||
|
Color the additions green, and removals red, or the value in the
|
||||||
|
.Ev DIFFCOLORS
|
||||||
|
environment variable.
|
||||||
|
The possible values of
|
||||||
|
.Ar when
|
||||||
|
are
|
||||||
|
.Dq Cm never ,
|
||||||
|
.Dq Cm always
|
||||||
|
and
|
||||||
|
.Dq Cm auto .
|
||||||
|
.Cm auto
|
||||||
|
will use color if the output is a tty and the
|
||||||
|
.Ev COLORTERM
|
||||||
|
environment variable is set to a non-empty string.
|
||||||
|
.It Fl d -minimal
|
||||||
|
Try very hard to produce a diff as small as possible.
|
||||||
|
This may consume a lot of processing power and memory when processing
|
||||||
|
large files with many changes.
|
||||||
|
.It Fl F Ar pattern, Fl -show-function-line Ar pattern
|
||||||
|
Like
|
||||||
|
.Fl p,
|
||||||
|
but display the last line that matches provided pattern.
|
||||||
|
.It Fl I Ar pattern Fl -ignore-matching-lines Ar pattern
|
||||||
|
Ignores changes, insertions, and deletions whose lines match the
|
||||||
|
extended regular expression
|
||||||
|
.Ar pattern .
|
||||||
|
Multiple
|
||||||
|
.Fl I
|
||||||
|
patterns may be specified.
|
||||||
|
All lines in the change must match some pattern for the change to be
|
||||||
|
ignored.
|
||||||
|
See
|
||||||
|
.Xr re_format 7
|
||||||
|
for more information on regular expression patterns.
|
||||||
|
.It Fl i -ignore-case
|
||||||
|
Ignores the case of letters.
|
||||||
|
E.g.,
|
||||||
|
.Dq A
|
||||||
|
will compare equal to
|
||||||
|
.Dq a .
|
||||||
|
.It Fl l -paginate
|
||||||
|
Pass the output through
|
||||||
|
.Xr pr 1
|
||||||
|
to paginate it.
|
||||||
|
.It Fl L Ar label Fl -label Ar label
|
||||||
|
Print
|
||||||
|
.Ar label
|
||||||
|
instead of the first (and second, if this option is specified twice)
|
||||||
|
file name and time in the context or unified diff header.
|
||||||
|
.It Fl p -show-c-function
|
||||||
|
With unified and context diffs, show with each change
|
||||||
|
the first 40 characters of the last line before the context beginning
|
||||||
|
with a letter, an underscore or a dollar sign.
|
||||||
|
For C and Objective-C source code following standard layout conventions, this
|
||||||
|
will show the prototype of the function the change applies to.
|
||||||
|
.It Fl T -initial-tab
|
||||||
|
Print a tab rather than a space before the rest of the line for the
|
||||||
|
normal, context or unified output formats.
|
||||||
|
This makes the alignment of tabs in the line consistent.
|
||||||
|
.It Fl t -expand-tabs
|
||||||
|
Will expand tabs in output lines.
|
||||||
|
Normal or
|
||||||
|
.Fl c
|
||||||
|
output adds character(s) to the front of each line which may screw up
|
||||||
|
the indentation of the original source lines and make the output listing
|
||||||
|
difficult to interpret.
|
||||||
|
This option will preserve the original source's indentation.
|
||||||
|
.It Fl w -ignore-all-space
|
||||||
|
Is similar to
|
||||||
|
.Fl b -ignore-space-change
|
||||||
|
but causes whitespace (blanks and tabs) to be totally ignored.
|
||||||
|
E.g.,
|
||||||
|
.Dq if (\ \&a == b \&)
|
||||||
|
will compare equal to
|
||||||
|
.Dq if(a==b) .
|
||||||
|
.It Fl W Ar number Fl -width Ar number
|
||||||
|
Output at most
|
||||||
|
.Ar number
|
||||||
|
columns when using side by side format.
|
||||||
|
The default value is 130.
|
||||||
|
Note that unless
|
||||||
|
.It Fl t
|
||||||
|
was specified,
|
||||||
|
.Nm
|
||||||
|
will always align the second column to a tab stop, so values of
|
||||||
|
.Fl -width
|
||||||
|
smaller than approximately five times the value of
|
||||||
|
.Fl -tabsize
|
||||||
|
may yield surprising results.
|
||||||
|
.It Fl -changed-group-format Ar GFMT
|
||||||
|
Format input groups in the provided
|
||||||
|
.Pp
|
||||||
|
the format is a string with special keywords:
|
||||||
|
.Bl -tag -width %<
|
||||||
|
.It %<
|
||||||
|
lines from FILE1
|
||||||
|
.It %<
|
||||||
|
lines from FILE2
|
||||||
|
.El
|
||||||
|
.It Fl -ignore-file-name-case
|
||||||
|
ignore case when comparing file names
|
||||||
|
.It Fl -no-dereference
|
||||||
|
do not follow symbolic links
|
||||||
|
.It Fl -no-ignore-file-name-case
|
||||||
|
do not ignore case wen comparing file names (default)
|
||||||
|
.It Fl -normal
|
||||||
|
default diff output
|
||||||
|
.It Fl -speed-large-files
|
||||||
|
stub option for compatibility with GNU diff
|
||||||
|
.It Fl -strip-trailing-cr
|
||||||
|
strip carriage return on input files
|
||||||
|
.It Fl -suppress-common-lines
|
||||||
|
Do not output common lines when using the side by side format
|
||||||
|
.It Fl -tabsize Ar number
|
||||||
|
Number of spaces representing a tab (default 8)
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Directory comparison options:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl N -new-file
|
||||||
|
If a file is found in only one directory, act as if it was found in the
|
||||||
|
other directory too but was of zero size.
|
||||||
|
.It Fl P -unidirectional-new-file
|
||||||
|
If a file is found only in
|
||||||
|
.Ar dir2 ,
|
||||||
|
act as if it was found in
|
||||||
|
.Ar dir1
|
||||||
|
too but was of zero size.
|
||||||
|
.It Fl r -recursive
|
||||||
|
Causes application of
|
||||||
|
.Nm
|
||||||
|
recursively to common subdirectories encountered.
|
||||||
|
.It Fl S Ar name Fl -starting-file Ar name
|
||||||
|
Re-starts a directory
|
||||||
|
.Nm
|
||||||
|
in the middle, beginning with file
|
||||||
|
.Ar name .
|
||||||
|
.It Fl s -report-identical-files
|
||||||
|
Causes
|
||||||
|
.Nm
|
||||||
|
to report files which are the same, which are otherwise not mentioned.
|
||||||
|
.It Fl X Ar file Fl -exclude-from Ar file
|
||||||
|
Exclude files and subdirectories from comparison whose basenames match
|
||||||
|
lines in
|
||||||
|
.Ar file .
|
||||||
|
Multiple
|
||||||
|
.Fl X
|
||||||
|
options may be specified.
|
||||||
|
.It Fl x Ar pattern Fl -exclude Ar pattern
|
||||||
|
Exclude files and subdirectories from comparison whose basenames match
|
||||||
|
.Ar pattern .
|
||||||
|
Patterns are matched using shell-style globbing via
|
||||||
|
.Xr fnmatch 3 .
|
||||||
|
Multiple
|
||||||
|
.Fl x
|
||||||
|
options may be specified.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
If both arguments are directories,
|
||||||
|
.Nm
|
||||||
|
sorts the contents of the directories by name, and then runs the
|
||||||
|
regular file
|
||||||
|
.Nm
|
||||||
|
algorithm, producing a change list,
|
||||||
|
on text files which are different.
|
||||||
|
Binary files which differ,
|
||||||
|
common subdirectories, and files which appear in only one directory
|
||||||
|
are described as such.
|
||||||
|
In directory mode only regular files and directories are compared.
|
||||||
|
If a non-regular file such as a device special file or FIFO is encountered,
|
||||||
|
a diagnostic message is printed.
|
||||||
|
.Pp
|
||||||
|
If only one of
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2
|
||||||
|
is a directory,
|
||||||
|
.Nm
|
||||||
|
is applied to the non-directory file and the file contained in
|
||||||
|
the directory file with a filename that is the same as the
|
||||||
|
last component of the non-directory file.
|
||||||
|
.Pp
|
||||||
|
If either
|
||||||
|
.Ar file1
|
||||||
|
or
|
||||||
|
.Ar file2
|
||||||
|
is
|
||||||
|
.Sq - ,
|
||||||
|
the standard input is
|
||||||
|
used in its place.
|
||||||
|
.Ss Output Style
|
||||||
|
The default (without
|
||||||
|
.Fl e ,
|
||||||
|
.Fl c ,
|
||||||
|
or
|
||||||
|
.Fl n -rcs
|
||||||
|
.\" -C
|
||||||
|
options)
|
||||||
|
output contains lines of these forms, where
|
||||||
|
.Va XX , YY , ZZ , QQ
|
||||||
|
are line numbers respective of file order.
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width "XX,YYcZZ,QQ" -compact
|
||||||
|
.It Li XX Ns Ic a Ns Li YY
|
||||||
|
At (the end of) line
|
||||||
|
.Va XX
|
||||||
|
of
|
||||||
|
.Ar file1 ,
|
||||||
|
append the contents
|
||||||
|
of line
|
||||||
|
.Va YY
|
||||||
|
of
|
||||||
|
.Ar file2
|
||||||
|
to make them equal.
|
||||||
|
.It Li XX Ns Ic a Ns Li YY,ZZ
|
||||||
|
Same as above, but append the range of lines,
|
||||||
|
.Va YY
|
||||||
|
through
|
||||||
|
.Va ZZ
|
||||||
|
of
|
||||||
|
.Ar file2
|
||||||
|
to line
|
||||||
|
.Va XX
|
||||||
|
of file1.
|
||||||
|
.It Li XX Ns Ic d Ns Li YY
|
||||||
|
At line
|
||||||
|
.Va XX
|
||||||
|
delete
|
||||||
|
the line.
|
||||||
|
The value
|
||||||
|
.Va YY
|
||||||
|
tells to which line the change would bring
|
||||||
|
.Ar file1
|
||||||
|
in line with
|
||||||
|
.Ar file2 .
|
||||||
|
.It Li XX,YY Ns Ic d Ns Li ZZ
|
||||||
|
Delete the range of lines
|
||||||
|
.Va XX
|
||||||
|
through
|
||||||
|
.Va YY
|
||||||
|
in
|
||||||
|
.Ar file1 .
|
||||||
|
.It Li XX Ns Ic c Ns Li YY
|
||||||
|
Change the line
|
||||||
|
.Va XX
|
||||||
|
in
|
||||||
|
.Ar file1
|
||||||
|
to the line
|
||||||
|
.Va YY
|
||||||
|
in
|
||||||
|
.Ar file2 .
|
||||||
|
.It Li XX,YY Ns Ic c Ns Li ZZ
|
||||||
|
Replace the range of specified lines with the line
|
||||||
|
.Va ZZ .
|
||||||
|
.It Li XX,YY Ns Ic c Ns Li ZZ,QQ
|
||||||
|
Replace the range
|
||||||
|
.Va XX , Ns Va YY
|
||||||
|
from
|
||||||
|
.Ar file1
|
||||||
|
with the range
|
||||||
|
.Va ZZ , Ns Va QQ
|
||||||
|
from
|
||||||
|
.Ar file2 .
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
These lines resemble
|
||||||
|
.Xr ed 1
|
||||||
|
subcommands to convert
|
||||||
|
.Ar file1
|
||||||
|
into
|
||||||
|
.Ar file2 .
|
||||||
|
The line numbers before the action letters pertain to
|
||||||
|
.Ar file1 ;
|
||||||
|
those after pertain to
|
||||||
|
.Ar file2 .
|
||||||
|
Thus, by exchanging
|
||||||
|
.Ic a
|
||||||
|
for
|
||||||
|
.Ic d
|
||||||
|
and reading the line in reverse order, one can also
|
||||||
|
determine how to convert
|
||||||
|
.Ar file2
|
||||||
|
into
|
||||||
|
.Ar file1 .
|
||||||
|
As in
|
||||||
|
.Xr ed 1 ,
|
||||||
|
identical
|
||||||
|
pairs (where num1 = num2) are abbreviated as a single
|
||||||
|
number.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width DIFFCOLORS
|
||||||
|
.It Ev DIFFCOLORS
|
||||||
|
The value of this variable is the form
|
||||||
|
.Ar add Ns : Ns Ar rm ,
|
||||||
|
where
|
||||||
|
.Ar add
|
||||||
|
is the ASCII escape sequence for additions and
|
||||||
|
.Ar rm
|
||||||
|
is the ASCII escape sequence for deletions.
|
||||||
|
If this is unset,
|
||||||
|
.Nm
|
||||||
|
uses green for additions and red for removals.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /tmp/diff.XXXXXXXX -compact
|
||||||
|
.It Pa /tmp/diff.XXXXXXXX
|
||||||
|
Temporary file used when comparing a device or the standard input.
|
||||||
|
Note that the temporary file is unlinked as soon as it is created
|
||||||
|
so it will not show up in a directory listing.
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility exits with one of the following values:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width Ds -offset indent -compact
|
||||||
|
.It 0
|
||||||
|
No differences were found.
|
||||||
|
.It 1
|
||||||
|
Differences were found.
|
||||||
|
.It >1
|
||||||
|
An error occurred.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Fl -help
|
||||||
|
and
|
||||||
|
.Fl -version
|
||||||
|
options exit with a status of 0.
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Compare
|
||||||
|
.Pa old_dir
|
||||||
|
and
|
||||||
|
.Pa new_dir
|
||||||
|
recursively generating an unified diff and treating files found only in one
|
||||||
|
of those directories as new files:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ diff -ruN /path/to/old_dir /path/to/new_dir
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Same as above but excluding files matching the expressions
|
||||||
|
.Dq *.h
|
||||||
|
and
|
||||||
|
.Dq *.c :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ diff -ruN -x '*.h' -x '*.c' /path/to/old_dir /path/to/new_dir
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Show a single line indicating if the files differ:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ diff -q /boot/loader.conf /boot/defaults/loader.conf
|
||||||
|
Files /boot/loader.conf and /boot/defaults/loader.conf differ
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Assuming a file named
|
||||||
|
.Pa example.txt
|
||||||
|
with the following contents:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
FreeBSD is an operating system
|
||||||
|
Linux is a kernel
|
||||||
|
OpenBSD is an operating system
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Compare stdin with
|
||||||
|
.Pa example.txt
|
||||||
|
excluding from the comparison those lines containing either
|
||||||
|
.Qq Linux
|
||||||
|
or
|
||||||
|
.Qq Open :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
$ echo "FreeBSD is an operating system" | diff -q -I 'Linux|Open' example.txt -
|
||||||
|
.Ed
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr cmp 1 ,
|
||||||
|
.Xr comm 1 ,
|
||||||
|
.Xr diff3 1 ,
|
||||||
|
.Xr ed 1 ,
|
||||||
|
.Xr patch 1 ,
|
||||||
|
.Xr pr 1 ,
|
||||||
|
.Xr sdiff 1
|
||||||
|
.Rs
|
||||||
|
.%A James W. Hunt
|
||||||
|
.%A M. Douglas McIlroy
|
||||||
|
.%T "An Algorithm for Differential File Comparison"
|
||||||
|
.%J Computing Science Technical Report
|
||||||
|
.%Q Bell Laboratories 41
|
||||||
|
.%D June 1976
|
||||||
|
.Re
|
||||||
|
.Sh STANDARDS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility is compliant with the
|
||||||
|
.St -p1003.1-2008
|
||||||
|
specification.
|
||||||
|
.Pp
|
||||||
|
The flags
|
||||||
|
.Op Fl aDdIiLlNnPpqSsTtwXxy
|
||||||
|
are extensions to that specification.
|
||||||
|
.Sh HISTORY
|
||||||
|
A
|
||||||
|
.Nm
|
||||||
|
command appeared in
|
||||||
|
.At v6 .
|
||||||
+651
@@ -0,0 +1,651 @@
|
|||||||
|
/* $OpenBSD: diff.c,v 1.67 2019/06/28 13:35:00 deraadt Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* Sponsored in part by the Defense Advanced Research Projects
|
||||||
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||||
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
|
#include "xmalloc.h"
|
||||||
|
|
||||||
|
static const char diff_version[] = "FreeBSD diff 20220309";
|
||||||
|
bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
|
||||||
|
bool ignore_file_case, suppress_common, color, noderef;
|
||||||
|
static bool help = false;
|
||||||
|
int diff_format, diff_context, status;
|
||||||
|
int tabsize = 8, width = 130;
|
||||||
|
static int colorflag = COLORFLAG_NEVER;
|
||||||
|
char *start, *ifdefname, *diffargs, *label[2];
|
||||||
|
char *ignore_pats, *most_recent_pat;
|
||||||
|
char *group_format = NULL;
|
||||||
|
const char *add_code, *del_code;
|
||||||
|
struct stat stb1, stb2;
|
||||||
|
struct excludes *excludes_list;
|
||||||
|
regex_t ignore_re, most_recent_re;
|
||||||
|
|
||||||
|
#define OPTIONS "0123456789aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
|
||||||
|
enum {
|
||||||
|
OPT_TSIZE = CHAR_MAX + 1,
|
||||||
|
OPT_STRIPCR,
|
||||||
|
OPT_IGN_FN_CASE,
|
||||||
|
OPT_NO_IGN_FN_CASE,
|
||||||
|
OPT_NORMAL,
|
||||||
|
OPT_HELP,
|
||||||
|
OPT_HORIZON_LINES,
|
||||||
|
OPT_CHANGED_GROUP_FORMAT,
|
||||||
|
OPT_SUPPRESS_COMMON,
|
||||||
|
OPT_COLOR,
|
||||||
|
OPT_NO_DEREFERENCE,
|
||||||
|
OPT_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct option longopts[] = {
|
||||||
|
{ "text", no_argument, 0, 'a' },
|
||||||
|
{ "ignore-space-change", no_argument, 0, 'b' },
|
||||||
|
{ "context", optional_argument, 0, 'C' },
|
||||||
|
{ "ifdef", required_argument, 0, 'D' },
|
||||||
|
{ "minimal", no_argument, 0, 'd' },
|
||||||
|
{ "ed", no_argument, 0, 'e' },
|
||||||
|
{ "forward-ed", no_argument, 0, 'f' },
|
||||||
|
{ "show-function-line", required_argument, 0, 'F' },
|
||||||
|
{ "speed-large-files", no_argument, NULL, 'H' },
|
||||||
|
{ "ignore-blank-lines", no_argument, 0, 'B' },
|
||||||
|
{ "ignore-matching-lines", required_argument, 0, 'I' },
|
||||||
|
{ "ignore-case", no_argument, 0, 'i' },
|
||||||
|
{ "paginate", no_argument, NULL, 'l' },
|
||||||
|
{ "label", required_argument, 0, 'L' },
|
||||||
|
{ "new-file", no_argument, 0, 'N' },
|
||||||
|
{ "rcs", no_argument, 0, 'n' },
|
||||||
|
{ "unidirectional-new-file", no_argument, 0, 'P' },
|
||||||
|
{ "show-c-function", no_argument, 0, 'p' },
|
||||||
|
{ "brief", no_argument, 0, 'q' },
|
||||||
|
{ "recursive", no_argument, 0, 'r' },
|
||||||
|
{ "report-identical-files", no_argument, 0, 's' },
|
||||||
|
{ "starting-file", required_argument, 0, 'S' },
|
||||||
|
{ "expand-tabs", no_argument, 0, 't' },
|
||||||
|
{ "initial-tab", no_argument, 0, 'T' },
|
||||||
|
{ "unified", optional_argument, 0, 'U' },
|
||||||
|
{ "ignore-all-space", no_argument, 0, 'w' },
|
||||||
|
{ "width", required_argument, 0, 'W' },
|
||||||
|
{ "exclude", required_argument, 0, 'x' },
|
||||||
|
{ "exclude-from", required_argument, 0, 'X' },
|
||||||
|
{ "side-by-side", no_argument, NULL, 'y' },
|
||||||
|
{ "ignore-file-name-case", no_argument, NULL, OPT_IGN_FN_CASE },
|
||||||
|
{ "help", no_argument, NULL, OPT_HELP},
|
||||||
|
{ "horizon-lines", required_argument, NULL, OPT_HORIZON_LINES },
|
||||||
|
{ "no-dereference", no_argument, NULL, OPT_NO_DEREFERENCE},
|
||||||
|
{ "no-ignore-file-name-case", no_argument, NULL, OPT_NO_IGN_FN_CASE },
|
||||||
|
{ "normal", no_argument, NULL, OPT_NORMAL },
|
||||||
|
{ "strip-trailing-cr", no_argument, NULL, OPT_STRIPCR },
|
||||||
|
{ "tabsize", required_argument, NULL, OPT_TSIZE },
|
||||||
|
{ "changed-group-format", required_argument, NULL, OPT_CHANGED_GROUP_FORMAT},
|
||||||
|
{ "suppress-common-lines", no_argument, NULL, OPT_SUPPRESS_COMMON },
|
||||||
|
{ "color", optional_argument, NULL, OPT_COLOR },
|
||||||
|
{ "version", no_argument, NULL, OPT_VERSION},
|
||||||
|
{ NULL, 0, 0, '\0'}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void checked_regcomp(char const *, regex_t *);
|
||||||
|
static void usage(void) __dead2;
|
||||||
|
static void conflicting_format(void) __dead2;
|
||||||
|
static void push_excludes(char *);
|
||||||
|
static void push_ignore_pats(char *);
|
||||||
|
static void read_excludes_file(char *file);
|
||||||
|
static void set_argstr(char **, char **);
|
||||||
|
static char *splice(char *, char *);
|
||||||
|
static bool do_color(void);
|
||||||
|
|
||||||
|
int
|
||||||
|
diff_main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *errstr = NULL;
|
||||||
|
char *ep, **oargv;
|
||||||
|
long l;
|
||||||
|
int ch, dflags, lastch, gotstdin, prevoptind, newarg;
|
||||||
|
|
||||||
|
oargv = argv;
|
||||||
|
gotstdin = 0;
|
||||||
|
dflags = 0;
|
||||||
|
lastch = '\0';
|
||||||
|
prevoptind = 1;
|
||||||
|
newarg = 1;
|
||||||
|
diff_context = 3;
|
||||||
|
diff_format = D_UNSET;
|
||||||
|
#define FORMAT_MISMATCHED(type) \
|
||||||
|
(diff_format != D_UNSET && diff_format != (type))
|
||||||
|
while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
if (newarg)
|
||||||
|
usage(); /* disallow -[0-9]+ */
|
||||||
|
else if (lastch == 'c' || lastch == 'u')
|
||||||
|
diff_context = 0;
|
||||||
|
else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
|
||||||
|
usage();
|
||||||
|
diff_context = (diff_context * 10) + (ch - '0');
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
dflags |= D_FORCEASCII;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
dflags |= D_FOLDBLANKS;
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
case 'c':
|
||||||
|
if (FORMAT_MISMATCHED(D_CONTEXT))
|
||||||
|
conflicting_format();
|
||||||
|
cflag = true;
|
||||||
|
diff_format = D_CONTEXT;
|
||||||
|
if (optarg != NULL) {
|
||||||
|
l = strtol(optarg, &ep, 10);
|
||||||
|
if (*ep != '\0' || l < 0 || l >= INT_MAX)
|
||||||
|
usage();
|
||||||
|
diff_context = (int)l;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
dflags |= D_MINIMAL;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (FORMAT_MISMATCHED(D_IFDEF))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_IFDEF;
|
||||||
|
ifdefname = optarg;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
if (FORMAT_MISMATCHED(D_EDIT))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_EDIT;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (FORMAT_MISMATCHED(D_REVERSE))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_REVERSE;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
/* ignore but needed for compatibility with GNU diff */
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
/* silently ignore for backwards compatibility */
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
dflags |= D_SKIPBLANKLINES;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
if (dflags & D_PROTOTYPE)
|
||||||
|
conflicting_format();
|
||||||
|
dflags |= D_MATCHLAST;
|
||||||
|
most_recent_pat = xstrdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
push_ignore_pats(optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
dflags |= D_IGNORECASE;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
if (label[0] == NULL)
|
||||||
|
label[0] = optarg;
|
||||||
|
else if (label[1] == NULL)
|
||||||
|
label[1] = optarg;
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
lflag = true;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
Nflag = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (FORMAT_MISMATCHED(D_NREVERSE))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_NREVERSE;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (dflags & D_MATCHLAST)
|
||||||
|
conflicting_format();
|
||||||
|
dflags |= D_PROTOTYPE;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
Pflag = true;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
rflag = true;
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
if (FORMAT_MISMATCHED(D_BRIEF))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_BRIEF;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
start = optarg;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
sflag = true;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
Tflag = true;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
dflags |= D_EXPANDTABS;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
case 'u':
|
||||||
|
if (FORMAT_MISMATCHED(D_UNIFIED))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_UNIFIED;
|
||||||
|
if (optarg != NULL) {
|
||||||
|
l = strtol(optarg, &ep, 10);
|
||||||
|
if (*ep != '\0' || l < 0 || l >= INT_MAX)
|
||||||
|
usage();
|
||||||
|
diff_context = (int)l;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
dflags |= D_IGNOREBLANKS;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
width = (int) strtonum(optarg, 1, INT_MAX, &errstr);
|
||||||
|
if (errstr)
|
||||||
|
errx(1, "width is %s: %s", errstr, optarg);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
read_excludes_file(optarg);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
push_excludes(optarg);
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_SIDEBYSIDE;
|
||||||
|
break;
|
||||||
|
case OPT_CHANGED_GROUP_FORMAT:
|
||||||
|
if (FORMAT_MISMATCHED(D_GFORMAT))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_GFORMAT;
|
||||||
|
group_format = optarg;
|
||||||
|
break;
|
||||||
|
case OPT_HELP:
|
||||||
|
help = true;
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
case OPT_HORIZON_LINES:
|
||||||
|
break; /* XXX TODO for compatibility with GNU diff3 */
|
||||||
|
case OPT_IGN_FN_CASE:
|
||||||
|
ignore_file_case = true;
|
||||||
|
break;
|
||||||
|
case OPT_NO_IGN_FN_CASE:
|
||||||
|
ignore_file_case = false;
|
||||||
|
break;
|
||||||
|
case OPT_NORMAL:
|
||||||
|
if (FORMAT_MISMATCHED(D_NORMAL))
|
||||||
|
conflicting_format();
|
||||||
|
diff_format = D_NORMAL;
|
||||||
|
break;
|
||||||
|
case OPT_TSIZE:
|
||||||
|
tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr);
|
||||||
|
if (errstr)
|
||||||
|
errx(1, "tabsize is %s: %s", errstr, optarg);
|
||||||
|
break;
|
||||||
|
case OPT_STRIPCR:
|
||||||
|
dflags |= D_STRIPCR;
|
||||||
|
break;
|
||||||
|
case OPT_SUPPRESS_COMMON:
|
||||||
|
suppress_common = 1;
|
||||||
|
break;
|
||||||
|
case OPT_COLOR:
|
||||||
|
if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
|
||||||
|
colorflag = COLORFLAG_AUTO;
|
||||||
|
else if (strncmp(optarg, "always", 6) == 0)
|
||||||
|
colorflag = COLORFLAG_ALWAYS;
|
||||||
|
else if (strncmp(optarg, "never", 5) == 0)
|
||||||
|
colorflag = COLORFLAG_NEVER;
|
||||||
|
else
|
||||||
|
errx(2, "unsupported --color value '%s' (must be always, auto, or never)",
|
||||||
|
optarg);
|
||||||
|
break;
|
||||||
|
case OPT_NO_DEREFERENCE:
|
||||||
|
noderef = true;
|
||||||
|
break;
|
||||||
|
case OPT_VERSION:
|
||||||
|
printf("%s\n", diff_version);
|
||||||
|
exit(0);
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastch = ch;
|
||||||
|
newarg = optind != prevoptind;
|
||||||
|
prevoptind = optind;
|
||||||
|
}
|
||||||
|
if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
|
||||||
|
diff_format = D_CONTEXT;
|
||||||
|
if (diff_format == D_UNSET)
|
||||||
|
diff_format = D_NORMAL;
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (do_color()) {
|
||||||
|
char *p;
|
||||||
|
const char *env;
|
||||||
|
|
||||||
|
color = true;
|
||||||
|
add_code = "32";
|
||||||
|
del_code = "31";
|
||||||
|
env = getenv("DIFFCOLORS");
|
||||||
|
if (env != NULL && *env != '\0' && (p = strdup(env))) {
|
||||||
|
add_code = p;
|
||||||
|
strsep(&p, ":");
|
||||||
|
if (p != NULL)
|
||||||
|
del_code = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
if (pledge("stdio rpath tmppath", NULL) == -1)
|
||||||
|
err(2, "pledge");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do sanity checks, fill in stb1 and stb2 and call the appropriate
|
||||||
|
* driver routine. Both drivers use the contents of stb1 and stb2.
|
||||||
|
*/
|
||||||
|
if (argc != 2)
|
||||||
|
usage();
|
||||||
|
checked_regcomp(ignore_pats, &ignore_re);
|
||||||
|
checked_regcomp(most_recent_pat, &most_recent_re);
|
||||||
|
if (strcmp(argv[0], "-") == 0) {
|
||||||
|
fstat(STDIN_FILENO, &stb1);
|
||||||
|
gotstdin = 1;
|
||||||
|
} else if (stat(argv[0], &stb1) != 0) {
|
||||||
|
if (!Nflag || errno != ENOENT)
|
||||||
|
err(2, "%s", argv[0]);
|
||||||
|
dflags |= D_EMPTY1;
|
||||||
|
memset(&stb1, 0, sizeof(struct stat));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "-") == 0) {
|
||||||
|
fstat(STDIN_FILENO, &stb2);
|
||||||
|
gotstdin = 1;
|
||||||
|
} else if (stat(argv[1], &stb2) != 0) {
|
||||||
|
if (!Nflag || errno != ENOENT)
|
||||||
|
err(2, "%s", argv[1]);
|
||||||
|
dflags |= D_EMPTY2;
|
||||||
|
memset(&stb2, 0, sizeof(stb2));
|
||||||
|
stb2.st_mode = stb1.st_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dflags & D_EMPTY1 && dflags & D_EMPTY2){
|
||||||
|
warn("%s", argv[0]);
|
||||||
|
warn("%s", argv[1]);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stb1.st_mode == 0)
|
||||||
|
stb1.st_mode = stb2.st_mode;
|
||||||
|
|
||||||
|
if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
|
||||||
|
errx(2, "can't compare - to a directory");
|
||||||
|
set_argstr(oargv, argv);
|
||||||
|
if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
|
||||||
|
if (diff_format == D_IFDEF)
|
||||||
|
errx(2, "-D option not supported with directories");
|
||||||
|
diffdir(argv[0], argv[1], dflags);
|
||||||
|
} else {
|
||||||
|
if (S_ISDIR(stb1.st_mode)) {
|
||||||
|
argv[0] = splice(argv[0], argv[1]);
|
||||||
|
if (stat(argv[0], &stb1) == -1)
|
||||||
|
err(2, "%s", argv[0]);
|
||||||
|
}
|
||||||
|
if (S_ISDIR(stb2.st_mode)) {
|
||||||
|
argv[1] = splice(argv[1], argv[0]);
|
||||||
|
if (stat(argv[1], &stb2) == -1)
|
||||||
|
err(2, "%s", argv[1]);
|
||||||
|
}
|
||||||
|
print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0],
|
||||||
|
argv[1], "");
|
||||||
|
}
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checked_regcomp(char const *pattern, regex_t *comp)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (pattern == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
|
||||||
|
if (error != 0) {
|
||||||
|
regerror(error, comp, buf, sizeof(buf));
|
||||||
|
if (*pattern != '\0')
|
||||||
|
errx(2, "%s: %s", pattern, buf);
|
||||||
|
else
|
||||||
|
errx(2, "%s", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_argstr(char **av, char **ave)
|
||||||
|
{
|
||||||
|
size_t argsize;
|
||||||
|
char **ap;
|
||||||
|
|
||||||
|
argsize = 4 + *ave - *av + 1;
|
||||||
|
diffargs = xmalloc(argsize);
|
||||||
|
strlcpy(diffargs, "diff", argsize);
|
||||||
|
for (ap = av + 1; ap < ave; ap++) {
|
||||||
|
if (strcmp(*ap, "--") != 0) {
|
||||||
|
strlcat(diffargs, " ", argsize);
|
||||||
|
strlcat(diffargs, *ap, argsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read in an excludes file and push each line.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
read_excludes_file(char *file)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *buf, *pattern;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (strcmp(file, "-") == 0)
|
||||||
|
fp = stdin;
|
||||||
|
else if ((fp = fopen(file, "r")) == NULL)
|
||||||
|
err(2, "%s", file);
|
||||||
|
while ((buf = fgetln(fp, &len)) != NULL) {
|
||||||
|
if (buf[len - 1] == '\n')
|
||||||
|
len--;
|
||||||
|
if ((pattern = strndup(buf, len)) == NULL)
|
||||||
|
err(2, "xstrndup");
|
||||||
|
push_excludes(pattern);
|
||||||
|
}
|
||||||
|
if (strcmp(file, "-") != 0)
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Push a pattern onto the excludes list.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
push_excludes(char *pattern)
|
||||||
|
{
|
||||||
|
struct excludes *entry;
|
||||||
|
|
||||||
|
entry = xmalloc(sizeof(*entry));
|
||||||
|
entry->pattern = pattern;
|
||||||
|
entry->next = excludes_list;
|
||||||
|
excludes_list = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
push_ignore_pats(char *pattern)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (ignore_pats == NULL)
|
||||||
|
ignore_pats = xstrdup(pattern);
|
||||||
|
else {
|
||||||
|
/* old + "|" + new + NUL */
|
||||||
|
len = strlen(ignore_pats) + strlen(pattern) + 2;
|
||||||
|
ignore_pats = xreallocarray(ignore_pats, 1, len);
|
||||||
|
strlcat(ignore_pats, "|", len);
|
||||||
|
strlcat(ignore_pats, pattern, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_status(int val, char *path1, char *path2, const char *entry)
|
||||||
|
{
|
||||||
|
if (label[0] != NULL)
|
||||||
|
path1 = label[0];
|
||||||
|
if (label[1] != NULL)
|
||||||
|
path2 = label[1];
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case D_BINARY:
|
||||||
|
printf("Binary files %s%s and %s%s differ\n",
|
||||||
|
path1, entry, path2, entry);
|
||||||
|
break;
|
||||||
|
case D_DIFFER:
|
||||||
|
if (diff_format == D_BRIEF)
|
||||||
|
printf("Files %s%s and %s%s differ\n",
|
||||||
|
path1, entry, path2, entry);
|
||||||
|
break;
|
||||||
|
case D_SAME:
|
||||||
|
if (sflag)
|
||||||
|
printf("Files %s%s and %s%s are identical\n",
|
||||||
|
path1, entry, path2, entry);
|
||||||
|
break;
|
||||||
|
case D_MISMATCH1:
|
||||||
|
printf("File %s%s is a directory while file %s%s is a regular file\n",
|
||||||
|
path1, entry, path2, entry);
|
||||||
|
break;
|
||||||
|
case D_MISMATCH2:
|
||||||
|
printf("File %s%s is a regular file while file %s%s is a directory\n",
|
||||||
|
path1, entry, path2, entry);
|
||||||
|
break;
|
||||||
|
case D_SKIPPED1:
|
||||||
|
printf("File %s%s is not a regular file or directory and was skipped\n",
|
||||||
|
path1, entry);
|
||||||
|
break;
|
||||||
|
case D_SKIPPED2:
|
||||||
|
printf("File %s%s is not a regular file or directory and was skipped\n",
|
||||||
|
path2, entry);
|
||||||
|
break;
|
||||||
|
case D_ERROR:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
(void)fprintf(help ? stdout : stderr,
|
||||||
|
"usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
|
||||||
|
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
|
||||||
|
" [-I pattern] [-F pattern] [-L label] file1 file2\n"
|
||||||
|
" diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
|
||||||
|
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
|
||||||
|
" [-F pattern] -C number file1 file2\n"
|
||||||
|
" diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
|
||||||
|
" [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
|
||||||
|
" diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
|
||||||
|
" [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
|
||||||
|
" [-F pattern] -U number file1 file2\n"
|
||||||
|
" diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
|
||||||
|
" [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
|
||||||
|
" [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2\n"
|
||||||
|
" diff [-aBbditwW] [--expand-tabs] [--ignore-all-space]\n"
|
||||||
|
" [--ignore-blank-lines] [--ignore-case] [--minimal]\n"
|
||||||
|
" [--no-ignore-file-name-case] [--strip-trailing-cr]\n"
|
||||||
|
" [--suppress-common-lines] [--tabsize] [--text] [--width]\n"
|
||||||
|
" -y | --side-by-side file1 file2\n"
|
||||||
|
" diff [--help] [--version]\n");
|
||||||
|
|
||||||
|
if (help)
|
||||||
|
exit(0);
|
||||||
|
else
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
conflicting_format(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf(stderr, "error: conflicting output format options.\n");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
do_color(void)
|
||||||
|
{
|
||||||
|
const char *p, *p2;
|
||||||
|
|
||||||
|
switch (colorflag) {
|
||||||
|
case COLORFLAG_AUTO:
|
||||||
|
p = getenv("CLICOLOR");
|
||||||
|
p2 = getenv("COLORTERM");
|
||||||
|
if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0'))
|
||||||
|
return isatty(STDOUT_FILENO);
|
||||||
|
break;
|
||||||
|
case COLORFLAG_ALWAYS:
|
||||||
|
return (true);
|
||||||
|
case COLORFLAG_NEVER:
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
splice(char *dir, char *path)
|
||||||
|
{
|
||||||
|
char *tail, *buf;
|
||||||
|
size_t dirlen;
|
||||||
|
|
||||||
|
dirlen = strlen(dir);
|
||||||
|
while (dirlen != 0 && dir[dirlen - 1] == '/')
|
||||||
|
dirlen--;
|
||||||
|
if ((tail = strrchr(path, '/')) == NULL)
|
||||||
|
tail = path;
|
||||||
|
else
|
||||||
|
tail++;
|
||||||
|
xasprintf(&buf, "%.*s/%s", (int)dirlen, dir, tail);
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
/* $OpenBSD: diff.h,v 1.34 2020/11/01 18:16:08 jcs Exp $ */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1991, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)diff.h 8.1 (Berkeley) 6/6/93
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Namespace flag globals to avoid collisions in the vx multicall binary.
|
||||||
|
* cmp/extern.h defines identically-named globals.
|
||||||
|
*/
|
||||||
|
#define lflag diff_lflag
|
||||||
|
#define sflag diff_sflag
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output format options
|
||||||
|
*/
|
||||||
|
#define D_NORMAL 0 /* Normal output */
|
||||||
|
#define D_EDIT -1 /* Editor script out */
|
||||||
|
#define D_REVERSE 1 /* Reverse editor script */
|
||||||
|
#define D_CONTEXT 2 /* Diff with context */
|
||||||
|
#define D_UNIFIED 3 /* Unified context diff */
|
||||||
|
#define D_IFDEF 4 /* Diff with merged #ifdef's */
|
||||||
|
#define D_NREVERSE 5 /* Reverse ed script with numbered
|
||||||
|
lines and no trailing . */
|
||||||
|
#define D_BRIEF 6 /* Say if the files differ */
|
||||||
|
#define D_GFORMAT 7 /* Diff with defined changed group format */
|
||||||
|
#define D_SIDEBYSIDE 8 /* Side by side */
|
||||||
|
|
||||||
|
#define D_UNSET -2
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output flags
|
||||||
|
*/
|
||||||
|
#define D_HEADER 0x001 /* Print a header/footer between files */
|
||||||
|
#define D_EMPTY1 0x002 /* Treat first file as empty (/dev/null) */
|
||||||
|
#define D_EMPTY2 0x004 /* Treat second file as empty (/dev/null) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command line flags
|
||||||
|
*/
|
||||||
|
#define D_FORCEASCII 0x008 /* Treat file as ascii regardless of content */
|
||||||
|
#define D_FOLDBLANKS 0x010 /* Treat all white space as equal */
|
||||||
|
#define D_MINIMAL 0x020 /* Make diff as small as possible */
|
||||||
|
#define D_IGNORECASE 0x040 /* Case-insensitive matching */
|
||||||
|
#define D_PROTOTYPE 0x080 /* Display C function prototype */
|
||||||
|
#define D_EXPANDTABS 0x100 /* Expand tabs to spaces */
|
||||||
|
#define D_IGNOREBLANKS 0x200 /* Ignore white space changes */
|
||||||
|
#define D_STRIPCR 0x400 /* Strip trailing cr */
|
||||||
|
#define D_SKIPBLANKLINES 0x800 /* Skip blank lines */
|
||||||
|
#define D_MATCHLAST 0x1000 /* Display last line matching provided regex */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Status values for print_status() and diffreg() return values
|
||||||
|
*/
|
||||||
|
#define D_SAME 0 /* Files are the same */
|
||||||
|
#define D_DIFFER 1 /* Files are different */
|
||||||
|
#define D_BINARY 2 /* Binary files are different */
|
||||||
|
#define D_MISMATCH1 3 /* path1 was a dir, path2 a file */
|
||||||
|
#define D_MISMATCH2 4 /* path1 was a file, path2 a dir */
|
||||||
|
#define D_SKIPPED1 5 /* path1 was a special file */
|
||||||
|
#define D_SKIPPED2 6 /* path2 was a special file */
|
||||||
|
#define D_ERROR 7 /* A file access error occurred */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Color options
|
||||||
|
*/
|
||||||
|
#define COLORFLAG_NEVER 0
|
||||||
|
#define COLORFLAG_AUTO 1
|
||||||
|
#define COLORFLAG_ALWAYS 2
|
||||||
|
|
||||||
|
struct excludes {
|
||||||
|
char *pattern;
|
||||||
|
struct excludes *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
|
||||||
|
extern bool ignore_file_case, suppress_common, color, noderef;
|
||||||
|
extern int diff_format, diff_context, status;
|
||||||
|
extern int tabsize, width;
|
||||||
|
extern char *start, *ifdefname, *diffargs, *label[2];
|
||||||
|
extern char *ignore_pats, *most_recent_pat;
|
||||||
|
extern char *group_format;
|
||||||
|
extern const char *add_code, *del_code;
|
||||||
|
extern struct stat stb1, stb2;
|
||||||
|
extern struct excludes *excludes_list;
|
||||||
|
extern regex_t ignore_re, most_recent_re;
|
||||||
|
|
||||||
|
int diffreg(char *, char *, int, int);
|
||||||
|
void diffdir(char *, char *, int);
|
||||||
|
void print_status(int, char *, char *, const char *);
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
/* $OpenBSD: diffdir.c,v 1.45 2015/10/05 20:15:00 millert Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* Sponsored in part by the Defense Advanced Research Projects
|
||||||
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||||
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/tree.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
|
|
||||||
|
static int selectfile(const struct dirent *);
|
||||||
|
static void diffit(struct dirent *, char *, size_t, struct dirent *,
|
||||||
|
char *, size_t, int);
|
||||||
|
static void print_only(const char *, size_t, const char *);
|
||||||
|
|
||||||
|
struct inode {
|
||||||
|
dev_t dev;
|
||||||
|
ino_t ino;
|
||||||
|
RB_ENTRY(inode) entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
inodecmp(struct inode *a, struct inode *b)
|
||||||
|
{
|
||||||
|
return (a->dev < b->dev ? -1 : a->dev > b->dev ? 1 :
|
||||||
|
a->ino < b->ino ? -1 : a->ino > b->ino ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RB_HEAD(inodetree, inode);
|
||||||
|
static struct inodetree v1 = RB_INITIALIZER(&v1);
|
||||||
|
static struct inodetree v2 = RB_INITIALIZER(&v2);
|
||||||
|
RB_GENERATE_STATIC(inodetree, inode, entry, inodecmp);
|
||||||
|
|
||||||
|
static int
|
||||||
|
vscandir(struct inodetree *tree, const char *path, struct dirent ***dirp,
|
||||||
|
int (*selectf)(const struct dirent *),
|
||||||
|
int (*comparf)(const struct dirent **, const struct dirent **))
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
struct inode *ino = NULL;
|
||||||
|
int fd = -1, ret, serrno;
|
||||||
|
|
||||||
|
if ((fd = open(path, O_DIRECTORY | O_RDONLY)) < 0 ||
|
||||||
|
(ino = calloc(1, sizeof(*ino))) == NULL ||
|
||||||
|
fstat(fd, &sb) != 0)
|
||||||
|
goto fail;
|
||||||
|
ino->dev = sb.st_dev;
|
||||||
|
ino->ino = sb.st_ino;
|
||||||
|
if (RB_FIND(inodetree, tree, ino)) {
|
||||||
|
free(ino);
|
||||||
|
close(fd);
|
||||||
|
warnx("%s: Directory loop detected", path);
|
||||||
|
*dirp = NULL;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* fdscandir() is FreeBSD-specific. Use scandir() on the path
|
||||||
|
* instead, since we have already verified the fd is valid.
|
||||||
|
*/
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
if ((ret = scandir(path, dirp, selectf, comparf)) < 0)
|
||||||
|
goto fail;
|
||||||
|
RB_INSERT(inodetree, tree, ino);
|
||||||
|
return (ret);
|
||||||
|
fail:
|
||||||
|
serrno = errno;
|
||||||
|
if (ino != NULL)
|
||||||
|
free(ino);
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
errno = serrno;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Diff directory traversal. Will be called recursively if -r was specified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
diffdir(char *p1, char *p2, int flags)
|
||||||
|
{
|
||||||
|
struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
|
||||||
|
struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
|
||||||
|
size_t dirlen1, dirlen2;
|
||||||
|
char path1[PATH_MAX], path2[PATH_MAX];
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
edp1 = edp2 = NULL;
|
||||||
|
|
||||||
|
dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
|
||||||
|
if (dirlen1 >= sizeof(path1) - 1) {
|
||||||
|
warnc(ENAMETOOLONG, "%s", p1);
|
||||||
|
status |= 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (dirlen1 > 1 && path1[dirlen1 - 1] == '/')
|
||||||
|
path1[--dirlen1] = '\0';
|
||||||
|
dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
|
||||||
|
if (dirlen2 >= sizeof(path2) - 1) {
|
||||||
|
warnc(ENAMETOOLONG, "%s", p2);
|
||||||
|
status |= 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (dirlen2 > 1 && path2[dirlen2 - 1] == '/')
|
||||||
|
path2[--dirlen2] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a list of entries in each directory, skipping "excluded" files
|
||||||
|
* and sorting alphabetically.
|
||||||
|
*/
|
||||||
|
pos = vscandir(&v1, path1, &dirp1, selectfile, alphasort);
|
||||||
|
if (pos == -1) {
|
||||||
|
if (errno == ENOENT && (Nflag || Pflag)) {
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
warn("%s", path1);
|
||||||
|
goto closem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dp1 = dirp1;
|
||||||
|
edp1 = dirp1 + pos;
|
||||||
|
|
||||||
|
pos = vscandir(&v2, path2, &dirp2, selectfile, alphasort);
|
||||||
|
if (pos == -1) {
|
||||||
|
if (errno == ENOENT && Nflag) {
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
warn("%s", path2);
|
||||||
|
goto closem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dp2 = dirp2;
|
||||||
|
edp2 = dirp2 + pos;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we were given a starting point, find it.
|
||||||
|
*/
|
||||||
|
if (start != NULL) {
|
||||||
|
while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
|
||||||
|
dp1++;
|
||||||
|
while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
|
||||||
|
dp2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append separator so children's names can be appended directly.
|
||||||
|
*/
|
||||||
|
if (path1[dirlen1 - 1] != '/') {
|
||||||
|
path1[dirlen1++] = '/';
|
||||||
|
path1[dirlen1] = '\0';
|
||||||
|
}
|
||||||
|
if (path2[dirlen2 - 1] != '/') {
|
||||||
|
path2[dirlen2++] = '/';
|
||||||
|
path2[dirlen2] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate through the two directory lists, diffing as we go.
|
||||||
|
*/
|
||||||
|
while (dp1 != edp1 || dp2 != edp2) {
|
||||||
|
dent1 = dp1 != edp1 ? *dp1 : NULL;
|
||||||
|
dent2 = dp2 != edp2 ? *dp2 : NULL;
|
||||||
|
|
||||||
|
pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
|
||||||
|
ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name) :
|
||||||
|
strcmp(dent1->d_name, dent2->d_name) ;
|
||||||
|
if (pos == 0) {
|
||||||
|
/* file exists in both dirs, diff it */
|
||||||
|
diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
|
||||||
|
dp1++;
|
||||||
|
dp2++;
|
||||||
|
} else if (pos < 0) {
|
||||||
|
/* file only in first dir, only diff if -N */
|
||||||
|
if (Nflag) {
|
||||||
|
diffit(dent1, path1, dirlen1, dent2, path2,
|
||||||
|
dirlen2, flags);
|
||||||
|
} else {
|
||||||
|
print_only(path1, dirlen1, dent1->d_name);
|
||||||
|
status |= 1;
|
||||||
|
}
|
||||||
|
dp1++;
|
||||||
|
} else {
|
||||||
|
/* file only in second dir, only diff if -N or -P */
|
||||||
|
if (Nflag || Pflag)
|
||||||
|
diffit(dent2, path1, dirlen1, dent1, path2,
|
||||||
|
dirlen2, flags);
|
||||||
|
else {
|
||||||
|
print_only(path2, dirlen2, dent2->d_name);
|
||||||
|
status |= 1;
|
||||||
|
}
|
||||||
|
dp2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closem:
|
||||||
|
if (dirp1 != NULL) {
|
||||||
|
for (dp1 = dirp1; dp1 < edp1; dp1++)
|
||||||
|
free(*dp1);
|
||||||
|
free(dirp1);
|
||||||
|
}
|
||||||
|
if (dirp2 != NULL) {
|
||||||
|
for (dp2 = dirp2; dp2 < edp2; dp2++)
|
||||||
|
free(*dp2);
|
||||||
|
free(dirp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the actual diff by calling either diffreg() or diffdir().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
|
||||||
|
char *path2, size_t plen2, int flags)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
flags |= D_HEADER;
|
||||||
|
strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are ignoring file case, use dent2s name here if both names are
|
||||||
|
* the same apart from case.
|
||||||
|
*/
|
||||||
|
if (ignore_file_case && strcasecmp(dp2->d_name, dp2->d_name) == 0)
|
||||||
|
strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
|
||||||
|
else
|
||||||
|
strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
|
||||||
|
|
||||||
|
if (noderef) {
|
||||||
|
if (lstat(path1, &stb1) != 0) {
|
||||||
|
if (!(Nflag || Pflag) || errno != ENOENT) {
|
||||||
|
warn("%s", path1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flags |= D_EMPTY1;
|
||||||
|
memset(&stb1, 0, sizeof(stb1));
|
||||||
|
}
|
||||||
|
if (lstat(path2, &stb2) != 0) {
|
||||||
|
if (!Nflag || errno != ENOENT) {
|
||||||
|
warn("%s", path2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flags |= D_EMPTY2;
|
||||||
|
memset(&stb2, 0, sizeof(stb2));
|
||||||
|
stb2.st_mode = stb1.st_mode;
|
||||||
|
}
|
||||||
|
if (stb1.st_mode == 0)
|
||||||
|
stb1.st_mode = stb2.st_mode;
|
||||||
|
if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
|
||||||
|
if (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
|
||||||
|
char buf1[PATH_MAX];
|
||||||
|
char buf2[PATH_MAX];
|
||||||
|
ssize_t len1 = 0;
|
||||||
|
ssize_t len2 = 0;
|
||||||
|
|
||||||
|
len1 = readlink(path1, buf1, sizeof(buf1));
|
||||||
|
len2 = readlink(path2, buf2, sizeof(buf2));
|
||||||
|
|
||||||
|
if (len1 < 0 || len2 < 0) {
|
||||||
|
perror("reading links");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf1[len1] = '\0';
|
||||||
|
buf2[len2] = '\0';
|
||||||
|
|
||||||
|
if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
|
||||||
|
printf("Symbolic links %s and %s differ\n",
|
||||||
|
path1, path2);
|
||||||
|
status |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("File %s is a %s while file %s is a %s\n",
|
||||||
|
path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
|
||||||
|
(S_ISDIR(stb1.st_mode) ? "directory" :
|
||||||
|
(S_ISREG(stb1.st_mode) ? "file" : "error")),
|
||||||
|
path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
|
||||||
|
(S_ISDIR(stb2.st_mode) ? "directory" :
|
||||||
|
(S_ISREG(stb2.st_mode) ? "file" : "error")));
|
||||||
|
status |= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (stat(path1, &stb1) != 0) {
|
||||||
|
if (!(Nflag || Pflag) || errno != ENOENT) {
|
||||||
|
warn("%s", path1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flags |= D_EMPTY1;
|
||||||
|
memset(&stb1, 0, sizeof(stb1));
|
||||||
|
}
|
||||||
|
if (stat(path2, &stb2) != 0) {
|
||||||
|
if (!Nflag || errno != ENOENT) {
|
||||||
|
warn("%s", path2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flags |= D_EMPTY2;
|
||||||
|
memset(&stb2, 0, sizeof(stb2));
|
||||||
|
stb2.st_mode = stb1.st_mode;
|
||||||
|
}
|
||||||
|
if (stb1.st_mode == 0)
|
||||||
|
stb1.st_mode = stb2.st_mode;
|
||||||
|
}
|
||||||
|
if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
|
||||||
|
return;
|
||||||
|
if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
|
||||||
|
if (rflag)
|
||||||
|
diffdir(path1, path2, flags);
|
||||||
|
else
|
||||||
|
printf("Common subdirectories: %s and %s\n",
|
||||||
|
path1, path2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
|
||||||
|
rc = D_SKIPPED1;
|
||||||
|
else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
|
||||||
|
rc = D_SKIPPED2;
|
||||||
|
else
|
||||||
|
rc = diffreg(path1, path2, flags, 0);
|
||||||
|
print_status(rc, path1, path2, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 if the directory entry should be included in the
|
||||||
|
* diff, else 0. Checks the excludes list.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
selectfile(const struct dirent *dp)
|
||||||
|
{
|
||||||
|
struct excludes *excl;
|
||||||
|
|
||||||
|
if (dp->d_fileno == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* always skip "." and ".." */
|
||||||
|
if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
|
||||||
|
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* check excludes list */
|
||||||
|
for (excl = excludes_list; excl != NULL; excl = excl->next)
|
||||||
|
if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_only(const char *path, size_t dirlen, const char *entry)
|
||||||
|
{
|
||||||
|
if (dirlen > 1)
|
||||||
|
dirlen--;
|
||||||
|
printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
|
||||||
|
}
|
||||||
+1722
File diff suppressed because it is too large
Load Diff
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer
|
||||||
|
* in this position and unchanged.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <paths.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "pr.h"
|
||||||
|
#include "diff.h"
|
||||||
|
#include "xmalloc.h"
|
||||||
|
|
||||||
|
#define _PATH_PR "/usr/bin/pr"
|
||||||
|
|
||||||
|
struct pr *
|
||||||
|
start_pr(char *file1, char *file2)
|
||||||
|
{
|
||||||
|
int pfd[2];
|
||||||
|
pid_t pid;
|
||||||
|
char *header;
|
||||||
|
struct pr *pr;
|
||||||
|
|
||||||
|
pr = xcalloc(1, sizeof(*pr));
|
||||||
|
|
||||||
|
xasprintf(&header, "%s %s %s", diffargs, file1, file2);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
fflush(stdout);
|
||||||
|
if (pipe(pfd) == -1)
|
||||||
|
err(2, "pipe");
|
||||||
|
switch ((pid = fork())) {
|
||||||
|
case -1:
|
||||||
|
err(2, "No more processes");
|
||||||
|
case 0:
|
||||||
|
/* child */
|
||||||
|
if (pfd[0] != STDIN_FILENO) {
|
||||||
|
dup2(pfd[0], STDIN_FILENO);
|
||||||
|
close(pfd[0]);
|
||||||
|
}
|
||||||
|
close(pfd[1]);
|
||||||
|
execl(_PATH_PR, _PATH_PR, "-h", header, (char *)0);
|
||||||
|
_exit(127);
|
||||||
|
default:
|
||||||
|
/* parent */
|
||||||
|
if (pfd[1] == STDOUT_FILENO) {
|
||||||
|
pr->ostdout = STDOUT_FILENO;
|
||||||
|
} else {
|
||||||
|
if ((pr->ostdout = dup(STDOUT_FILENO)) < 0 ||
|
||||||
|
dup2(pfd[1], STDOUT_FILENO) < 0) {
|
||||||
|
err(2, "stdout");
|
||||||
|
}
|
||||||
|
close(pfd[1]);
|
||||||
|
}
|
||||||
|
close(pfd[0]);
|
||||||
|
free(header);
|
||||||
|
pr->pid = pid;
|
||||||
|
}
|
||||||
|
return (pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close the pipe to pr and restore stdout */
|
||||||
|
void
|
||||||
|
stop_pr(struct pr *pr)
|
||||||
|
{
|
||||||
|
int wstatus;
|
||||||
|
|
||||||
|
if (pr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
if (pr->ostdout != STDOUT_FILENO) {
|
||||||
|
dup2(pr->ostdout, STDOUT_FILENO);
|
||||||
|
close(pr->ostdout);
|
||||||
|
}
|
||||||
|
if (waitpid(pr->pid, &wstatus, 0) == -1)
|
||||||
|
err(2, "waitpid");
|
||||||
|
free(pr);
|
||||||
|
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
|
||||||
|
errx(2, "pr exited abnormally");
|
||||||
|
else if (WIFSIGNALED(wstatus))
|
||||||
|
errx(2, "pr killed by signal %d",
|
||||||
|
WTERMSIG(wstatus));
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer
|
||||||
|
* in this position and unchanged.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
struct pr {
|
||||||
|
int ostdout;
|
||||||
|
pid_t pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pr *start_pr(char *file1, char *file2);
|
||||||
|
void stop_pr(struct pr *);
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
1a2
|
||||||
|
>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
1a2
|
||||||
|
> C
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
7c8
|
||||||
|
< G
|
||||||
|
---
|
||||||
|
> X
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Autogenerated - do NOT edit!
|
||||||
|
|
||||||
|
DIRDEPS = \
|
||||||
|
|
||||||
|
|
||||||
|
.include <dirdeps.mk>
|
||||||
|
|
||||||
|
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||||
|
# local dependencies - needed for -jN in clean tree
|
||||||
|
.endif
|
||||||
Executable
+478
@@ -0,0 +1,478 @@
|
|||||||
|
|
||||||
|
atf_test_case simple
|
||||||
|
atf_test_case unified
|
||||||
|
atf_test_case header
|
||||||
|
atf_test_case header_ns
|
||||||
|
atf_test_case ifdef
|
||||||
|
atf_test_case group_format
|
||||||
|
atf_test_case side_by_side
|
||||||
|
atf_test_case side_by_side_tabbed
|
||||||
|
atf_test_case brief_format
|
||||||
|
atf_test_case b230049
|
||||||
|
atf_test_case stripcr_o
|
||||||
|
atf_test_case b252515
|
||||||
|
atf_test_case b278988
|
||||||
|
atf_test_case Bflag
|
||||||
|
atf_test_case Nflag
|
||||||
|
atf_test_case tabsize
|
||||||
|
atf_test_case conflicting_format
|
||||||
|
atf_test_case label
|
||||||
|
atf_test_case report_identical
|
||||||
|
atf_test_case non_regular_file
|
||||||
|
atf_test_case binary
|
||||||
|
atf_test_case functionname
|
||||||
|
atf_test_case noderef
|
||||||
|
atf_test_case ignorecase
|
||||||
|
atf_test_case dirloop
|
||||||
|
atf_test_case bigc
|
||||||
|
atf_test_case bigu
|
||||||
|
atf_test_case prleak
|
||||||
|
atf_test_case same
|
||||||
|
|
||||||
|
simple_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple.out -s eq:1 \
|
||||||
|
diff "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_e.out -s eq:1 \
|
||||||
|
diff -e "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_u.out -s eq:1 \
|
||||||
|
diff -u -L input1 -L input2 "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_n.out -s eq:1 \
|
||||||
|
diff -n "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input2.in"
|
||||||
|
|
||||||
|
atf_check -o inline:"Files $(atf_get_srcdir)/input1.in and $(atf_get_srcdir)/input2.in differ\n" -s eq:1 \
|
||||||
|
diff -q "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input2.in"
|
||||||
|
|
||||||
|
atf_check \
|
||||||
|
diff -q "$(atf_get_srcdir)/input1.in" "$(atf_get_srcdir)/input1.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_i.out -s eq:1 \
|
||||||
|
diff -i "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_w.out -s eq:1 \
|
||||||
|
diff -w "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_b.out -s eq:1 \
|
||||||
|
diff -b "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/simple_p.out -s eq:1 \
|
||||||
|
diff --label input_c1.in --label input_c2.in -p "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
}
|
||||||
|
|
||||||
|
unified_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/unified_p.out -s eq:1 \
|
||||||
|
diff -up -L input_c1.in -L input_c2.in "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/unified_9999.out -s eq:1 \
|
||||||
|
diff -u9999 -L input_c1.in -L input_c2.in "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
}
|
||||||
|
|
||||||
|
b230049_body()
|
||||||
|
{
|
||||||
|
printf 'a\nb\r\nc\n' > b230049_a.in
|
||||||
|
printf 'a\r\nb\r\nc\r\n' > b230049_b.in
|
||||||
|
atf_check -o empty -s eq:0 \
|
||||||
|
diff -up --strip-trailing-cr -L b230049_a.in -L b230049_b.in \
|
||||||
|
b230049_a.in b230049_b.in
|
||||||
|
}
|
||||||
|
|
||||||
|
stripcr_o_body()
|
||||||
|
{
|
||||||
|
printf 'a\nX\nc\n' > stripcr_o_X.in
|
||||||
|
printf 'a\r\nY\r\nc\r\n' > stripcr_o_Y.in
|
||||||
|
atf_check -o "file:$(atf_get_srcdir)/strip_o.out" -s eq:1 \
|
||||||
|
diff -L1 -L2 -u --strip-trailing-cr stripcr_o_X.in stripcr_o_Y.in
|
||||||
|
}
|
||||||
|
|
||||||
|
b252515_body()
|
||||||
|
{
|
||||||
|
printf 'a b\n' > b252515_a.in
|
||||||
|
printf 'a b\n' > b252515_b.in
|
||||||
|
atf_check -o empty -s eq:0 \
|
||||||
|
diff -qw b252515_a.in b252515_b.in
|
||||||
|
}
|
||||||
|
|
||||||
|
b278988_body()
|
||||||
|
{
|
||||||
|
printf 'a\nb\nn' > b278988.a.in
|
||||||
|
printf 'a\n\nb\nn' > b278988.b.in
|
||||||
|
atf_check -o empty -s eq:0 \
|
||||||
|
diff -Bw b278988.a.in b278988.b.in
|
||||||
|
}
|
||||||
|
|
||||||
|
header_body()
|
||||||
|
{
|
||||||
|
export TZ=UTC
|
||||||
|
: > empty
|
||||||
|
echo hello > hello
|
||||||
|
touch -d 2015-04-03T01:02:03 empty
|
||||||
|
touch -d 2016-12-22T11:22:33 hello
|
||||||
|
atf_check -o "file:$(atf_get_srcdir)/header.out" -s eq:1 \
|
||||||
|
diff -u empty hello
|
||||||
|
}
|
||||||
|
|
||||||
|
header_ns_body()
|
||||||
|
{
|
||||||
|
export TZ=UTC
|
||||||
|
: > empty
|
||||||
|
echo hello > hello
|
||||||
|
touch -d 2015-04-03T01:02:03.123456789 empty
|
||||||
|
touch -d 2016-12-22T11:22:33.987654321 hello
|
||||||
|
atf_check -o "file:$(atf_get_srcdir)/header_ns.out" -s eq:1 \
|
||||||
|
diff -u empty hello
|
||||||
|
}
|
||||||
|
|
||||||
|
ifdef_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/ifdef.out -s eq:1 \
|
||||||
|
diff -D PLOP "$(atf_get_srcdir)/input_c1.in" \
|
||||||
|
"$(atf_get_srcdir)/input_c2.in"
|
||||||
|
}
|
||||||
|
|
||||||
|
group_format_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/group-format.out -s eq:1 \
|
||||||
|
diff --changed-group-format='<<<<<<< (local)
|
||||||
|
%<=======
|
||||||
|
%>>>>>>>> (stock)
|
||||||
|
' "$(atf_get_srcdir)/input_c1.in" "$(atf_get_srcdir)/input_c2.in"
|
||||||
|
}
|
||||||
|
|
||||||
|
side_by_side_body()
|
||||||
|
{
|
||||||
|
atf_check -o save:A printf "A\nB\nC\n"
|
||||||
|
atf_check -o save:B printf "D\nB\nE\n"
|
||||||
|
|
||||||
|
exp_output=$(printf "A[[:space:]]+|[[:space:]]+D\nB[[:space:]]+B\nC[[:space:]]+|[[:space:]]+E")
|
||||||
|
exp_output_suppressed=$(printf "A[[:space:]]+|[[:space:]]+D\nC[[:space:]]+|[[:space:]]+E")
|
||||||
|
|
||||||
|
atf_check -o match:"$exp_output" -s exit:1 \
|
||||||
|
diff --side-by-side A B
|
||||||
|
atf_check -o match:"$exp_output" -s exit:1 \
|
||||||
|
diff -y A B
|
||||||
|
atf_check -o match:"$exp_output_suppressed" -s exit:1 \
|
||||||
|
diff -y --suppress-common-lines A B
|
||||||
|
atf_check -o match:"$exp_output_suppressed" -s exit:1 \
|
||||||
|
diff -W 65 -y --suppress-common-lines A B
|
||||||
|
}
|
||||||
|
|
||||||
|
side_by_side_tabbed_body()
|
||||||
|
{
|
||||||
|
file_a=$(atf_get_srcdir)/side_by_side_tabbed_a.in
|
||||||
|
file_b=$(atf_get_srcdir)/side_by_side_tabbed_b.in
|
||||||
|
|
||||||
|
atf_check -o save:diffout -s not-exit:0 \
|
||||||
|
diff -y ${file_a} ${file_b}
|
||||||
|
atf_check -o save:diffout_expanded -s not-exit:0 \
|
||||||
|
diff -yt ${file_a} ${file_b}
|
||||||
|
|
||||||
|
atf_check -o not-empty grep -Ee 'file A.+file B' diffout
|
||||||
|
atf_check -o not-empty grep -Ee 'file A.+file B' diffout_expanded
|
||||||
|
|
||||||
|
atf_check -o not-empty grep -Ee 'tabs.+tabs' diffout
|
||||||
|
atf_check -o not-empty grep -Ee 'tabs.+tabs' diffout_expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
brief_format_body()
|
||||||
|
{
|
||||||
|
atf_check mkdir A B
|
||||||
|
|
||||||
|
atf_check -x "echo 1 > A/test-file"
|
||||||
|
atf_check -x "echo 2 > B/test-file"
|
||||||
|
|
||||||
|
atf_check cp -Rf A C
|
||||||
|
atf_check cp -Rf A D
|
||||||
|
|
||||||
|
atf_check -x "echo 3 > D/another-test-file"
|
||||||
|
|
||||||
|
atf_check \
|
||||||
|
-s exit:1 \
|
||||||
|
-o inline:"Files A/test-file and B/test-file differ\n" \
|
||||||
|
diff -rq A B
|
||||||
|
|
||||||
|
atf_check diff -rq A C
|
||||||
|
|
||||||
|
atf_check \
|
||||||
|
-s exit:1 \
|
||||||
|
-o inline:"Only in D: another-test-file\n" \
|
||||||
|
diff -rq A D
|
||||||
|
|
||||||
|
atf_check \
|
||||||
|
-s exit:1 \
|
||||||
|
-o inline:"Files A/another-test-file and D/another-test-file differ\n" \
|
||||||
|
diff -Nrq A D
|
||||||
|
}
|
||||||
|
|
||||||
|
Bflag_body()
|
||||||
|
{
|
||||||
|
atf_check -x 'printf "A\nB\n" > A'
|
||||||
|
atf_check -x 'printf "A\n\nB\n" > B'
|
||||||
|
atf_check -x 'printf "A\n \nB\n" > C'
|
||||||
|
atf_check -x 'printf "A\nC\nB\n" > D'
|
||||||
|
atf_check -x 'printf "A\nB\nC\nD\nE\nF\nG\nH" > E'
|
||||||
|
atf_check -x 'printf "A\n\nB\nC\nD\nE\nF\nX\nH" > F'
|
||||||
|
|
||||||
|
atf_check -s exit:0 -o inline:"" diff -B A B
|
||||||
|
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_C.out" diff -B A C
|
||||||
|
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_D.out" diff -B A D
|
||||||
|
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_F.out" diff -B E F
|
||||||
|
}
|
||||||
|
|
||||||
|
Nflag_body()
|
||||||
|
{
|
||||||
|
atf_check -x 'printf "foo" > A'
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -N A NOFILE
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -N NOFILE A
|
||||||
|
atf_check -s exit:2 -o ignore -e ignore diff -N NOFILE1 NOFILE2
|
||||||
|
}
|
||||||
|
|
||||||
|
tabsize_body()
|
||||||
|
{
|
||||||
|
printf "\tA\n" > A
|
||||||
|
printf "\tB\n" > B
|
||||||
|
|
||||||
|
atf_check -s exit:1 \
|
||||||
|
-o inline:"1c1\n< A\n---\n> B\n" \
|
||||||
|
diff -t --tabsize 1 A B
|
||||||
|
}
|
||||||
|
|
||||||
|
conflicting_format_body()
|
||||||
|
{
|
||||||
|
printf "\tA\n" > A
|
||||||
|
printf "\tB\n" > B
|
||||||
|
|
||||||
|
atf_check -s exit:2 -e ignore diff -c -u A B
|
||||||
|
atf_check -s exit:2 -e ignore diff -e -f A B
|
||||||
|
atf_check -s exit:2 -e ignore diff -y -q A B
|
||||||
|
atf_check -s exit:2 -e ignore diff -q -u A B
|
||||||
|
atf_check -s exit:2 -e ignore diff -q -c A B
|
||||||
|
atf_check -s exit:2 -e ignore diff --normal -c A B
|
||||||
|
atf_check -s exit:2 -e ignore diff -c --normal A B
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -u -u A B
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -e -e A B
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -y -y A B
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -q -q A B
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff -c -c A B
|
||||||
|
atf_check -s exit:1 -o ignore -e ignore diff --normal --normal A B
|
||||||
|
}
|
||||||
|
|
||||||
|
label_body()
|
||||||
|
{
|
||||||
|
printf "\tA\n" > A
|
||||||
|
|
||||||
|
atf_check -o inline:"Files hello and world are identical\n" \
|
||||||
|
-s exit:0 diff --label hello --label world -s A A
|
||||||
|
|
||||||
|
atf_check -o inline:"Binary files hello and world differ\n" \
|
||||||
|
-s exit:1 diff --label hello --label world `which diff` `which ls`
|
||||||
|
}
|
||||||
|
|
||||||
|
report_identical_head()
|
||||||
|
{
|
||||||
|
atf_set "require.user" unprivileged
|
||||||
|
}
|
||||||
|
report_identical_body()
|
||||||
|
{
|
||||||
|
printf "\tA\n" > A
|
||||||
|
printf "\tB\n" > B
|
||||||
|
chmod -r B
|
||||||
|
atf_check -s exit:2 -e inline:"diff: B: Permission denied\n" \
|
||||||
|
-o empty diff -s A B
|
||||||
|
}
|
||||||
|
|
||||||
|
non_regular_file_body()
|
||||||
|
{
|
||||||
|
printf "\tA\n" > A
|
||||||
|
mkfifo B
|
||||||
|
printf "\tA\n" > B &
|
||||||
|
|
||||||
|
atf_check diff A B
|
||||||
|
printf "\tB\n" > B &
|
||||||
|
atf_check -s exit:1 \
|
||||||
|
-o inline:"--- A\n+++ B\n@@ -1 +1 @@\n-\tA\n+\tB\n" \
|
||||||
|
diff --label A --label B -u A B
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_body()
|
||||||
|
{
|
||||||
|
# the NUL byte has to be after at least BUFSIZ bytes to trick asciifile()
|
||||||
|
yes 012345678901234567890123456789012345678901234567890 | head -n 174 > A
|
||||||
|
cp A B
|
||||||
|
printf '\n\0\n' >> A
|
||||||
|
printf '\nx\n' >> B
|
||||||
|
|
||||||
|
atf_check -o inline:"Binary files A and B differ\n" -s exit:1 diff A B
|
||||||
|
atf_check -o inline:"176c\nx\n.\n" -s exit:1 diff -ae A B
|
||||||
|
}
|
||||||
|
|
||||||
|
functionname_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/functionname_c.out -s exit:1 \
|
||||||
|
diff -u -p -L functionname.in -L functionname_c.in \
|
||||||
|
"$(atf_get_srcdir)/functionname.in" "$(atf_get_srcdir)/functionname_c.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/functionname_objcm.out -s exit:1 \
|
||||||
|
diff -u -p -L functionname.in -L functionname_objcm.in \
|
||||||
|
"$(atf_get_srcdir)/functionname.in" "$(atf_get_srcdir)/functionname_objcm.in"
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/functionname_objcclassm.out -s exit:1 \
|
||||||
|
diff -u -p -L functionname.in -L functionname_objcclassm.in \
|
||||||
|
"$(atf_get_srcdir)/functionname.in" "$(atf_get_srcdir)/functionname_objcclassm.in"
|
||||||
|
}
|
||||||
|
|
||||||
|
noderef_body()
|
||||||
|
{
|
||||||
|
atf_check mkdir A B
|
||||||
|
|
||||||
|
atf_check -x "echo 1 > A/test-file"
|
||||||
|
atf_check -x "echo 1 > test-file"
|
||||||
|
atf_check -x "echo 1 > test-file2"
|
||||||
|
|
||||||
|
atf_check ln -s $(pwd)/test-file B/test-file
|
||||||
|
|
||||||
|
atf_check -o empty -s exit:0 diff -r A B
|
||||||
|
atf_check -o inline:"File A/test-file is a file while file B/test-file is a symbolic link\n" \
|
||||||
|
-s exit:1 diff -r --no-dereference A B
|
||||||
|
|
||||||
|
# both test files are now the same symbolic link
|
||||||
|
atf_check rm A/test-file
|
||||||
|
|
||||||
|
atf_check ln -s $(pwd)/test-file A/test-file
|
||||||
|
atf_check -o empty -s exit:0 diff -r A B
|
||||||
|
atf_check -o empty -s exit:0 diff -r --no-dereference A B
|
||||||
|
|
||||||
|
# make test files different symbolic links, but same contents
|
||||||
|
atf_check unlink A/test-file
|
||||||
|
atf_check ln -s $(pwd)/test-file2 A/test-file
|
||||||
|
|
||||||
|
atf_check -o empty -s exit:0 diff -r A B
|
||||||
|
atf_check -o inline:"Symbolic links A/test-file and B/test-file differ\n" -s exit:1 diff -r --no-dereference A B
|
||||||
|
}
|
||||||
|
|
||||||
|
ignorecase_body()
|
||||||
|
{
|
||||||
|
atf_check mkdir A
|
||||||
|
atf_check mkdir B
|
||||||
|
|
||||||
|
atf_check -x "echo hello > A/foo"
|
||||||
|
atf_check -x "echo hello > B/FOO"
|
||||||
|
|
||||||
|
atf_check -o empty -s exit:0 diff -u -r --ignore-file-name-case A B
|
||||||
|
}
|
||||||
|
|
||||||
|
dirloop_head()
|
||||||
|
{
|
||||||
|
atf_set "timeout" "10"
|
||||||
|
}
|
||||||
|
dirloop_body()
|
||||||
|
{
|
||||||
|
atf_check mkdir -p a/foo/bar
|
||||||
|
atf_check ln -s .. a/foo/bar/up
|
||||||
|
atf_check cp -a a b
|
||||||
|
atf_check \
|
||||||
|
-e match:"a/foo/bar/up: Directory loop detected" \
|
||||||
|
-e match:"b/foo/bar/up: Directory loop detected" \
|
||||||
|
diff -r a b
|
||||||
|
}
|
||||||
|
|
||||||
|
bigc_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Context diff with very large context"
|
||||||
|
}
|
||||||
|
bigc_body()
|
||||||
|
{
|
||||||
|
echo $'x\na\ny' >a
|
||||||
|
echo $'x\nb\ny' >b
|
||||||
|
atf_check -s exit:2 -e ignore diff -C$(((1<<31)-1)) a b
|
||||||
|
atf_check -s exit:1 -o match:'--- 1,3 ---' \
|
||||||
|
diff -C$(((1<<31)-2)) a b
|
||||||
|
}
|
||||||
|
|
||||||
|
bigu_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Unified diff with very large context"
|
||||||
|
}
|
||||||
|
bigu_body()
|
||||||
|
{
|
||||||
|
echo $'x\na\ny' >a
|
||||||
|
echo $'x\nb\ny' >b
|
||||||
|
atf_check -s exit:2 -e ignore diff -U$(((1<<31)-1)) a b
|
||||||
|
atf_check -s exit:1 -o match:'^@@ -1,3 \+1,3 @@$' \
|
||||||
|
diff -U$(((1<<31)-2)) a b
|
||||||
|
}
|
||||||
|
|
||||||
|
prleak_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Verify that pagination does not leak resources"
|
||||||
|
}
|
||||||
|
prleak_body()
|
||||||
|
{
|
||||||
|
local n=32
|
||||||
|
mkdir a b
|
||||||
|
for hi in $(jot -w%02x $n 0) ; do
|
||||||
|
mkdir a/$hi b/$hi
|
||||||
|
for lo in $(jot -w%02x $n 0) ; do
|
||||||
|
echo "$hi$lo" >a/$hi/$lo
|
||||||
|
echo "$hi$lo" >b/$hi/$lo
|
||||||
|
done
|
||||||
|
done
|
||||||
|
ulimit -n 1000
|
||||||
|
ulimit -u 1000
|
||||||
|
atf_check diff -rul a b
|
||||||
|
}
|
||||||
|
|
||||||
|
same_head()
|
||||||
|
{
|
||||||
|
atf_set "descr" "Don't diff a file or directory with itself"
|
||||||
|
}
|
||||||
|
same_body()
|
||||||
|
{
|
||||||
|
local n=256
|
||||||
|
mkdir a
|
||||||
|
for hi in $(jot -w%02x $n 0) ; do
|
||||||
|
mkdir a/$hi
|
||||||
|
for lo in $(jot -w%02x $n 0) ; do
|
||||||
|
echo "$hi$lo" >a/$hi/$lo
|
||||||
|
done
|
||||||
|
done
|
||||||
|
ln -s a b
|
||||||
|
atf_check timeout 1s diff -rqs a b
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_init_test_cases()
|
||||||
|
{
|
||||||
|
atf_add_test_case simple
|
||||||
|
atf_add_test_case unified
|
||||||
|
atf_add_test_case header
|
||||||
|
atf_add_test_case header_ns
|
||||||
|
atf_add_test_case ifdef
|
||||||
|
atf_add_test_case group_format
|
||||||
|
atf_add_test_case side_by_side
|
||||||
|
atf_add_test_case side_by_side_tabbed
|
||||||
|
atf_add_test_case brief_format
|
||||||
|
atf_add_test_case b230049
|
||||||
|
atf_add_test_case stripcr_o
|
||||||
|
atf_add_test_case b252515
|
||||||
|
atf_add_test_case b278988
|
||||||
|
atf_add_test_case Bflag
|
||||||
|
atf_add_test_case Nflag
|
||||||
|
atf_add_test_case tabsize
|
||||||
|
atf_add_test_case conflicting_format
|
||||||
|
atf_add_test_case label
|
||||||
|
atf_add_test_case report_identical
|
||||||
|
atf_add_test_case non_regular_file
|
||||||
|
atf_add_test_case binary
|
||||||
|
atf_add_test_case functionname
|
||||||
|
atf_add_test_case noderef
|
||||||
|
atf_add_test_case ignorecase
|
||||||
|
atf_add_test_case dirloop
|
||||||
|
atf_add_test_case bigc
|
||||||
|
atf_add_test_case bigu
|
||||||
|
atf_add_test_case prleak
|
||||||
|
atf_add_test_case same
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
static void
|
||||||
|
doSomethingThenPrintHello(int test)
|
||||||
|
{
|
||||||
|
test = test << 4;
|
||||||
|
if (test % 8 == 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("goodbye\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (long) readOffset:(FILE*)file
|
||||||
|
{
|
||||||
|
if( version >= 11){
|
||||||
|
long offset;
|
||||||
|
fread(&offset, sizeof(long), 1, file);
|
||||||
|
return offset;
|
||||||
|
} else {
|
||||||
|
int offset;
|
||||||
|
fread(&offset, sizeof(int), 1, file);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
static void
|
||||||
|
doSomethingThenPrintHello(int test)
|
||||||
|
{
|
||||||
|
test = test << 4;
|
||||||
|
if (test % 8 == 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("hello\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (long) readOffset:(FILE*)file
|
||||||
|
{
|
||||||
|
if( version >= 11){
|
||||||
|
long offset;
|
||||||
|
fread(&offset, sizeof(long), 1, file);
|
||||||
|
return offset;
|
||||||
|
} else {
|
||||||
|
int offset;
|
||||||
|
fread(&offset, sizeof(int), 1, file);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
--- functionname.in
|
||||||
|
+++ functionname_c.in
|
||||||
|
@@ -6,7 +6,7 @@ doSomethingThenPrintHello(int test)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- print("goodbye\n");
|
||||||
|
+ print("hello\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
static void
|
||||||
|
doSomethingThenPrintHello(int test)
|
||||||
|
{
|
||||||
|
test = test << 4;
|
||||||
|
if (test % 8 == 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("goodbye\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (long) readOffset:(FILE*)file
|
||||||
|
{
|
||||||
|
if( version >= 11){
|
||||||
|
long offset;
|
||||||
|
fread(&offset, sizeof(long), 1, file);
|
||||||
|
return offset;
|
||||||
|
} else {
|
||||||
|
int offset;
|
||||||
|
fread(&offset, sizeof(int), 1, file);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
{
|
||||||
|
/* With a solid guide book (such as Phillips 2006) assume we can't eat
|
||||||
|
* the fungus */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
--- functionname.in
|
||||||
|
+++ functionname_objcclassm.in
|
||||||
|
@@ -25,5 +25,7 @@ + (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
|
||||||
|
+ (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
{
|
||||||
|
- return TRUE;
|
||||||
|
+ /* With a solid guide book (such as Phillips 2006) assume we can't eat
|
||||||
|
+ * the fungus */
|
||||||
|
+ return FALSE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
static void
|
||||||
|
doSomethingThenPrintHello(int test)
|
||||||
|
{
|
||||||
|
test = test << 4;
|
||||||
|
if (test % 8 == 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("goodbye\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (long) readOffset:(FILE*)file
|
||||||
|
{
|
||||||
|
if( version >= 11){
|
||||||
|
long offset;
|
||||||
|
fread(&offset, sizeof(long), 1, file);
|
||||||
|
return offset;
|
||||||
|
} else {
|
||||||
|
int offset;
|
||||||
|
fread(&offset-1, sizeof(int), 1, file);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL) isEdible:(NSString *)mushroom
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
--- functionname.in
|
||||||
|
+++ functionname_objcm.in
|
||||||
|
@@ -18,7 +18,7 @@ - (long) readOffset:(FILE*)file
|
||||||
|
return offset;
|
||||||
|
} else {
|
||||||
|
int offset;
|
||||||
|
- fread(&offset, sizeof(int), 1, file);
|
||||||
|
+ fread(&offset-1, sizeof(int), 1, file);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
<<<<<<< (local)
|
||||||
|
* And another bla
|
||||||
|
=======
|
||||||
|
* And another bla
|
||||||
|
>>>>>>> (stock)
|
||||||
|
*
|
||||||
|
<<<<<<< (local)
|
||||||
|
* And yet another
|
||||||
|
=======
|
||||||
|
* and yet another
|
||||||
|
>>>>>>> (stock)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
<<<<<<< (local)
|
||||||
|
=======
|
||||||
|
|
||||||
|
>>>>>>> (stock)
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
--- empty 2015-04-03 01:02:03.000000000 +0000
|
||||||
|
+++ hello 2016-12-22 11:22:33.000000000 +0000
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+hello
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
--- empty 2015-04-03 01:02:03.123456789 +0000
|
||||||
|
+++ hello 2016-12-22 11:22:33.987654321 +0000
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+hello
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
#ifndef PLOP
|
||||||
|
* And another bla
|
||||||
|
#else /* PLOP */
|
||||||
|
* And another bla
|
||||||
|
#endif /* PLOP */
|
||||||
|
*
|
||||||
|
#ifndef PLOP
|
||||||
|
* And yet another
|
||||||
|
#else /* PLOP */
|
||||||
|
* and yet another
|
||||||
|
#endif /* PLOP */
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
#ifdef PLOP
|
||||||
|
|
||||||
|
#endif /* PLOP */
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Simple input file designed
|
||||||
|
to be able to test diff
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Simple input file designed
|
||||||
|
and written
|
||||||
|
to be able to test diff utility
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
* And another bla
|
||||||
|
*
|
||||||
|
* And yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
* And another bla
|
||||||
|
*
|
||||||
|
* and yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
This
|
||||||
|
is my test file A
|
||||||
|
it has tabs
|
||||||
|
Thanks
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
This
|
||||||
|
is my test file B
|
||||||
|
it has tabs
|
||||||
|
Thanks
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
2c2,3
|
||||||
|
< to be able to test diff
|
||||||
|
---
|
||||||
|
> and written
|
||||||
|
> to be able to test diff utility
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
6c6
|
||||||
|
< * And yet another
|
||||||
|
---
|
||||||
|
> * and yet another
|
||||||
|
11a12
|
||||||
|
>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
2c
|
||||||
|
and written
|
||||||
|
to be able to test diff utility
|
||||||
|
.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
4c4
|
||||||
|
< * And another bla
|
||||||
|
---
|
||||||
|
> * And another bla
|
||||||
|
11a12
|
||||||
|
>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
d2 1
|
||||||
|
a2 2
|
||||||
|
and written
|
||||||
|
to be able to test diff utility
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
*** input_c1.in
|
||||||
|
--- input_c2.in
|
||||||
|
***************
|
||||||
|
*** 1,14 ****
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
! * And another bla
|
||||||
|
*
|
||||||
|
! * And yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
--- 1,15 ----
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
! * And another bla
|
||||||
|
*
|
||||||
|
! * and yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
--- input1
|
||||||
|
+++ input2
|
||||||
|
@@ -1,2 +1,3 @@
|
||||||
|
Simple input file designed
|
||||||
|
-to be able to test diff
|
||||||
|
+and written
|
||||||
|
+to be able to test diff utility
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
6c6
|
||||||
|
< * And yet another
|
||||||
|
---
|
||||||
|
> * and yet another
|
||||||
|
11a12
|
||||||
|
>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
--- 1
|
||||||
|
+++ 2
|
||||||
|
@@ -1,3 +1,3 @@
|
||||||
|
a
|
||||||
|
-X
|
||||||
|
+Y
|
||||||
|
c
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
--- input_c1.in
|
||||||
|
+++ input_c2.in
|
||||||
|
@@ -1,15 +1,16 @@
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
- * And another bla
|
||||||
|
+ * And another bla
|
||||||
|
*
|
||||||
|
- * And yet another
|
||||||
|
+ * and yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
*** input_c1.in
|
||||||
|
--- input_c2.in
|
||||||
|
***************
|
||||||
|
*** 1,15 ****
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
! * And another bla
|
||||||
|
*
|
||||||
|
! * And yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
--- 1,16 ----
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
! * And another bla
|
||||||
|
*
|
||||||
|
! * and yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
--- input_c1.in
|
||||||
|
+++ input_c2.in
|
||||||
|
@@ -1,14 +1,15 @@
|
||||||
|
/*
|
||||||
|
* A comment
|
||||||
|
*
|
||||||
|
- * And another bla
|
||||||
|
+ * And another bla
|
||||||
|
*
|
||||||
|
- * And yet another
|
||||||
|
+ * and yet another
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
printf("something");
|
||||||
|
|
||||||
|
return (0);
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/* $OpenBSD: xmalloc.c,v 1.10 2019/06/28 05:44:09 deraadt Exp $ */
|
||||||
|
/*
|
||||||
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
|
* All rights reserved
|
||||||
|
* Versions of malloc and friends that check their results, and never return
|
||||||
|
* failure (they call fatal if they encounter an error).
|
||||||
|
*
|
||||||
|
* As far as I am concerned, the code I have written for this software
|
||||||
|
* can be used freely for any purpose. Any derived versions of this
|
||||||
|
* software must be clearly marked as such, and if the derived work is
|
||||||
|
* incompatible with the protocol description in the RFC file, it must be
|
||||||
|
* called by a name other than "ssh" or "Secure Shell".
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "xmalloc.h"
|
||||||
|
|
||||||
|
void *
|
||||||
|
xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
errx(2, "xmalloc: zero size");
|
||||||
|
ptr = malloc(size);
|
||||||
|
if (ptr == NULL)
|
||||||
|
err(2, "xmalloc: allocating %zu bytes", size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xcalloc(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = calloc(nmemb, size);
|
||||||
|
if (ptr == NULL)
|
||||||
|
err(2, "xcalloc: allocating %zu * %zu bytes", nmemb, size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xreallocarray(void *ptr, size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *new_ptr;
|
||||||
|
|
||||||
|
new_ptr = reallocarray(ptr, nmemb, size);
|
||||||
|
if (new_ptr == NULL)
|
||||||
|
err(2, "xreallocarray: allocating %zu * %zu bytes",
|
||||||
|
nmemb, size);
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
xstrdup(const char *str)
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
if ((cp = strdup(str)) == NULL)
|
||||||
|
err(2, "xstrdup");
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
xasprintf(char **ret, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
i = vasprintf(ret, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (i == -1)
|
||||||
|
err(2, "xasprintf");
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/* $OpenBSD: xmalloc.h,v 1.4 2015/11/12 16:30:30 mmcc Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
|
* All rights reserved
|
||||||
|
* Created: Mon Mar 20 22:09:17 1995 ylo
|
||||||
|
*
|
||||||
|
* Versions of malloc and friends that check their results, and never return
|
||||||
|
* failure (they call fatal if they encounter an error).
|
||||||
|
*
|
||||||
|
* As far as I am concerned, the code I have written for this software
|
||||||
|
* can be used freely for any purpose. Any derived versions of this
|
||||||
|
* software must be clearly marked as such, and if the derived work is
|
||||||
|
* incompatible with the protocol description in the RFC file, it must be
|
||||||
|
* called by a name other than "ssh" or "Secure Shell".
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XMALLOC_H
|
||||||
|
#define XMALLOC_H
|
||||||
|
|
||||||
|
void *xmalloc(size_t);
|
||||||
|
void *xcalloc(size_t, size_t);
|
||||||
|
void *xreallocarray(void *, size_t, size_t);
|
||||||
|
char *xstrdup(const char *);
|
||||||
|
int xasprintf(char **, const char *, ...)
|
||||||
|
__attribute__((__format__ (printf, 2, 3)))
|
||||||
|
__attribute__((__nonnull__ (2)));
|
||||||
|
|
||||||
|
#endif /* XMALLOC_H */
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
.\" $OpenBSD: diff3.1,v 1.7 2007/05/31 19:20:09 jmc Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 1990, 1993, 1994
|
||||||
|
.\" The Regents of the University of California. All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
|
.\" modification, are permitted provided that the following conditions
|
||||||
|
.\" are met:
|
||||||
|
.\" 1. Redistributions of source code must retain the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer.
|
||||||
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer in the
|
||||||
|
.\" documentation and/or other materials provided with the distribution.
|
||||||
|
.\" 3. Neither the name of the University nor the names of its contributors
|
||||||
|
.\" may be used to endorse or promote products derived from this software
|
||||||
|
.\" without specific prior written permission.
|
||||||
|
.\"
|
||||||
|
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
.\" SUCH DAMAGE.
|
||||||
|
.\"
|
||||||
|
.\" @(#)diff3.1 8.2 (Berkeley) 4/18/94
|
||||||
|
.\"
|
||||||
|
.Dd June 23, 2022
|
||||||
|
.Dt DIFF3 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm diff3
|
||||||
|
.Nd 3-way differential file comparison
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl 3AaEeimTXx
|
||||||
|
.Op Fl Fl diff-program Ar program
|
||||||
|
.Op Fl Fl strip-trailing-cr
|
||||||
|
.Op Fl L | Fl Fl label Ar label1
|
||||||
|
.Op Fl L | Fl Fl label Ar label2
|
||||||
|
.Op Fl L | Fl Fl label Ar label3
|
||||||
|
.Ar file1 file2 file3
|
||||||
|
.Nm
|
||||||
|
.Op Fl Fl help
|
||||||
|
.Op Fl Fl version
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility compares the contents of three different versions of a file,
|
||||||
|
.Ar file1 ,
|
||||||
|
.Ar file2
|
||||||
|
and
|
||||||
|
.Ar file3 ,
|
||||||
|
writing the result to the standard output.
|
||||||
|
The options describe different methods of merging and
|
||||||
|
purging
|
||||||
|
the separate versions into a new file.
|
||||||
|
.Nm
|
||||||
|
is used by
|
||||||
|
.Xr rcs 1
|
||||||
|
to merge specific versions or create
|
||||||
|
new versions.
|
||||||
|
.Pp
|
||||||
|
The options are as follows:
|
||||||
|
.Bl -tag -width "-E, -X"
|
||||||
|
.It Fl 3 , Fl Fl easy-only
|
||||||
|
Produces an output script suitable for
|
||||||
|
.Xr ed 1
|
||||||
|
with changes
|
||||||
|
specific only to
|
||||||
|
.Ar file3 .
|
||||||
|
.It Fl A Fl Fl show-all
|
||||||
|
Output all changes, bracketing conflicts.
|
||||||
|
.It Fl a , Fl Fl text
|
||||||
|
Treat all files as ASCII.
|
||||||
|
.It Fl E , Fl Fl show-overlap
|
||||||
|
.It Fl X
|
||||||
|
Similar to
|
||||||
|
.Fl e
|
||||||
|
and
|
||||||
|
.Fl x ,
|
||||||
|
respectively, but treat overlapping changes (i.e., changes that would
|
||||||
|
be noted with ==== in the normal listing) differently.
|
||||||
|
The overlapping lines from both files will be inserted by the edit script,
|
||||||
|
bracketed by "<<<<<<" and ">>>>>>" lines.
|
||||||
|
.It Fl e , Fl Fl ed
|
||||||
|
Produces output in a form suitable as an input script for the
|
||||||
|
.Xr ed 1
|
||||||
|
utility.
|
||||||
|
The script may then be used to merge differences common between all
|
||||||
|
three files and differences specific to
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file3 .
|
||||||
|
In other words, the
|
||||||
|
.Fl e
|
||||||
|
option ignores differences specific to
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2 ,
|
||||||
|
and those specific to
|
||||||
|
.Ar file2
|
||||||
|
and
|
||||||
|
.Ar file3 .
|
||||||
|
It is useful for backing out changes specific to
|
||||||
|
.Ar file2
|
||||||
|
only.
|
||||||
|
.It Fl Fl help
|
||||||
|
Prints usage information and exits.
|
||||||
|
.It Fl i
|
||||||
|
Appends 'w' and 'q'
|
||||||
|
.Xr ed 1
|
||||||
|
commands.
|
||||||
|
.It Fl L , Fl Fl label
|
||||||
|
Defines labels to print instead of file names
|
||||||
|
.Ar file1 ,
|
||||||
|
.Ar file2
|
||||||
|
and
|
||||||
|
.Ar file3 .
|
||||||
|
.It Fl m, Fl Fl merge
|
||||||
|
Merge output instead of generating ed script.
|
||||||
|
.It Fl T, Fl Fl initial-tab
|
||||||
|
In the normal listing,
|
||||||
|
use a tab instead of two spaces
|
||||||
|
at the beginning of each line.
|
||||||
|
In modes that produce an
|
||||||
|
.Xr ed 1
|
||||||
|
script, this option changes nothing.
|
||||||
|
.It Fl x, Fl Fl overlap-only
|
||||||
|
Produces an output script suitable for
|
||||||
|
.Xr ed 1
|
||||||
|
with changes
|
||||||
|
specific only to all three versions.
|
||||||
|
.It Fl Fl diff-program Ar program
|
||||||
|
Use
|
||||||
|
.Ar program
|
||||||
|
instead of the default
|
||||||
|
.Xr diff 1
|
||||||
|
to compare files.
|
||||||
|
.It Fl Fl strip-trailing-cr
|
||||||
|
Strip trailing carriage return on input files.
|
||||||
|
.It Fl Fl version
|
||||||
|
Prints version information and exits.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Fl E
|
||||||
|
option is used by
|
||||||
|
.Tn RCS
|
||||||
|
.Xr merge 1
|
||||||
|
to ensure that overlapping changes in the merged files are preserved
|
||||||
|
and brought to someone's attention.
|
||||||
|
.Pp
|
||||||
|
For example, suppose lines 7-8 are changed in both
|
||||||
|
.Ar file1
|
||||||
|
and
|
||||||
|
.Ar file2 .
|
||||||
|
Applying the edit script generated by the command
|
||||||
|
.Pp
|
||||||
|
.Dl $ diff3 -E file1 file2 file3
|
||||||
|
.Pp
|
||||||
|
to
|
||||||
|
.Ar file1
|
||||||
|
results in the file:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
lines 1-6
|
||||||
|
of file1
|
||||||
|
<<<<<<< file1
|
||||||
|
lines 7-8
|
||||||
|
of file1
|
||||||
|
=======
|
||||||
|
lines 7-8
|
||||||
|
of file3
|
||||||
|
>>>>>>> file3
|
||||||
|
rest of file1
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
The default output of
|
||||||
|
.Nm
|
||||||
|
makes notation of the differences between all files, and those
|
||||||
|
differences specific to each pair of files.
|
||||||
|
The changes are described by the commands necessary for
|
||||||
|
.Xr ed 1
|
||||||
|
to create the desired target from the different versions.
|
||||||
|
See
|
||||||
|
.Xr diff 1
|
||||||
|
for a description of the commands.
|
||||||
|
.Bl -tag -width "====="
|
||||||
|
.It Li \&====
|
||||||
|
The lines beneath this notation are ranges of lines which are different
|
||||||
|
between all files.
|
||||||
|
.It \&==== Ns Va n
|
||||||
|
The lines beneath this notation are ranges of lines which are exclusively
|
||||||
|
different in file
|
||||||
|
.Va n .
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr diff 1 ,
|
||||||
|
.Xr ed 1 ,
|
||||||
|
.Xr merge 1 ,
|
||||||
|
.Xr rcs 1 ,
|
||||||
|
.Xr sdiff 1
|
||||||
|
.Sh HISTORY
|
||||||
|
A
|
||||||
|
.Nm
|
||||||
|
command appeared in
|
||||||
|
.At v7 .
|
||||||
|
.Sh BUGS
|
||||||
|
The
|
||||||
|
.Fl e
|
||||||
|
option cannot catch and change lines which have
|
||||||
|
.Ql \&.
|
||||||
|
as the first and only character on the line.
|
||||||
|
The resulting script will fail on that line
|
||||||
|
as
|
||||||
|
.Ql \&.
|
||||||
|
is an
|
||||||
|
.Xr ed 1
|
||||||
|
editing command.
|
||||||
@@ -0,0 +1,977 @@
|
|||||||
|
/* $OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Caldera International Inc. 2001-2002.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code and documentation must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed or owned by Caldera
|
||||||
|
* International, Inc.
|
||||||
|
* 4. Neither the name of Caldera International, Inc. nor the names of other
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
||||||
|
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
|
||||||
|
* INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1991, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)diff3.c 8.1 (Berkeley) 6/6/93
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#ifndef lint
|
||||||
|
static char sccsid[] = "@(#)diff3.c 8.1 (Berkeley) 6/6/93";
|
||||||
|
#endif
|
||||||
|
#endif /* not lint */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "from" is first in range of changed lines; "to" is last+1
|
||||||
|
* from=to=line after point of insertion for added lines.
|
||||||
|
*/
|
||||||
|
struct range {
|
||||||
|
int from;
|
||||||
|
int to;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct diff {
|
||||||
|
#define DIFF_TYPE2 2
|
||||||
|
#define DIFF_TYPE3 3
|
||||||
|
int type;
|
||||||
|
#if DEBUG
|
||||||
|
char *line;
|
||||||
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
/* Ranges as lines */
|
||||||
|
struct range old;
|
||||||
|
struct range new;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EFLAG_NONE 0
|
||||||
|
#define EFLAG_OVERLAP 1
|
||||||
|
#define EFLAG_NOOVERLAP 2
|
||||||
|
#define EFLAG_UNMERGED 3
|
||||||
|
|
||||||
|
static size_t szchanges;
|
||||||
|
|
||||||
|
static struct diff *d13;
|
||||||
|
static struct diff *d23;
|
||||||
|
/*
|
||||||
|
* "de" is used to gather editing scripts. These are later spewed out in
|
||||||
|
* reverse order. Its first element must be all zero, the "old" and "new"
|
||||||
|
* components of "de" contain line positions. Array overlap indicates which
|
||||||
|
* sections in "de" correspond to lines that are different in all three files.
|
||||||
|
*/
|
||||||
|
static struct diff *de;
|
||||||
|
static char *overlap;
|
||||||
|
static int overlapcnt;
|
||||||
|
static FILE *fp[3];
|
||||||
|
static int cline[3]; /* # of the last-read line in each file (0-2) */
|
||||||
|
/*
|
||||||
|
* The latest known correspondence between line numbers of the 3 files
|
||||||
|
* is stored in last[1-3];
|
||||||
|
*/
|
||||||
|
static int last[4];
|
||||||
|
static int Aflag, eflag, iflag, mflag, Tflag;
|
||||||
|
static int oflag; /* indicates whether to mark overlaps (-E or -X) */
|
||||||
|
static int strip_cr;
|
||||||
|
static char *f1mark, *f2mark, *f3mark;
|
||||||
|
static const char *oldmark = "<<<<<<<";
|
||||||
|
static const char *orgmark = "|||||||";
|
||||||
|
static const char *newmark = ">>>>>>>";
|
||||||
|
static const char *divider = "=======";
|
||||||
|
|
||||||
|
static bool duplicate(struct range *, struct range *);
|
||||||
|
static int edit(struct diff *, bool, int, int);
|
||||||
|
static char *getchange(FILE *);
|
||||||
|
static char *get_line(FILE *, size_t *);
|
||||||
|
static int readin(int fd, struct diff **);
|
||||||
|
static int skip(int, int, const char *);
|
||||||
|
static void change(int, struct range *, bool);
|
||||||
|
static void keep(int, struct range *);
|
||||||
|
static void merge(int, int);
|
||||||
|
static void prange(struct range *, bool);
|
||||||
|
static void repos(int);
|
||||||
|
static void edscript(int) __dead2;
|
||||||
|
static void Ascript(int) __dead2;
|
||||||
|
static void mergescript(int) __dead2;
|
||||||
|
static void increase(void);
|
||||||
|
static void usage(void);
|
||||||
|
static void printrange(FILE *, struct range *);
|
||||||
|
|
||||||
|
static const char diff3_version[] = "FreeBSD diff3 20220517";
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DIFFPROG_OPT,
|
||||||
|
STRIPCR_OPT,
|
||||||
|
HELP_OPT,
|
||||||
|
VERSION_OPT
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DIFF_PATH "/usr/bin/diff"
|
||||||
|
|
||||||
|
#define OPTIONS "3aAeEiL:mTxX"
|
||||||
|
static struct option longopts[] = {
|
||||||
|
{ "ed", no_argument, NULL, 'e' },
|
||||||
|
{ "show-overlap", no_argument, NULL, 'E' },
|
||||||
|
{ "overlap-only", no_argument, NULL, 'x' },
|
||||||
|
{ "initial-tab", no_argument, NULL, 'T' },
|
||||||
|
{ "text", no_argument, NULL, 'a' },
|
||||||
|
{ "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT },
|
||||||
|
{ "show-all", no_argument, NULL, 'A' },
|
||||||
|
{ "easy-only", no_argument, NULL, '3' },
|
||||||
|
{ "merge", no_argument, NULL, 'm' },
|
||||||
|
{ "label", required_argument, NULL, 'L' },
|
||||||
|
{ "diff-program", required_argument, NULL, DIFFPROG_OPT },
|
||||||
|
{ "help", no_argument, NULL, HELP_OPT},
|
||||||
|
{ "version", no_argument, NULL, VERSION_OPT}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
|
||||||
|
"[-L label3] file1 file2 file3\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
readin(int fd, struct diff **dd)
|
||||||
|
{
|
||||||
|
int a, b, c, d;
|
||||||
|
size_t i;
|
||||||
|
char kind, *p;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fdopen(fd, "r");
|
||||||
|
if (f == NULL)
|
||||||
|
err(2, "fdopen");
|
||||||
|
for (i = 0; (p = getchange(f)); i++) {
|
||||||
|
#if DEBUG
|
||||||
|
(*dd)[i].line = strdup(p);
|
||||||
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
if (i >= szchanges - 1)
|
||||||
|
increase();
|
||||||
|
a = b = (int)strtoimax(p, &p, 10);
|
||||||
|
if (*p == ',') {
|
||||||
|
p++;
|
||||||
|
b = (int)strtoimax(p, &p, 10);
|
||||||
|
}
|
||||||
|
kind = *p++;
|
||||||
|
c = d = (int)strtoimax(p, &p, 10);
|
||||||
|
if (*p == ',') {
|
||||||
|
p++;
|
||||||
|
d = (int)strtoimax(p, &p, 10);
|
||||||
|
}
|
||||||
|
if (kind == 'a')
|
||||||
|
a++;
|
||||||
|
if (kind == 'd')
|
||||||
|
c++;
|
||||||
|
b++;
|
||||||
|
d++;
|
||||||
|
(*dd)[i].old.from = a;
|
||||||
|
(*dd)[i].old.to = b;
|
||||||
|
(*dd)[i].new.from = c;
|
||||||
|
(*dd)[i].new.to = d;
|
||||||
|
}
|
||||||
|
if (i) {
|
||||||
|
(*dd)[i].old.from = (*dd)[i - 1].old.to;
|
||||||
|
(*dd)[i].new.from = (*dd)[i - 1].new.to;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t
|
||||||
|
diffexec(const char *diffprog, char **diffargv, int fd[])
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
switch ((pid = fork())) {
|
||||||
|
case 0:
|
||||||
|
close(fd[0]);
|
||||||
|
if (dup2(fd[1], STDOUT_FILENO) == -1)
|
||||||
|
err(2, "child could not duplicate descriptor");
|
||||||
|
close(fd[1]);
|
||||||
|
execvp(diffprog, diffargv);
|
||||||
|
err(2, "could not execute diff: %s", diffprog);
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
err(2, "could not fork");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(fd[1]);
|
||||||
|
return (pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
getchange(FILE *b)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
while ((line = get_line(b, NULL))) {
|
||||||
|
if (isdigit((unsigned char)line[0]))
|
||||||
|
return (line);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_line(FILE *b, size_t *n)
|
||||||
|
{
|
||||||
|
ssize_t len;
|
||||||
|
static char *buf = NULL;
|
||||||
|
static size_t bufsize = 0;
|
||||||
|
|
||||||
|
if ((len = getline(&buf, &bufsize, b)) < 0)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
|
||||||
|
buf[len - 2] = '\n';
|
||||||
|
buf[len - 1] = '\0';
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != NULL)
|
||||||
|
*n = len;
|
||||||
|
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
merge(int m1, int m2)
|
||||||
|
{
|
||||||
|
struct diff *d1, *d2, *d3;
|
||||||
|
int j, t1, t2;
|
||||||
|
bool dup = false;
|
||||||
|
|
||||||
|
d1 = d13;
|
||||||
|
d2 = d23;
|
||||||
|
j = 0;
|
||||||
|
|
||||||
|
while (t1 = d1 < d13 + m1, t2 = d2 < d23 + m2, t1 || t2) {
|
||||||
|
/* first file is different from the others */
|
||||||
|
if (!t2 || (t1 && d1->new.to < d2->new.from)) {
|
||||||
|
/* stuff peculiar to 1st file */
|
||||||
|
if (eflag == EFLAG_NONE) {
|
||||||
|
printf("====1\n");
|
||||||
|
change(1, &d1->old, false);
|
||||||
|
keep(2, &d1->new);
|
||||||
|
change(3, &d1->new, false);
|
||||||
|
}
|
||||||
|
d1++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* second file is different from others */
|
||||||
|
if (!t1 || (t2 && d2->new.to < d1->new.from)) {
|
||||||
|
if (eflag == EFLAG_NONE) {
|
||||||
|
printf("====2\n");
|
||||||
|
keep(1, &d2->new);
|
||||||
|
change(3, &d2->new, false);
|
||||||
|
change(2, &d2->old, false);
|
||||||
|
} else if (Aflag || mflag) {
|
||||||
|
// XXX-THJ: What does it mean for the second file to differ?
|
||||||
|
j = edit(d2, dup, j, DIFF_TYPE2);
|
||||||
|
}
|
||||||
|
d2++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Merge overlapping changes in first file
|
||||||
|
* this happens after extension (see below).
|
||||||
|
*/
|
||||||
|
if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
|
||||||
|
d1[1].old.from = d1->old.from;
|
||||||
|
d1[1].new.from = d1->new.from;
|
||||||
|
d1++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* merge overlapping changes in second */
|
||||||
|
if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
|
||||||
|
d2[1].old.from = d2->old.from;
|
||||||
|
d2[1].new.from = d2->new.from;
|
||||||
|
d2++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* stuff peculiar to third file or different in all */
|
||||||
|
if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
|
||||||
|
dup = duplicate(&d1->old, &d2->old);
|
||||||
|
/*
|
||||||
|
* dup = 0 means all files differ
|
||||||
|
* dup = 1 means files 1 and 2 identical
|
||||||
|
*/
|
||||||
|
if (eflag == EFLAG_NONE) {
|
||||||
|
printf("====%s\n", dup ? "3" : "");
|
||||||
|
change(1, &d1->old, dup);
|
||||||
|
change(2, &d2->old, false);
|
||||||
|
d3 = d1->old.to > d1->old.from ? d1 : d2;
|
||||||
|
change(3, &d3->new, false);
|
||||||
|
} else {
|
||||||
|
j = edit(d1, dup, j, DIFF_TYPE3);
|
||||||
|
}
|
||||||
|
dup = false;
|
||||||
|
d1++;
|
||||||
|
d2++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Overlapping changes from file 1 and 2; extend changes
|
||||||
|
* appropriately to make them coincide.
|
||||||
|
*/
|
||||||
|
if (d1->new.from < d2->new.from) {
|
||||||
|
d2->old.from -= d2->new.from - d1->new.from;
|
||||||
|
d2->new.from = d1->new.from;
|
||||||
|
} else if (d2->new.from < d1->new.from) {
|
||||||
|
d1->old.from -= d1->new.from - d2->new.from;
|
||||||
|
d1->new.from = d2->new.from;
|
||||||
|
}
|
||||||
|
if (d1->new.to > d2->new.to) {
|
||||||
|
d2->old.to += d1->new.to - d2->new.to;
|
||||||
|
d2->new.to = d1->new.to;
|
||||||
|
} else if (d2->new.to > d1->new.to) {
|
||||||
|
d1->old.to += d2->new.to - d1->new.to;
|
||||||
|
d1->new.to = d2->new.to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mflag)
|
||||||
|
mergescript(j);
|
||||||
|
else if (Aflag)
|
||||||
|
Ascript(j);
|
||||||
|
else if (eflag)
|
||||||
|
edscript(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The range of lines rold.from thru rold.to in file i is to be changed.
|
||||||
|
* It is to be printed only if it does not duplicate something to be
|
||||||
|
* printed later.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
change(int i, struct range *rold, bool dup)
|
||||||
|
{
|
||||||
|
|
||||||
|
printf("%d:", i);
|
||||||
|
last[i] = rold->to;
|
||||||
|
prange(rold, false);
|
||||||
|
if (dup)
|
||||||
|
return;
|
||||||
|
i--;
|
||||||
|
skip(i, rold->from, NULL);
|
||||||
|
skip(i, rold->to, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print the range of line numbers, rold.from thru rold.to, as n1,n2 or
|
||||||
|
* n1.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
prange(struct range *rold, bool delete)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (rold->to <= rold->from)
|
||||||
|
printf("%da\n", rold->from - 1);
|
||||||
|
else {
|
||||||
|
printf("%d", rold->from);
|
||||||
|
if (rold->to > rold->from + 1)
|
||||||
|
printf(",%d", rold->to - 1);
|
||||||
|
if (delete)
|
||||||
|
printf("d\n");
|
||||||
|
else
|
||||||
|
printf("c\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No difference was reported by diff between file 1 (or 2) and file 3,
|
||||||
|
* and an artificial dummy difference (trange) must be ginned up to
|
||||||
|
* correspond to the change reported in the other file.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
keep(int i, struct range *rnew)
|
||||||
|
{
|
||||||
|
int delta;
|
||||||
|
struct range trange;
|
||||||
|
|
||||||
|
delta = last[3] - last[i];
|
||||||
|
trange.from = rnew->from - delta;
|
||||||
|
trange.to = rnew->to - delta;
|
||||||
|
change(i, &trange, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* skip to just before line number from in file "i". If "pr" is non-NULL,
|
||||||
|
* print all skipped stuff with string pr as a prefix.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
skip(int i, int from, const char *pr)
|
||||||
|
{
|
||||||
|
size_t j, n;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
for (n = 0; cline[i] < from - 1; n += j) {
|
||||||
|
if ((line = get_line(fp[i], &j)) == NULL)
|
||||||
|
errx(EXIT_FAILURE, "logic error");
|
||||||
|
if (pr != NULL)
|
||||||
|
printf("%s%s", Tflag == 1 ? "\t" : pr, line);
|
||||||
|
cline[i]++;
|
||||||
|
}
|
||||||
|
return ((int) n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 or 0 according as the old range (in file 1) contains exactly
|
||||||
|
* the same data as the new range (in file 2).
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
duplicate(struct range *r1, struct range *r2)
|
||||||
|
{
|
||||||
|
int c, d;
|
||||||
|
int nchar;
|
||||||
|
int nline;
|
||||||
|
|
||||||
|
if (r1->to-r1->from != r2->to-r2->from)
|
||||||
|
return (0);
|
||||||
|
skip(0, r1->from, NULL);
|
||||||
|
skip(1, r2->from, NULL);
|
||||||
|
nchar = 0;
|
||||||
|
for (nline = 0; nline < r1->to - r1->from; nline++) {
|
||||||
|
do {
|
||||||
|
c = getc(fp[0]);
|
||||||
|
d = getc(fp[1]);
|
||||||
|
if (c == -1 && d == -1)
|
||||||
|
break;
|
||||||
|
if (c == -1 || d == -1)
|
||||||
|
errx(EXIT_FAILURE, "logic error");
|
||||||
|
nchar++;
|
||||||
|
if (c != d) {
|
||||||
|
repos(nchar);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
} while (c != '\n');
|
||||||
|
}
|
||||||
|
repos(nchar);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
repos(int nchar)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* collect an editing script for later regurgitation
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
edit(struct diff *diff, bool dup, int j, int difftype)
|
||||||
|
{
|
||||||
|
if (!(eflag == EFLAG_UNMERGED ||
|
||||||
|
(!dup && eflag == EFLAG_OVERLAP ) ||
|
||||||
|
(dup && eflag == EFLAG_NOOVERLAP))) {
|
||||||
|
return (j);
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
overlap[j] = !dup;
|
||||||
|
if (!dup)
|
||||||
|
overlapcnt++;
|
||||||
|
|
||||||
|
de[j].type = difftype;
|
||||||
|
#if DEBUG
|
||||||
|
de[j].line = strdup(diff->line);
|
||||||
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
de[j].old.from = diff->old.from;
|
||||||
|
de[j].old.to = diff->old.to;
|
||||||
|
de[j].new.from = diff->new.from;
|
||||||
|
de[j].new.to = diff->new.to;
|
||||||
|
return (j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printrange(FILE *p, struct range *r)
|
||||||
|
{
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
int i = 1;
|
||||||
|
ssize_t rlen = 0;
|
||||||
|
|
||||||
|
/* We haven't been asked to print anything */
|
||||||
|
if (r->from == r->to)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (r->from > r->to)
|
||||||
|
errx(EXIT_FAILURE, "invalid print range");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX-THJ: We read through all of the file for each range printed.
|
||||||
|
* This duplicates work and will probably impact performance on large
|
||||||
|
* files with lots of ranges.
|
||||||
|
*/
|
||||||
|
fseek(p, 0L, SEEK_SET);
|
||||||
|
while ((rlen = getline(&line, &len, p)) > 0) {
|
||||||
|
if (i >= r->from)
|
||||||
|
printf("%s", line);
|
||||||
|
if (++i > r->to - 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* regurgitate */
|
||||||
|
static void
|
||||||
|
edscript(int n)
|
||||||
|
{
|
||||||
|
bool delete;
|
||||||
|
struct range *new, *old;
|
||||||
|
|
||||||
|
for (; n > 0; n--) {
|
||||||
|
new = &de[n].new;
|
||||||
|
old = &de[n].old;
|
||||||
|
|
||||||
|
delete = (new->from == new->to);
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
prange(old, delete);
|
||||||
|
} else {
|
||||||
|
printf("%da\n", old->to - 1);
|
||||||
|
printf("%s\n", divider);
|
||||||
|
}
|
||||||
|
printrange(fp[2], new);
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
if (!delete)
|
||||||
|
printf(".\n");
|
||||||
|
} else {
|
||||||
|
printf("%s %s\n.\n", newmark, f3mark);
|
||||||
|
printf("%da\n%s %s\n.\n", old->from - 1,
|
||||||
|
oldmark, f1mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iflag)
|
||||||
|
printf("w\nq\n");
|
||||||
|
|
||||||
|
exit(eflag == EFLAG_NONE ? overlapcnt : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output an edit script to turn mine into yours, when there is a conflict
|
||||||
|
* between the 3 files bracket the changes. Regurgitate the diffs in reverse
|
||||||
|
* order to allow the ed script to track down where the lines are as changes
|
||||||
|
* are made.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Ascript(int n)
|
||||||
|
{
|
||||||
|
int startmark;
|
||||||
|
bool deletenew;
|
||||||
|
bool deleteold;
|
||||||
|
|
||||||
|
struct range *new, *old;
|
||||||
|
|
||||||
|
for (; n > 0; n--) {
|
||||||
|
new = &de[n].new;
|
||||||
|
old = &de[n].old;
|
||||||
|
deletenew = (new->from == new->to);
|
||||||
|
deleteold = (old->from == old->to);
|
||||||
|
|
||||||
|
if (de[n].type == DIFF_TYPE2) {
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
prange(old, deletenew);
|
||||||
|
printrange(fp[2], new);
|
||||||
|
} else {
|
||||||
|
startmark = new->to;
|
||||||
|
|
||||||
|
if (!deletenew)
|
||||||
|
startmark--;
|
||||||
|
|
||||||
|
printf("%da\n", startmark);
|
||||||
|
printf("%s %s\n", newmark, f3mark);
|
||||||
|
|
||||||
|
printf(".\n");
|
||||||
|
|
||||||
|
printf("%da\n", startmark -
|
||||||
|
(new->to - new->from));
|
||||||
|
printf("%s %s\n", oldmark, f2mark);
|
||||||
|
if (!deleteold)
|
||||||
|
printrange(fp[1], old);
|
||||||
|
printf("%s\n.\n", divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (de[n].type == DIFF_TYPE3) {
|
||||||
|
startmark = old->to - 1;
|
||||||
|
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
prange(old, deletenew);
|
||||||
|
printrange(fp[2], new);
|
||||||
|
} else {
|
||||||
|
printf("%da\n", startmark);
|
||||||
|
printf("%s %s\n", orgmark, f2mark);
|
||||||
|
|
||||||
|
if (deleteold) {
|
||||||
|
struct range r;
|
||||||
|
r.from = old->from-1;
|
||||||
|
r.to = new->to;
|
||||||
|
printrange(fp[1], &r);
|
||||||
|
} else
|
||||||
|
printrange(fp[1], old);
|
||||||
|
|
||||||
|
printf("%s\n", divider);
|
||||||
|
printrange(fp[2], new);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
if (!deletenew)
|
||||||
|
printf(".\n");
|
||||||
|
} else {
|
||||||
|
printf("%s %s\n.\n", newmark, f3mark);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go to the start of the conflict in original
|
||||||
|
* file and append lines
|
||||||
|
*/
|
||||||
|
printf("%da\n%s %s\n.\n",
|
||||||
|
startmark - (old->to - old->from),
|
||||||
|
oldmark, f1mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iflag)
|
||||||
|
printf("w\nq\n");
|
||||||
|
|
||||||
|
exit(overlapcnt > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output the merged file directly (don't generate an ed script). When
|
||||||
|
* regurgitating diffs we need to walk forward through the file and print any
|
||||||
|
* inbetween lines.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
mergescript(int i)
|
||||||
|
{
|
||||||
|
struct range r, *new, *old;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
r.from = 1;
|
||||||
|
r.to = 1;
|
||||||
|
|
||||||
|
for (n = 1; n < i+1; n++) {
|
||||||
|
new = &de[n].new;
|
||||||
|
old = &de[n].old;
|
||||||
|
|
||||||
|
/* print any lines leading up to here */
|
||||||
|
r.to = old->from;
|
||||||
|
printrange(fp[0], &r);
|
||||||
|
|
||||||
|
if (de[n].type == DIFF_TYPE2) {
|
||||||
|
printf("%s %s\n", oldmark, f2mark);
|
||||||
|
printrange(fp[1], old);
|
||||||
|
printf("%s\n", divider);
|
||||||
|
printrange(fp[2], new);
|
||||||
|
printf("%s %s\n", newmark, f3mark);
|
||||||
|
} else if (de[n].type == DIFF_TYPE3) {
|
||||||
|
if (!oflag || !overlap[n]) {
|
||||||
|
printrange(fp[2], new);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
printf("%s %s\n", oldmark, f1mark);
|
||||||
|
printrange(fp[0], old);
|
||||||
|
|
||||||
|
printf("%s %s\n", orgmark, f2mark);
|
||||||
|
if (old->from == old->to) {
|
||||||
|
struct range or;
|
||||||
|
or.from = old->from - 1;
|
||||||
|
or.to = new->to;
|
||||||
|
printrange(fp[1], &or);
|
||||||
|
} else
|
||||||
|
printrange(fp[1], old);
|
||||||
|
|
||||||
|
printf("%s\n", divider);
|
||||||
|
|
||||||
|
printrange(fp[2], new);
|
||||||
|
printf("%s %s\n", newmark, f3mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old->from == old->to)
|
||||||
|
r.from = new->to;
|
||||||
|
else
|
||||||
|
r.from = old->to;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Print from the final range to the end of 'myfile'. Any deletions or
|
||||||
|
* additions to this file should have been handled by now.
|
||||||
|
*
|
||||||
|
* If the ranges are the same we need to rewind a line.
|
||||||
|
* If the new range is 0 length (from == to), we need to use the old
|
||||||
|
* range.
|
||||||
|
*/
|
||||||
|
new = &de[n-1].new;
|
||||||
|
old = &de[n-1].old;
|
||||||
|
if ((old->from == new->from) &&
|
||||||
|
(old->to == new->to))
|
||||||
|
r.from--;
|
||||||
|
else if (new->from == new->to)
|
||||||
|
r.from = old->from;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the range is a 3 way merge then we need to skip a line in the
|
||||||
|
* trailing output.
|
||||||
|
*/
|
||||||
|
if (de[n-1].type == DIFF_TYPE3)
|
||||||
|
r.from++;
|
||||||
|
|
||||||
|
r.to = INT_MAX;
|
||||||
|
printrange(fp[0], &r);
|
||||||
|
exit(overlapcnt > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
increase(void)
|
||||||
|
{
|
||||||
|
struct diff *p;
|
||||||
|
char *q;
|
||||||
|
size_t newsz, incr;
|
||||||
|
|
||||||
|
/* are the memset(3) calls needed? */
|
||||||
|
newsz = szchanges == 0 ? 64 : 2 * szchanges;
|
||||||
|
incr = newsz - szchanges;
|
||||||
|
|
||||||
|
p = reallocarray(d13, newsz, sizeof(struct diff));
|
||||||
|
if (p == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
memset(p + szchanges, 0, incr * sizeof(struct diff));
|
||||||
|
d13 = p;
|
||||||
|
p = reallocarray(d23, newsz, sizeof(struct diff));
|
||||||
|
if (p == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
memset(p + szchanges, 0, incr * sizeof(struct diff));
|
||||||
|
d23 = p;
|
||||||
|
p = reallocarray(de, newsz, sizeof(struct diff));
|
||||||
|
if (p == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
memset(p + szchanges, 0, incr * sizeof(struct diff));
|
||||||
|
de = p;
|
||||||
|
q = reallocarray(overlap, newsz, sizeof(char));
|
||||||
|
if (q == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
memset(q + szchanges, 0, incr * sizeof(char));
|
||||||
|
overlap = q;
|
||||||
|
szchanges = newsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
diff3_main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ch, nblabels, status, m, n, i;
|
||||||
|
char *labels[] = { NULL, NULL, NULL };
|
||||||
|
const char *diffprog = DIFF_PATH;
|
||||||
|
char *file1, *file2, *file3;
|
||||||
|
char *diffargv[7];
|
||||||
|
int diffargc = 0;
|
||||||
|
int fd13[2], fd23[2];
|
||||||
|
pid_t pd13, pd23;
|
||||||
|
|
||||||
|
nblabels = 0;
|
||||||
|
eflag = EFLAG_NONE;
|
||||||
|
oflag = 0;
|
||||||
|
diffargv[diffargc++] = __DECONST(char *, diffprog);
|
||||||
|
while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case '3':
|
||||||
|
eflag = EFLAG_NOOVERLAP;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
diffargv[diffargc++] = __DECONST(char *, "-a");
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
Aflag = 1;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
eflag = EFLAG_UNMERGED;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
eflag = EFLAG_UNMERGED;
|
||||||
|
oflag = 1;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
iflag = 1;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
oflag = 1;
|
||||||
|
if (nblabels >= 3)
|
||||||
|
errx(2, "too many file label options");
|
||||||
|
labels[nblabels++] = optarg;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
Aflag = 1;
|
||||||
|
oflag = 1;
|
||||||
|
mflag = 1;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
Tflag = 1;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
eflag = EFLAG_OVERLAP;
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
oflag = 1;
|
||||||
|
eflag = EFLAG_OVERLAP;
|
||||||
|
break;
|
||||||
|
case DIFFPROG_OPT:
|
||||||
|
diffprog = optarg;
|
||||||
|
break;
|
||||||
|
case STRIPCR_OPT:
|
||||||
|
strip_cr = 1;
|
||||||
|
diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr");
|
||||||
|
break;
|
||||||
|
case HELP_OPT:
|
||||||
|
usage();
|
||||||
|
exit(0);
|
||||||
|
case VERSION_OPT:
|
||||||
|
printf("%s\n", diff3_version);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (Aflag) {
|
||||||
|
eflag = EFLAG_UNMERGED;
|
||||||
|
oflag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
usage();
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO stdio */
|
||||||
|
file1 = argv[0];
|
||||||
|
file2 = argv[1];
|
||||||
|
file3 = argv[2];
|
||||||
|
|
||||||
|
if (oflag) {
|
||||||
|
asprintf(&f1mark, "%s",
|
||||||
|
labels[0] != NULL ? labels[0] : file1);
|
||||||
|
if (f1mark == NULL)
|
||||||
|
err(2, "asprintf");
|
||||||
|
asprintf(&f2mark, "%s",
|
||||||
|
labels[1] != NULL ? labels[1] : file2);
|
||||||
|
if (f2mark == NULL)
|
||||||
|
err(2, "asprintf");
|
||||||
|
asprintf(&f3mark, "%s",
|
||||||
|
labels[2] != NULL ? labels[2] : file3);
|
||||||
|
if (f3mark == NULL)
|
||||||
|
err(2, "asprintf");
|
||||||
|
}
|
||||||
|
fp[0] = fopen(file1, "r");
|
||||||
|
if (fp[0] == NULL)
|
||||||
|
err(2, "Can't open %s", file1);
|
||||||
|
|
||||||
|
fp[1] = fopen(file2, "r");
|
||||||
|
if (fp[1] == NULL)
|
||||||
|
err(2, "Can't open %s", file2);
|
||||||
|
|
||||||
|
fp[2] = fopen(file3, "r");
|
||||||
|
if (fp[2] == NULL)
|
||||||
|
err(2, "Can't open %s", file3);
|
||||||
|
|
||||||
|
if (pipe(fd13))
|
||||||
|
err(2, "pipe");
|
||||||
|
if (pipe(fd23))
|
||||||
|
err(2, "pipe");
|
||||||
|
|
||||||
|
diffargv[diffargc] = file1;
|
||||||
|
diffargv[diffargc + 1] = file3;
|
||||||
|
diffargv[diffargc + 2] = NULL;
|
||||||
|
|
||||||
|
pd13 = diffexec(diffprog, diffargv, fd13);
|
||||||
|
|
||||||
|
diffargv[diffargc] = file2;
|
||||||
|
pd23 = diffexec(diffprog, diffargv, fd23);
|
||||||
|
|
||||||
|
/* parse diffs */
|
||||||
|
increase();
|
||||||
|
m = readin(fd13[0], &d13);
|
||||||
|
n = readin(fd23[0], &d23);
|
||||||
|
|
||||||
|
/* wait for diff children */
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
pid_t pid = (i == 0) ? pd13 : pd23;
|
||||||
|
if (waitpid(pid, &status, 0) == -1)
|
||||||
|
err(2, "waitpid");
|
||||||
|
if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
|
||||||
|
errx(2, "diff exited abnormally");
|
||||||
|
else if (WIFSIGNALED(status))
|
||||||
|
errx(2, "diff killed by signal %d",
|
||||||
|
WTERMSIG(status));
|
||||||
|
}
|
||||||
|
merge(m, n);
|
||||||
|
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
====
|
||||||
|
1:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
2:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
3:2c
|
||||||
|
and a few typ0s to fix
|
||||||
|
====2
|
||||||
|
1:4a
|
||||||
|
3:3a
|
||||||
|
2:5c
|
||||||
|
New line
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
This is a more complete test
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
also I plan to add few lines
|
||||||
|
and to remove
|
||||||
|
other lines
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
This is a more complete test
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
also I plan to add few lines
|
||||||
|
and to remove
|
||||||
|
other lines
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
====
|
||||||
|
1:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
2:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
3:2c
|
||||||
|
and a few typ0s to fix
|
||||||
|
====2
|
||||||
|
1:4a
|
||||||
|
3:3a
|
||||||
|
2:5c
|
||||||
|
New line
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
2,3c
|
||||||
|
and a few typ0s to fix
|
||||||
|
.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
This is a more complete test
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
also I plan to add few lines
|
||||||
|
New line
|
||||||
|
and to remove
|
||||||
|
other lines
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
3a
|
||||||
|
=======
|
||||||
|
and a few typ0s to fix
|
||||||
|
>>>>>>> 3
|
||||||
|
.
|
||||||
|
1a
|
||||||
|
<<<<<<< 1
|
||||||
|
.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
This is a more complete test
|
||||||
|
and a few typ0s to fix
|
||||||
|
also I plan to add few lines
|
||||||
|
and to remove
|
||||||
|
other lines
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
3a
|
||||||
|
=======
|
||||||
|
and a few typ0s to fix
|
||||||
|
>>>>>>> 3
|
||||||
|
.
|
||||||
|
1a
|
||||||
|
<<<<<<< 1
|
||||||
|
.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
2,3c
|
||||||
|
and a few typ0s to fix
|
||||||
|
.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
====
|
||||||
|
1:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
2:2,3c
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
3:2c
|
||||||
|
and a few typ0s to fix
|
||||||
|
====2
|
||||||
|
1:4a
|
||||||
|
3:3a
|
||||||
|
2:5c
|
||||||
|
New line
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
4a
|
||||||
|
>>>>>>> 3
|
||||||
|
.
|
||||||
|
4a
|
||||||
|
<<<<<<< 2
|
||||||
|
New line
|
||||||
|
=======
|
||||||
|
.
|
||||||
|
3a
|
||||||
|
||||||| 2
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
=======
|
||||||
|
and a few typ0s to fix
|
||||||
|
>>>>>>> 3
|
||||||
|
.
|
||||||
|
1a
|
||||||
|
<<<<<<< 1
|
||||||
|
.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
This is a more complete test
|
||||||
|
<<<<<<< 1
|
||||||
|
which multiple lines
|
||||||
|
and a few typ0s to fix
|
||||||
|
||||||| 2
|
||||||
|
which multiple lines
|
||||||
|
and a few typos to fix
|
||||||
|
=======
|
||||||
|
and a few typ0s to fix
|
||||||
|
>>>>>>> 3
|
||||||
|
also I plan to add few lines
|
||||||
|
<<<<<<< 2
|
||||||
|
New line
|
||||||
|
=======
|
||||||
|
>>>>>>> 3
|
||||||
|
and to remove
|
||||||
|
other lines
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
PACKAGE= tests
|
||||||
|
|
||||||
|
ATF_TESTS_SH= diff3_test
|
||||||
|
${PACKAGE}FILES+= \
|
||||||
|
1.txt \
|
||||||
|
1cr.txt \
|
||||||
|
2.txt \
|
||||||
|
3.txt \
|
||||||
|
long-m.txt \
|
||||||
|
long-o.txt \
|
||||||
|
long-y.txt \
|
||||||
|
1.out \
|
||||||
|
1t.out \
|
||||||
|
2.out \
|
||||||
|
3.out \
|
||||||
|
4.out \
|
||||||
|
5.out \
|
||||||
|
6.out \
|
||||||
|
7.out \
|
||||||
|
8.out \
|
||||||
|
9.out \
|
||||||
|
long-ed.out \
|
||||||
|
long-A.out \
|
||||||
|
long-merge.out \
|
||||||
|
|
||||||
|
.include <bsd.test.mk>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Autogenerated - do NOT edit!
|
||||||
|
|
||||||
|
DIRDEPS = \
|
||||||
|
|
||||||
|
|
||||||
|
.include <dirdeps.mk>
|
||||||
|
|
||||||
|
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||||
|
# local dependencies - needed for -jN in clean tree
|
||||||
|
.endif
|
||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
atf_test_case diff3
|
||||||
|
atf_test_case diff3_lesssimple
|
||||||
|
atf_test_case diff3_ed
|
||||||
|
atf_test_case diff3_A
|
||||||
|
atf_test_case diff3_merge
|
||||||
|
|
||||||
|
diff3_body()
|
||||||
|
{
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/1.out \
|
||||||
|
diff3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/1.out \
|
||||||
|
diff3 --strip-trailing-cr $(atf_get_srcdir)/1cr.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/1t.out \
|
||||||
|
diff3 -T $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/2.out \
|
||||||
|
diff3 -e $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/3.out \
|
||||||
|
diff3 -E -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/4.out \
|
||||||
|
diff3 -X -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/5.out \
|
||||||
|
diff3 -x $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/6.out \
|
||||||
|
diff3 -3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -o file:$(atf_get_srcdir)/7.out \
|
||||||
|
diff3 -i $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
diff3_lesssimple_body()
|
||||||
|
{
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/10.out \
|
||||||
|
diff3 -m -L 1 -L 2 -L 3 $(atf_get_srcdir)/4.txt $(atf_get_srcdir)/5.txt $(atf_get_srcdir)/6.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
diff3_ed_body()
|
||||||
|
{
|
||||||
|
atf_check -s exit:0 -o file:$(atf_get_srcdir)/long-ed.out \
|
||||||
|
diff3 -e $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
diff3_A_body()
|
||||||
|
{
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/8.out \
|
||||||
|
diff3 -A -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/long-A.out \
|
||||||
|
diff3 -A -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
diff3_merge_body()
|
||||||
|
{
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/9.out \
|
||||||
|
diff3 -m -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt
|
||||||
|
|
||||||
|
atf_check -s exit:1 -o file:$(atf_get_srcdir)/long-merge.out \
|
||||||
|
diff3 -m -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
atf_init_test_cases()
|
||||||
|
{
|
||||||
|
atf_add_test_case diff3
|
||||||
|
# atf_add_test_case diff3_lesssimple
|
||||||
|
atf_add_test_case diff3_ed
|
||||||
|
atf_add_test_case diff3_A
|
||||||
|
atf_add_test_case diff3_merge
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
24d
|
||||||
|
16a
|
||||||
|
||||||| long-o.txt
|
||||||
|
This line is different in yours and mine, but the different in each
|
||||||
|
=======
|
||||||
|
This line is different in yours and mine, but the best in yours
|
||||||
|
>>>>>>> long-y.txt
|
||||||
|
.
|
||||||
|
15a
|
||||||
|
<<<<<<< long-m.txt
|
||||||
|
.
|
||||||
|
11a
|
||||||
|
>>>>>>> long-y.txt
|
||||||
|
.
|
||||||
|
10a
|
||||||
|
<<<<<<< long-o.txt
|
||||||
|
This line is different in yours and mine, but the same
|
||||||
|
=======
|
||||||
|
.
|
||||||
|
8c
|
||||||
|
This line is different in yours, much butter
|
||||||
|
.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
24d
|
||||||
|
16c
|
||||||
|
This line is different in yours and mine, but the best in yours
|
||||||
|
.
|
||||||
|
8c
|
||||||
|
This line is different in yours, much butter
|
||||||
|
.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
This is a long file
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in mine, not better
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, the same is in both
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, best change in mine
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is deleted in yours
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
This is a long file
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in mine, not better
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours, much butter
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
<<<<<<< long-o.txt
|
||||||
|
This line is different in yours and mine, but the same
|
||||||
|
=======
|
||||||
|
This line is different in yours and mine, the same is in both
|
||||||
|
>>>>>>> long-y.txt
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
<<<<<<< long-m.txt
|
||||||
|
This line is different in yours and mine, best change in mine
|
||||||
|
||||||| long-o.txt
|
||||||
|
This line is different in yours and mine, but the different in each
|
||||||
|
=======
|
||||||
|
This line is different in yours and mine, but the best in yours
|
||||||
|
>>>>>>> long-y.txt
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
This is a long file
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in mine
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, but the same
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, but the different in each
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is deleted in mine
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is deleted in yours
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
This is a long file
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in mine
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours, much butter
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, the same is in both
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is different in yours and mine, but the best in yours
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
This line is deleted in mine
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
|
These lines are the same in all three files
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)extern.h 8.3 (Berkeley) 4/16/94
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
void brace_subst(char *, char **, char *, size_t);
|
||||||
|
PLAN *find_create(char ***);
|
||||||
|
int find_execute(PLAN *, char **);
|
||||||
|
PLAN *find_formplan(char **);
|
||||||
|
PLAN *not_squish(PLAN *);
|
||||||
|
PLAN *or_squish(PLAN *);
|
||||||
|
PLAN *paren_squish(PLAN *);
|
||||||
|
time_t get_date(char *);
|
||||||
|
struct stat;
|
||||||
|
void printlong(char *, char *, struct stat *);
|
||||||
|
int queryuser(char **);
|
||||||
|
OPTION *lookup_option(const char *);
|
||||||
|
void finish_execplus(void);
|
||||||
|
|
||||||
|
creat_f c_Xmin;
|
||||||
|
creat_f c_Xtime;
|
||||||
|
creat_f c_acl;
|
||||||
|
creat_f c_and;
|
||||||
|
creat_f c_delete;
|
||||||
|
creat_f c_depth;
|
||||||
|
creat_f c_daystart;
|
||||||
|
creat_f c_empty;
|
||||||
|
creat_f c_exec;
|
||||||
|
creat_f c_flags;
|
||||||
|
creat_f c_follow;
|
||||||
|
creat_f c_fstype;
|
||||||
|
creat_f c_group;
|
||||||
|
creat_f c_ignore_readdir_race;
|
||||||
|
creat_f c_inum;
|
||||||
|
creat_f c_links;
|
||||||
|
creat_f c_ls;
|
||||||
|
creat_f c_mXXdepth;
|
||||||
|
creat_f c_name;
|
||||||
|
creat_f c_newer;
|
||||||
|
creat_f c_nogroup;
|
||||||
|
creat_f c_nouser;
|
||||||
|
creat_f c_perm;
|
||||||
|
creat_f c_print;
|
||||||
|
creat_f c_printf;
|
||||||
|
creat_f c_regex;
|
||||||
|
creat_f c_samefile;
|
||||||
|
creat_f c_simple;
|
||||||
|
creat_f c_size;
|
||||||
|
creat_f c_sparse;
|
||||||
|
creat_f c_type;
|
||||||
|
creat_f c_user;
|
||||||
|
creat_f c_xdev;
|
||||||
|
|
||||||
|
exec_f f_Xmin;
|
||||||
|
exec_f f_Xtime;
|
||||||
|
exec_f f_acl;
|
||||||
|
exec_f f_always_true;
|
||||||
|
exec_f f_closeparen;
|
||||||
|
exec_f f_delete;
|
||||||
|
exec_f f_depth;
|
||||||
|
exec_f f_empty;
|
||||||
|
exec_f f_exec;
|
||||||
|
exec_f f_executable;
|
||||||
|
exec_f f_expr;
|
||||||
|
exec_f f_false;
|
||||||
|
exec_f f_flags;
|
||||||
|
exec_f f_fstype;
|
||||||
|
exec_f f_group;
|
||||||
|
exec_f f_inum;
|
||||||
|
exec_f f_links;
|
||||||
|
exec_f f_ls;
|
||||||
|
exec_f f_name;
|
||||||
|
exec_f f_newer;
|
||||||
|
exec_f f_nogroup;
|
||||||
|
exec_f f_not;
|
||||||
|
exec_f f_nouser;
|
||||||
|
exec_f f_openparen;
|
||||||
|
exec_f f_or;
|
||||||
|
exec_f f_path;
|
||||||
|
exec_f f_perm;
|
||||||
|
exec_f f_print;
|
||||||
|
exec_f f_print0;
|
||||||
|
exec_f f_printf;
|
||||||
|
exec_f f_prune;
|
||||||
|
exec_f f_quit;
|
||||||
|
exec_f f_readable;
|
||||||
|
exec_f f_regex;
|
||||||
|
exec_f f_size;
|
||||||
|
exec_f f_sparse;
|
||||||
|
exec_f f_type;
|
||||||
|
exec_f f_user;
|
||||||
|
exec_f f_writable;
|
||||||
|
|
||||||
|
extern int ftsoptions, ignore_readdir_race, isdepth, isoutput;
|
||||||
|
extern int issort, isxargs;
|
||||||
|
extern int daystart;
|
||||||
|
extern int mindepth, maxdepth;
|
||||||
|
extern int regexp_flags;
|
||||||
|
extern int exitstatus;
|
||||||
|
extern time_t now;
|
||||||
|
extern int dotfd;
|
||||||
|
extern FTS *tree;
|
||||||
|
extern volatile sig_atomic_t showinfo;
|
||||||
+1206
File diff suppressed because it is too large
Load Diff
+243
@@ -0,0 +1,243 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to Berkeley by
|
||||||
|
* Cimarron D. Taylor of the University of California, Berkeley.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fts.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "find.h"
|
||||||
|
|
||||||
|
static int find_compare(const FTSENT **s1, const FTSENT **s2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find_compare --
|
||||||
|
* tell fts_open() how to order the traversal of the hierarchy.
|
||||||
|
* This variant gives lexicographical order, i.e., alphabetical
|
||||||
|
* order within each directory.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
find_compare(const FTSENT **s1, const FTSENT **s2)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (strcoll((*s1)->fts_name, (*s2)->fts_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find_formplan --
|
||||||
|
* process the command line and create a "plan" corresponding to the
|
||||||
|
* command arguments.
|
||||||
|
*/
|
||||||
|
PLAN *
|
||||||
|
find_formplan(char *argv[])
|
||||||
|
{
|
||||||
|
PLAN *plan, *tail, *new;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* for each argument in the command line, determine what kind of node
|
||||||
|
* it is, create the appropriate node type and add the new plan node
|
||||||
|
* to the end of the existing plan. The resulting plan is a linked
|
||||||
|
* list of plan nodes. For example, the string:
|
||||||
|
*
|
||||||
|
* % find . -name foo -newer bar -print
|
||||||
|
*
|
||||||
|
* results in the plan:
|
||||||
|
*
|
||||||
|
* [-name foo]--> [-newer bar]--> [-print]
|
||||||
|
*
|
||||||
|
* in this diagram, `[-name foo]' represents the plan node generated
|
||||||
|
* by c_name() with an argument of foo and `-->' represents the
|
||||||
|
* plan->next pointer.
|
||||||
|
*/
|
||||||
|
for (plan = tail = NULL; *argv;) {
|
||||||
|
if (!(new = find_create(&argv)))
|
||||||
|
continue;
|
||||||
|
if (plan == NULL)
|
||||||
|
tail = plan = new;
|
||||||
|
else {
|
||||||
|
tail->next = new;
|
||||||
|
tail = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the user didn't specify one of -print, -ok or -exec, then -print
|
||||||
|
* is assumed so we bracket the current expression with parens, if
|
||||||
|
* necessary, and add a -print node on the end.
|
||||||
|
*/
|
||||||
|
if (!isoutput) {
|
||||||
|
OPTION *p;
|
||||||
|
char **argv1 = 0;
|
||||||
|
|
||||||
|
if (plan == NULL) {
|
||||||
|
p = lookup_option("-print");
|
||||||
|
new = (p->create)(p, &argv1);
|
||||||
|
tail = plan = new;
|
||||||
|
} else {
|
||||||
|
p = lookup_option("(");
|
||||||
|
new = (p->create)(p, &argv1);
|
||||||
|
new->next = plan;
|
||||||
|
plan = new;
|
||||||
|
p = lookup_option(")");
|
||||||
|
new = (p->create)(p, &argv1);
|
||||||
|
tail->next = new;
|
||||||
|
tail = new;
|
||||||
|
p = lookup_option("-print");
|
||||||
|
new = (p->create)(p, &argv1);
|
||||||
|
tail->next = new;
|
||||||
|
tail = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the command line has been completely processed into a search plan
|
||||||
|
* except for the (, ), !, and -o operators. Rearrange the plan so
|
||||||
|
* that the portions of the plan which are affected by the operators
|
||||||
|
* are moved into operator nodes themselves. For example:
|
||||||
|
*
|
||||||
|
* [!]--> [-name foo]--> [-print]
|
||||||
|
*
|
||||||
|
* becomes
|
||||||
|
*
|
||||||
|
* [! [-name foo] ]--> [-print]
|
||||||
|
*
|
||||||
|
* and
|
||||||
|
*
|
||||||
|
* [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
|
||||||
|
*
|
||||||
|
* becomes
|
||||||
|
*
|
||||||
|
* [expr [-depth]-->[-name foo] ]--> [-print]
|
||||||
|
*
|
||||||
|
* operators are handled in order of precedence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plan = paren_squish(plan); /* ()'s */
|
||||||
|
plan = not_squish(plan); /* !'s */
|
||||||
|
plan = or_squish(plan); /* -o's */
|
||||||
|
return (plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
FTS *tree; /* pointer to top of FTS hierarchy */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find_execute --
|
||||||
|
* take a search plan and an array of search paths and executes the plan
|
||||||
|
* over all FTSENT's returned for the given search paths.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
find_execute(PLAN *plan, char *paths[])
|
||||||
|
{
|
||||||
|
FTSENT *entry;
|
||||||
|
PLAN *p;
|
||||||
|
size_t counter = 0;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
tree = fts_open(paths, ftsoptions, (issort ? find_compare : NULL));
|
||||||
|
if (tree == NULL)
|
||||||
|
err(1, "ftsopen");
|
||||||
|
|
||||||
|
exitstatus = 0;
|
||||||
|
while (errno = 0, (entry = fts_read(tree)) != NULL) {
|
||||||
|
if (maxdepth != -1 && entry->fts_level >= maxdepth) {
|
||||||
|
if (fts_set(tree, entry, FTS_SKIP))
|
||||||
|
err(1, "%s", entry->fts_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (entry->fts_info) {
|
||||||
|
case FTS_D:
|
||||||
|
if (isdepth)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case FTS_DP:
|
||||||
|
if (!isdepth)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case FTS_DNR:
|
||||||
|
case FTS_NS:
|
||||||
|
if (ignore_readdir_race &&
|
||||||
|
entry->fts_errno == ENOENT && entry->fts_level > 0)
|
||||||
|
continue;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case FTS_ERR:
|
||||||
|
(void)fflush(stdout);
|
||||||
|
warnx("%s: %s",
|
||||||
|
entry->fts_path, strerror(entry->fts_errno));
|
||||||
|
exitstatus = 1;
|
||||||
|
continue;
|
||||||
|
#if defined(FTS_W) && defined(FTS_WHITEOUT)
|
||||||
|
case FTS_W:
|
||||||
|
if (ftsoptions & FTS_WHITEOUT)
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
#endif /* FTS_W */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showinfo) {
|
||||||
|
fprintf(stderr, "Scanning: %s\n", entry->fts_path);
|
||||||
|
fprintf(stderr, "Scanned: %zu\n\n", counter);
|
||||||
|
showinfo = 0;
|
||||||
|
}
|
||||||
|
++counter;
|
||||||
|
|
||||||
|
#define BADCH " \t\n\\'\""
|
||||||
|
if (isxargs && strpbrk(entry->fts_path, BADCH)) {
|
||||||
|
(void)fflush(stdout);
|
||||||
|
warnx("%s: illegal path", entry->fts_path);
|
||||||
|
exitstatus = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mindepth != -1 && entry->fts_level < mindepth)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call all the functions in the execution plan until one is
|
||||||
|
* false or all have been executed. This is where we do all
|
||||||
|
* the work specified by the user on the command line.
|
||||||
|
*/
|
||||||
|
for (p = plan; p && (p->execute)(p, entry); p = p->next);
|
||||||
|
}
|
||||||
|
e = errno;
|
||||||
|
finish_execplus();
|
||||||
|
if (e && (!ignore_readdir_race || e != ENOENT))
|
||||||
|
errc(1, e, "fts_read");
|
||||||
|
return (exitstatus);
|
||||||
|
}
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 1990, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to Berkeley by
|
||||||
|
* Cimarron D. Taylor of the University of California, Berkeley.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)find.h 8.1 (Berkeley) 6/6/93
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/vfs.h>
|
||||||
|
#include <sys/statfs.h>
|
||||||
|
#define HAVE_STRUCT_STAT_ST_FLAGS 0
|
||||||
|
#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 /* via statx() */
|
||||||
|
#define HAVE_STRUCT_STATFS_F_FSTYPENAME 0
|
||||||
|
#define HAVE_LINUX_STATX 1
|
||||||
|
#else
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#ifdef UF_SETTABLE
|
||||||
|
#define HAVE_STRUCT_STAT_ST_FLAGS 1
|
||||||
|
#else
|
||||||
|
#define HAVE_STRUCT_STAT_ST_FLAGS 0
|
||||||
|
#endif
|
||||||
|
#if defined(st_birthtime) || defined(st_birthtimespec)
|
||||||
|
#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
|
||||||
|
#else
|
||||||
|
#define HAVE_STRUCT_STAT_ST_BIRTHTIME 0
|
||||||
|
#endif
|
||||||
|
#if defined(MFSNAMELEN) || defined(MFSTYPENAMELEN)
|
||||||
|
#define HAVE_STRUCT_STATFS_F_FSTYPENAME 1
|
||||||
|
#else
|
||||||
|
#define HAVE_STRUCT_STATFS_F_FSTYPENAME 0
|
||||||
|
#endif
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
#ifndef HAVE_LINUX_STATX
|
||||||
|
#define HAVE_LINUX_STATX 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* forward declarations */
|
||||||
|
struct _plandata;
|
||||||
|
struct _option;
|
||||||
|
|
||||||
|
/* execute function */
|
||||||
|
typedef int exec_f(struct _plandata *, FTSENT *);
|
||||||
|
/* create function */
|
||||||
|
typedef struct _plandata *creat_f(struct _option *, char ***);
|
||||||
|
|
||||||
|
/* function modifiers */
|
||||||
|
#define F_NEEDOK 0x00000001 /* -ok vs. -exec */
|
||||||
|
#define F_EXECDIR 0x00000002 /* -execdir vs. -exec */
|
||||||
|
#define F_TIME_A 0x00000004 /* one of -atime, -anewer, -newera* */
|
||||||
|
#define F_TIME_C 0x00000008 /* one of -ctime, -cnewer, -newerc* */
|
||||||
|
#define F_TIME2_A 0x00000010 /* one of -newer?a */
|
||||||
|
#define F_TIME2_C 0x00000020 /* one of -newer?c */
|
||||||
|
#define F_TIME2_T 0x00000040 /* one of -newer?t */
|
||||||
|
#define F_MAXDEPTH F_TIME_A /* maxdepth vs. mindepth */
|
||||||
|
#define F_DEPTH F_TIME_A /* -depth n vs. -d */
|
||||||
|
/* command line function modifiers */
|
||||||
|
#define F_EQUAL 0x00000000 /* [acm]min [acm]time inum links size */
|
||||||
|
#define F_LESSTHAN 0x00000100
|
||||||
|
#define F_GREATER 0x00000200
|
||||||
|
#define F_ELG_MASK 0x00000300
|
||||||
|
#define F_ATLEAST 0x00000400 /* flags perm */
|
||||||
|
#define F_ANY 0x00000800 /* perm */
|
||||||
|
#define F_MTMASK 0x00003000
|
||||||
|
#define F_MTFLAG 0x00000000 /* fstype */
|
||||||
|
#define F_MTTYPE 0x00001000
|
||||||
|
#define F_MTUNKNOWN 0x00002000
|
||||||
|
#define F_IGNCASE 0x00010000 /* iname ipath iregex */
|
||||||
|
#define F_EXACTTIME F_IGNCASE /* -[acm]time units syntax */
|
||||||
|
#define F_EXECPLUS 0x00020000 /* -exec ... {} + */
|
||||||
|
#if HAVE_STRUCT_STAT_ST_BIRTHTIME
|
||||||
|
#define F_TIME_B 0x00040000 /* one of -Btime, -Bnewer, -newerB* */
|
||||||
|
#define F_TIME2_B 0x00080000 /* one of -newer?B */
|
||||||
|
#endif
|
||||||
|
#define F_LINK 0x00100000 /* lname or ilname */
|
||||||
|
|
||||||
|
/* node definition */
|
||||||
|
typedef struct _plandata {
|
||||||
|
struct _plandata *next; /* next node */
|
||||||
|
exec_f *execute; /* node evaluation function */
|
||||||
|
int flags; /* private flags */
|
||||||
|
union {
|
||||||
|
gid_t _g_data; /* gid */
|
||||||
|
ino_t _i_data; /* inode */
|
||||||
|
mode_t _m_data; /* mode mask */
|
||||||
|
struct {
|
||||||
|
u_long _f_flags;
|
||||||
|
u_long _f_notflags;
|
||||||
|
} fl;
|
||||||
|
nlink_t _l_data; /* link count */
|
||||||
|
short _d_data; /* level depth (-1 to N) */
|
||||||
|
off_t _o_data; /* file size */
|
||||||
|
struct timespec _t_data; /* time value */
|
||||||
|
uid_t _u_data; /* uid */
|
||||||
|
short _mt_data; /* mount flags */
|
||||||
|
struct _plandata *_p_data[2]; /* PLAN trees */
|
||||||
|
struct _ex {
|
||||||
|
char **_e_argv; /* argv array */
|
||||||
|
char **_e_orig; /* original strings */
|
||||||
|
int *_e_len; /* allocated length */
|
||||||
|
int _e_pbnum; /* base num. of args. used */
|
||||||
|
int _e_ppos; /* number of arguments used */
|
||||||
|
int _e_pnummax; /* max. number of arguments */
|
||||||
|
int _e_psize; /* number of bytes of args. */
|
||||||
|
int _e_pbsize; /* base num. of bytes of args */
|
||||||
|
int _e_psizemax; /* max num. of bytes of args */
|
||||||
|
struct _plandata *_e_next;/* next F_EXECPLUS in tree */
|
||||||
|
} ex;
|
||||||
|
char *_a_data[2]; /* array of char pointers */
|
||||||
|
char *_c_data; /* char pointer */
|
||||||
|
regex_t *_re_data; /* regex */
|
||||||
|
} p_un;
|
||||||
|
} PLAN;
|
||||||
|
#define a_data p_un._a_data
|
||||||
|
#define c_data p_un._c_data
|
||||||
|
#define d_data p_un._d_data
|
||||||
|
#define fl_flags p_un.fl._f_flags
|
||||||
|
#define fl_notflags p_un.fl._f_notflags
|
||||||
|
#define g_data p_un._g_data
|
||||||
|
#define i_data p_un._i_data
|
||||||
|
#define l_data p_un._l_data
|
||||||
|
#define m_data p_un._m_data
|
||||||
|
#define mt_data p_un._mt_data
|
||||||
|
#define o_data p_un._o_data
|
||||||
|
#define p_data p_un._p_data
|
||||||
|
#define t_data p_un._t_data
|
||||||
|
#define u_data p_un._u_data
|
||||||
|
#define re_data p_un._re_data
|
||||||
|
#define e_argv p_un.ex._e_argv
|
||||||
|
#define e_orig p_un.ex._e_orig
|
||||||
|
#define e_len p_un.ex._e_len
|
||||||
|
#define e_pbnum p_un.ex._e_pbnum
|
||||||
|
#define e_ppos p_un.ex._e_ppos
|
||||||
|
#define e_pnummax p_un.ex._e_pnummax
|
||||||
|
#define e_psize p_un.ex._e_psize
|
||||||
|
#define e_pbsize p_un.ex._e_pbsize
|
||||||
|
#define e_psizemax p_un.ex._e_psizemax
|
||||||
|
#define e_next p_un.ex._e_next
|
||||||
|
|
||||||
|
typedef struct _option {
|
||||||
|
const char *name; /* option name */
|
||||||
|
creat_f *create; /* create function */
|
||||||
|
exec_f *execute; /* execute function */
|
||||||
|
int flags;
|
||||||
|
} OPTION;
|
||||||
|
|
||||||
|
#include "extern.h"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user