Import VX multicall utility and applets

This commit is contained in:
2026-03-16 22:06:27 -05:00
parent 9a477cd852
commit 696cb27313
201 changed files with 35660 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
build*/
references
.vscode
+75
View File
@@ -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.
+21
View File
@@ -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
View File
@@ -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
+41
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+57
View File
@@ -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
View File
@@ -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);
}
+68
View File
@@ -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);
}
}
+195
View File
@@ -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);
}
+131
View File
@@ -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);
}
+14
View File
@@ -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>
+10
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
a b differ: char 3, line 1 is 143 c 144 d
+1
View File
@@ -0,0 +1 @@
3 143 c 144 d
+175
View File
@@ -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
}
+273
View File
@@ -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 */
+83
View File
@@ -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__ */
+46
View File
@@ -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__ */
+28
View File
@@ -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__ */
+213
View File
@@ -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;
}
+81
View File
@@ -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 */
+53
View File
@@ -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';
}
+67
View File
@@ -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
+41
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 *);
+384
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+110
View File
@@ -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));
}
+35
View File
@@ -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 *);
+2
View File
@@ -0,0 +1,2 @@
1a2
>
+2
View File
@@ -0,0 +1,2 @@
1a2
> C
+4
View File
@@ -0,0 +1,4 @@
7c8
< G
---
> X
+10
View File
@@ -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
+478
View File
@@ -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
}
+29
View File
@@ -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;
}
+29
View File
@@ -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;
}
+11
View File
@@ -0,0 +1,11 @@
--- functionname.in
+++ functionname_c.in
@@ -6,7 +6,7 @@ doSomethingThenPrintHello(int test)
return;
}
- print("goodbye\n");
+ print("hello\n");
}
+31
View File
@@ -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;
}
+29
View File
@@ -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;
}
+11
View File
@@ -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;
}
}
+27
View File
@@ -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);
}
+4
View File
@@ -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
+4
View File
@@ -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
+26
View File
@@ -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);
}
+2
View File
@@ -0,0 +1,2 @@
Simple input file designed
to be able to test diff
+3
View File
@@ -0,0 +1,3 @@
Simple input file designed
and written
to be able to test diff utility
+15
View File
@@ -0,0 +1,15 @@
/*
* A comment
*
* And another bla
*
* And yet another
*/
int
main(void)
{
printf("something");
return (0);
}
+16
View File
@@ -0,0 +1,16 @@
/*
* A comment
*
* And another bla
*
* and yet another
*/
int
main(void)
{
printf("something");
return (0);
}
+4
View File
@@ -0,0 +1,4 @@
This
is my test file A
it has tabs
Thanks
+4
View File
@@ -0,0 +1,4 @@
This
is my test file B
it has tabs
Thanks
+5
View File
@@ -0,0 +1,5 @@
2c2,3
< to be able to test diff
---
> and written
> to be able to test diff utility
+6
View File
@@ -0,0 +1,6 @@
6c6
< * And yet another
---
> * and yet another
11a12
>
+4
View File
@@ -0,0 +1,4 @@
2c
and written
to be able to test diff utility
.
+6
View File
@@ -0,0 +1,6 @@
4c4
< * And another bla
---
> * And another bla
11a12
>
+4
View File
@@ -0,0 +1,4 @@
d2 1
a2 2
and written
to be able to test diff utility
+34
View File
@@ -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);
+7
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
6c6
< * And yet another
---
> * and yet another
11a12
>
+7
View File
@@ -0,0 +1,7 @@
--- 1
+++ 2
@@ -1,3 +1,3 @@
a
-X
+Y
c
+21
View File
@@ -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);
}
+36
View File
@@ -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);
}
+20
View File
@@ -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);
+85
View File
@@ -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;
}
+30
View File
@@ -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 */
+223
View File
@@ -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.
+977
View File
@@ -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);
}
+14
View File
@@ -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
+6
View File
@@ -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
+6
View File
@@ -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
+14
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
2,3c
and a few typ0s to fix
.
+7
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
3a
=======
and a few typ0s to fix
>>>>>>> 3
.
1a
<<<<<<< 1
.
+5
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
3a
=======
and a few typ0s to fix
>>>>>>> 3
.
1a
<<<<<<< 1
.
+3
View File
@@ -0,0 +1,3 @@
2,3c
and a few typ0s to fix
.
View File
+14
View File
@@ -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
+19
View File
@@ -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
.
+17
View File
@@ -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
+27
View File
@@ -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>
+10
View File
@@ -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
+76
View File
@@ -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
}
+22
View File
@@ -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
.
+7
View File
@@ -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
.
+26
View File
@@ -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
+35
View File
@@ -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
+27
View File
@@ -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
+26
View File
@@ -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
+133
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+243
View File
@@ -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
View File
@@ -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