summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2013-02-03 20:13:33 +0000
committerattilio <attilio@FreeBSD.org>2013-02-03 20:13:33 +0000
commit0d3b58aee00948d85d75a9d3d222deb454afc98e (patch)
tree865d112b57519913a8de64b2d9ca8787633c95a2 /usr.bin
parent561dd1163dbb481d204da7a526739ac6e43d08f2 (diff)
parent2d2c37fb592dfc24f15e4bf14c2f109b5d4b5a83 (diff)
downloadFreeBSD-src-0d3b58aee00948d85d75a9d3d222deb454afc98e.zip
FreeBSD-src-0d3b58aee00948d85d75a9d3d222deb454afc98e.tar.gz
MFC
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile1
-rw-r--r--usr.bin/bmake/Makefile216
-rw-r--r--usr.bin/bmake/config.h6
-rw-r--r--usr.bin/bmake/unit-tests/Makefile2
-rw-r--r--usr.bin/calendar/calendars/calendar.freebsd1
-rw-r--r--usr.bin/clang/clang/Makefile22
-rw-r--r--usr.bin/dtc/HACKING65
-rw-r--r--usr.bin/dtc/Makefile11
-rw-r--r--usr.bin/dtc/checking.cc211
-rw-r--r--usr.bin/dtc/checking.hh308
-rw-r--r--usr.bin/dtc/dtb.cc312
-rw-r--r--usr.bin/dtc/dtb.hh365
-rw-r--r--usr.bin/dtc/dtc.1309
-rw-r--r--usr.bin/dtc/dtc.cc337
-rw-r--r--usr.bin/dtc/fdt.cc1361
-rw-r--r--usr.bin/dtc/fdt.hh782
-rw-r--r--usr.bin/dtc/input_buffer.cc263
-rw-r--r--usr.bin/dtc/input_buffer.hh289
-rw-r--r--usr.bin/dtc/string.cc258
-rw-r--r--usr.bin/dtc/string.hh147
-rw-r--r--usr.bin/dtc/util.hh92
-rw-r--r--usr.bin/ee/Makefile2
-rw-r--r--usr.bin/grep/grep.c4
-rw-r--r--usr.bin/man/man.12
-rwxr-xr-xusr.bin/man/man.sh2
-rw-r--r--usr.bin/netstat/inet.c2
-rw-r--r--usr.bin/patch/Makefile18
-rw-r--r--usr.bin/patch/backupfile.c246
-rw-r--r--usr.bin/patch/backupfile.h39
-rw-r--r--usr.bin/patch/common.h119
-rw-r--r--usr.bin/patch/inp.c484
-rw-r--r--usr.bin/patch/inp.h32
-rw-r--r--usr.bin/patch/mkpath.c78
-rw-r--r--usr.bin/patch/patch.1700
-rw-r--r--usr.bin/patch/patch.c1069
-rw-r--r--usr.bin/patch/pathnames.h12
-rw-r--r--usr.bin/patch/pch.c1593
-rw-r--r--usr.bin/patch/pch.h56
-rw-r--r--usr.bin/patch/util.c432
-rw-r--r--usr.bin/patch/util.h51
-rw-r--r--usr.bin/procstat/procstat.c4
-rw-r--r--usr.bin/sort/sort.c35
-rw-r--r--usr.bin/stdbuf/stdbuf.c8
-rw-r--r--usr.bin/systat/vmstat.c76
-rw-r--r--usr.bin/tr/tr.c3
-rw-r--r--usr.bin/truss/main.c11
-rw-r--r--usr.bin/vmstat/vmstat.c3
-rw-r--r--usr.bin/w/w.c6
-rw-r--r--usr.bin/xinstall/Makefile8
-rw-r--r--usr.bin/xinstall/install.1136
-rw-r--r--usr.bin/xinstall/xinstall.c732
51 files changed, 11021 insertions, 300 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 17aafa8..2923f74 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -123,6 +123,7 @@ SUBDIR= alias \
pagesize \
passwd \
paste \
+ patch \
pathchk \
perror \
pr \
diff --git a/usr.bin/bmake/Makefile b/usr.bin/bmake/Makefile
index 7ae5e5f..6cac1ec 100644
--- a/usr.bin/bmake/Makefile
+++ b/usr.bin/bmake/Makefile
@@ -10,109 +10,109 @@ SRCTOP?= ${.CURDIR:H:H}
# look here first for config.h
CFLAGS+= -I${.CURDIR}
-# $NetBSD: Makefile,v 1.56 2012/05/30 21:54:23 sjg Exp $
-# @(#)Makefile 5.2 (Berkeley) 12/28/90
-
-# $Id: Makefile.in,v 1.178 2012/11/15 16:48:59 sjg Exp $
+# for after-import
+CLEANDIRS+= FreeBSD
+CLEANFILES+= bootstrap
-PROG?= ${.CURDIR:T}
-SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \
- make.c parse.c str.c suff.c targ.c trace.c var.c util.c
-SRCS+= strlist.c
-SRCS+= make_malloc.c
-SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \
- lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \
- lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \
- lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \
- lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c
-SRCS += lstPrev.c
-
-# you can use this Makefile if you have an earlier version of bmake.
-prefix= /usr
-srcdir= ${SRCTOP}/contrib/bmake
-CC?= gcc
+# $Id: Makefile,v 1.5 2013/01/28 19:31:58 sjg Exp $
# Base version on src date
-MAKE_VERSION= 20121111
-DEFAULT_SYS_PATH = .../share/mk:/usr/share/mk
+MAKE_VERSION= 20130123
-CPPFLAGS+=
-CFLAGS+= ${CPPFLAGS}
-CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\"
-CFLAGS+= -I. -I${srcdir} -DHAVE_CONFIG_H ${XDEFS} -DMAKE_NATIVE
-CFLAGS+= ${CFLAGS_${.TARGET:T}}
-CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}}
-COPTS.main.c+= "-DMAKE_VERSION=\"${MAKE_VERSION}\""
-LDFLAGS=
-LIBOBJS= ${LIBOBJDIR}stresep$U.o
-LDADD=
+PROG?= ${.CURDIR:T}
+
+SRCS= \
+ arch.c \
+ buf.c \
+ compat.c \
+ cond.c \
+ dir.c \
+ for.c \
+ hash.c \
+ job.c \
+ main.c \
+ make.c \
+ make_malloc.c \
+ meta.c \
+ parse.c \
+ str.c \
+ strlist.c \
+ suff.c \
+ targ.c \
+ trace.c \
+ util.c \
+ var.c
+
+# from lst.lib/
+SRCS+= \
+ lstAppend.c \
+ lstAtEnd.c \
+ lstAtFront.c \
+ lstClose.c \
+ lstConcat.c \
+ lstDatum.c \
+ lstDeQueue.c \
+ lstDestroy.c \
+ lstDupl.c \
+ lstEnQueue.c \
+ lstFind.c \
+ lstFindFrom.c \
+ lstFirst.c \
+ lstForEach.c \
+ lstForEachFrom.c \
+ lstInit.c \
+ lstInsert.c \
+ lstIsAtEnd.c \
+ lstIsEmpty.c \
+ lstLast.c \
+ lstMember.c \
+ lstNext.c \
+ lstOpen.c \
+ lstPrev.c \
+ lstRemove.c \
+ lstReplace.c \
+ lstSucc.c
+
+# this file gets generated by configure
+.sinclude "Makefile.config"
.if !empty(LIBOBJS)
SRCS+= ${LIBOBJS:T:.o=.c}
.endif
-USE_META = yes
-.if ${USE_META} != "no"
-SRCS+= meta.c
+# just in case
+prefix?= /usr
+srcdir?= ${.CURDIR}
+
+DEFAULT_SYS_PATH?= .../share/mk:${prefix}/share/mk
+
CPPFLAGS+= -DUSE_META
+CFLAGS+= ${CPPFLAGS}
+CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\"
+CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE
+CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}}
+COPTS.main.c+= "-DMAKE_VERSION=\"${MAKE_VERSION}\""
+
+# meta mode can be useful even without filemon
FILEMON_H ?= /usr/include/dev/filemon/filemon.h
.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h"
COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
.endif
-.endif
.PATH: ${srcdir}
.PATH: ${srcdir}/lst.lib
-OS!= uname -s
-ARCH!= uname -p 2>/dev/null || uname -m
-
-# list of OS's which are derrived from BSD4.4
-isBSD44= NetBSD FreeBSD OpenBSD DragonFly
-
-.if ${OS} == "NetBSD"
-# Don't set these for anyone else since we don't know what the effect may be.
-# On FreeBSD WARNS=2 sets a bunch of -W flags that make does not handle.
-WFORMAT= 1
-WARNS=4
-.NOPATH: bmake.cat1
-.if make(install) && exists(${DESTDIR}/usr/share/doc)
-SUBDIR= PSD.doc
-.endif
-.endif
-
-.if defined(.PARSEDIR)
-# we cannot rely on anything but bmake to parse this correctly.
-.if empty(isBSD44:M${OS})
-MANTARGET=cat
-INSTALL?=${srcdir}/install-sh
-.if (${MACHINE} == "sun386")
-# even I don't have one of these anymore :-)
-CFLAGS+= -DPORTAR
-.elif (${MACHINE} != "sunos")
-SRCS+= sigcompat.c
-CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART
-.endif
-.endif
-
.if make(obj) || make(clean)
SUBDIR+= unit-tests
.endif
-.endif
-
-# many systems use gcc these days
-CC_IS_GCC=yes
-.if ${CC_IS_GCC} == "yes"
-# problem with gcc3
-CFLAGS_var.o+= -Wno-cast-qual
-.endif
-CFLAGS_main.o+= "-DMACHINE=\"${MACHINE}\"" "-DMACHINE_ARCH=\"${MACHINE_ARCH}\""
-EXTRACT_MAN=no
+MAN= ${PROG}.1
+MAN1= ${MAN}
-MAN=${PROG}.1
.if (${PROG} != "make")
+CLEANFILES+= my.history
+.if make(${MAN}) || !exists(${srcdir}/${MAN})
my.history: ${MAKEFILE}
@(echo ".Nm"; \
echo "is derived from NetBSD"; \
@@ -120,19 +120,16 @@ my.history: ${MAKEFILE}
echo "It uses autoconf to facilitate portability to other platforms."; \
echo ".Pp") > $@
+.NOPATH: ${MAN}
${MAN}: make.1 my.history
- @echo making ${PROG}.1
+ @echo making $@
@sed -e 's/^.Nx/NetBSD/' -e '/^.Nm/s/make/${PROG}/' \
-e '/^.Sh HISTORY/rmy.history' \
-e '/^.Sh HISTORY/,$$s,^.Nm,make,' ${srcdir}/make.1 > $@
+all beforeinstall: ${MAN}
+_mfromdir=.
.endif
-
-.if !empty(isBSD44:M${OS})
-.if "${OS}" != "NetBSD"
-MAN1=${MAN}
-.endif
-MANTARGET?=man
.endif
MANTARGET?= cat
@@ -142,58 +139,26 @@ MANDEST?= ${MANDIR}/${MANTARGET}1
_mfromdir=${srcdir}
.endif
-.if exists(${srcdir}/../Makefile.inc)
-.include "${srcdir}/../Makefile.inc"
-.endif
-.sinclude <bsd.prog.mk>
-# sigh, FreeBSD at least includes bsd.subdir.mk via bsd.obj.mk
-# so the inclusion below, results in complaints about re-defined
-# targets. For NetBSD though we need to explicitly include it.
-.if defined(.PARSEDIR)
-.if defined(SUBDIR) && !target(${SUBDIR:[1]})
-.sinclude <bsd.subdir.mk>
-.endif
-.endif
+.include <bsd.prog.mk>
-CPPFLAGS+= -DMAKE_NATIVE
+CPPFLAGS+= -DMAKE_NATIVE -DHAVE_CONFIG_H
COPTS.var.c += -Wno-cast-qual
COPTS.job.c += -Wno-format-nonliteral
COPTS.parse.c += -Wno-format-nonliteral
COPTS.var.c += -Wno-format-nonliteral
# Force these
+SHAREDIR= ${prefix}/share
BINDIR= ${prefix}/bin
-MANDIR= ${prefix}/man
+MANDIR= ${SHAREDIR}/man
+
+.if !exists(.depend)
+${OBJS}: config.h
+.endif
-arch.o: config.h
# make sure that MAKE_VERSION gets updated.
main.o: ${SRCS} ${MAKEFILE}
-MK?=${prefix}/share/mk
-MKSRC?=mk
-INSTALL?=${srcdir}/install-sh
-
-beforeinstall:
- test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR}
- test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST}
-
-# latest version of *.mk includes an installer.
-# you should not need to set USE_OS
-install-mk:
-.if exists(${MKSRC}/install-mk)
- test -d ${DESTDIR}${MK} || ${INSTALL} -m 775 -d ${DESTDIR}${MK}
- ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${MK} ${USE_OS}
-.else
- @echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false
-.endif
-
-.ifdef TOOLDIR
-# this is a native netbsd build,
-# use libutil rather than the local emalloc etc.
-CPPFLAGS+= -DUSE_EMALLOC
-LDADD+=-lutil
-DPADD+=${LIBUTIL}
-.endif
# A simple unit-test driver to help catch regressions
accept test:
@@ -205,7 +170,6 @@ MANDIR= /usr/share/man/man
# make sure we get this
CFLAGS+= ${COPTS.${.IMPSRC:T}}
-CLEANFILES+= bootstrap
after-import: ${SRCTOP}/contrib/bmake/bsd.after-import.mk
cd ${.CURDIR} && ${.MAKE} -f ${SRCTOP}/contrib/bmake/bsd.after-import.mk
diff --git a/usr.bin/bmake/config.h b/usr.bin/bmake/config.h
index 64777b8..32026c7 100644
--- a/usr.bin/bmake/config.h
+++ b/usr.bin/bmake/config.h
@@ -1,6 +1,6 @@
+/* $FreeBSD$ */
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.in by autoheader. */
-/* $FreeBSD$ */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
@@ -230,7 +230,7 @@
#define PACKAGE_NAME "bmake"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "bmake 20120620"
+#define PACKAGE_STRING "bmake 20121212"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "bmake"
@@ -239,7 +239,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "20120620"
+#define PACKAGE_VERSION "20121212"
/* Define as the return type of signal handlers (`int' or `void'). */
#define RETSIGTYPE void
diff --git a/usr.bin/bmake/unit-tests/Makefile b/usr.bin/bmake/unit-tests/Makefile
index 89c7fe0..8b2f514 100644
--- a/usr.bin/bmake/unit-tests/Makefile
+++ b/usr.bin/bmake/unit-tests/Makefile
@@ -5,7 +5,7 @@
SRCTOP?= ${.CURDIR:H:H:H}
-# $Id: Makefile.in,v 1.39 2012/11/09 19:16:10 sjg Exp $
+# $Id: Makefile.in,v 1.40 2012/12/28 21:28:19 sjg Exp $
#
# $NetBSD: Makefile,v 1.35 2012/11/09 19:08:28 sjg Exp $
#
diff --git a/usr.bin/calendar/calendars/calendar.freebsd b/usr.bin/calendar/calendars/calendar.freebsd
index 810f47b..f7f449b 100644
--- a/usr.bin/calendar/calendars/calendar.freebsd
+++ b/usr.bin/calendar/calendars/calendar.freebsd
@@ -165,6 +165,7 @@
05/22 Clive Tong-I Lin <clive@FreeBSD.org> born in Changhua, Taiwan, Republic of China, 1978
05/22 Michael Bushkov <bushman@FreeBSD.org> born in Rostov-on-Don, Russian Federation, 1985
05/22 Rui Paulo <rpaulo@FreeBSD.org> born in Evora, Portugal, 1986
+05/22 David Naylor <dbn@FreeBSD.org> born in Johannesburg, South Africa, 1988
05/23 Munechika Sumikawa <sumikawa@FreeBSD.org> born in Osaka, Osaka, Japan, 1972
05/24 Duncan McLennan Barclay <dmlb@FreeBSD.org> born in London, Middlesex, United Kingdom, 1970
05/24 Oliver Lehmann <oliver@FreeBSD.org> born in Karlsburg, Germany, 1981
diff --git a/usr.bin/clang/clang/Makefile b/usr.bin/clang/clang/Makefile
index 6fa9b80..18a768b 100644
--- a/usr.bin/clang/clang/Makefile
+++ b/usr.bin/clang/clang/Makefile
@@ -35,6 +35,19 @@ TGHDRS= CC1AsOptions \
DiagnosticLexKinds \
DiagnosticSemaKinds \
Options
+
+.if !defined(EARLY_BUILD) && defined(MK_CLANG_FULL) && ${MK_CLANG_FULL} != "no"
+_clangstaticanalyzer= \
+ clangstaticanalyzerfrontend \
+ clangstaticanalyzercheckers \
+ clangstaticanalyzercore
+_clangarcmigrate= \
+ clangarcmigrate
+_clangrewriter= \
+ clangrewritefrontend \
+ clangrewritecore
+.endif # !EARLY_BUILD && MK_CLANG_FULL
+
LIBDEPS=clangfrontendtool \
clangfrontend \
clangdriver \
@@ -42,13 +55,10 @@ LIBDEPS=clangfrontendtool \
clangcodegen \
clangparse \
clangsema \
- clangstaticanalyzerfrontend \
- clangstaticanalyzercheckers \
- clangstaticanalyzercore \
+ ${_clangstaticanalyzer} \
clanganalysis \
- clangarcmigrate \
- clangrewritefrontend \
- clangrewritecore \
+ ${_clangarcmigrate} \
+ ${_clangrewriter} \
clangedit \
clangast \
clanglex \
diff --git a/usr.bin/dtc/HACKING b/usr.bin/dtc/HACKING
new file mode 100644
index 0000000..4fb46da
--- /dev/null
+++ b/usr.bin/dtc/HACKING
@@ -0,0 +1,65 @@
+$FreeBSD$
+
+Notes for people hacking on dtc
+===============================
+
+This file contains some notes for people wishing to hack on dtc.
+
+Upstreaming
+-----------
+
+This code is developed in the FreeBSD svn repository:
+
+https://svn.freebsd.org/base/head/usr.bin/dtc
+
+If you got the source from anywhere else and wish to make changes, please
+ensure that you are working against the latest version, or you may end up
+fixing bugs that are already fixed upstream. Although the license makes no
+requirement that you share any improvements that you make, patches are very
+welcome.
+
+C++11
+-----
+
+This project currently aims to compile with g++ 4.2.1 and so doesn't make any
+use of C++11 features. It would be a good idea to relax this restriction once
+clang is the default compiler for ARM, MIPS and PowerPC.
+
+This code makes use of a lot of iterator loops, which would be cleaner using
+the new syntax in C++11. It also explicitly deletes a lot of objects held in
+collections in destructors that have these collections as their members. This
+could be simplified by using `shared_ptr`.
+
+The code does make use of `static_assert()`, but uses a macro in utility.hh to
+remove these if they are not supported. The FreeBSD standard headers also
+define a compatibility macro the implements static asserts in terms of an array
+with 1 element on success and -1 elements on failure.
+
+Adding New Checks
+-----------------
+
+Currently, the biggest weakness of this version of the tool is that it lacks
+most of the semantic checkers that can be implemented by simply reading the
+ePAPR spec. The `checker` class provides a simple superclass for implementing
+these quite easily. There are also helper methods on `device_tree` for finding
+specific nodes, for checks that require some understanding of the structure of
+the tree.
+
+We should probably add a parent pointer to the `node` class for easily walking
+up the tree.
+
+Adding Direct C Output
+----------------------
+
+The FreeBSD build system currently uses dtc to generate a blob and then
+converts this to C source code. A new `output_writer` subclass could easily
+generate the C directly.
+
+Parser Improvements
+-------------------
+
+There are a few FIXME lines in the parser for some corner cases that are not
+currently used by FreeBSD. These are mainly related to labels in the middle of
+values. These can be fixed by creating a new `property_value` with the
+specified label, starting at the location of the label. Don't forget to remove
+the associated comments from the BUGS section of the man page if you fix this.
diff --git a/usr.bin/dtc/Makefile b/usr.bin/dtc/Makefile
new file mode 100644
index 0000000..78973fc
--- /dev/null
+++ b/usr.bin/dtc/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG_CXX=dtc
+SRCS= dtc.cc input_buffer.cc string.cc dtb.cc fdt.cc checking.cc
+MAN= dtc.1
+
+WARNS?= 3
+
+NO_SHARED?=NO
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/dtc/checking.cc b/usr.bin/dtc/checking.cc
new file mode 100644
index 0000000..5462a49
--- /dev/null
+++ b/usr.bin/dtc/checking.cc
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "checking.hh"
+#include <stdio.h>
+
+namespace dtc
+{
+namespace fdt
+{
+namespace checking
+{
+
+bool
+checker::visit_node(device_tree *tree, node *n)
+{
+ path.push_back(std::make_pair(n->name, n->unit_address));
+ // Check this node
+ if (!check_node(tree, n))
+ {
+ return false;
+ }
+ // Now check its properties
+ for (node::property_iterator i=n->property_begin(), e=n->property_end()
+ ; i!=e ; ++i)
+ {
+ if (!check_property(tree, n, *i))
+ {
+ return false;
+ }
+ }
+ // And then recursively check the children
+ for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ;
+ ++i)
+ {
+ if (!visit_node(tree, *i))
+ {
+ return false;
+ }
+ }
+ path.pop_back();
+ return true;
+}
+
+void
+checker::report_error(const char *errmsg)
+{
+ fprintf(stderr, "Error: %s, while checking node: ", errmsg);
+ for (device_tree::node_path::iterator p=path.begin()+1, pe=path.end() ;
+ p!=pe ; ++p)
+ {
+ putc('/', stderr);
+ p->first.dump();
+ if (!(p->second.empty()))
+ {
+ putc('@', stderr);
+ p->second.dump();
+ }
+ }
+ fprintf(stderr, " [-W%s]\n", checker_name);
+}
+
+bool
+property_checker::check_property(device_tree *tree, node *n, property *p)
+{
+ if (p->get_key() == key)
+ {
+ if (!check(tree, n, p))
+ {
+ report_error("property check failed");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+property_size_checker::check(device_tree *tree, node *n, property *p)
+{
+ uint32_t psize = 0;
+ for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ; ++i)
+ {
+ if (!i->is_binary())
+ {
+ return false;
+ }
+ psize += i->byte_data.size();
+ }
+ return psize == size;
+}
+
+template<property_value::value_type T>
+void
+check_manager::add_property_type_checker(const char *name, string prop)
+{
+ checkers.insert(std::make_pair(string(name),
+ new property_type_checker<T>(name, prop)));
+}
+
+void
+check_manager::add_property_size_checker(const char *name,
+ string prop,
+ uint32_t size)
+{
+ checkers.insert(std::make_pair(string(name),
+ new property_size_checker(name, prop, size)));
+}
+
+check_manager::~check_manager()
+{
+ while (checkers.begin() != checkers.end())
+ {
+ delete checkers.begin()->second;
+ checkers.erase(checkers.begin());
+ }
+ while (disabled_checkers.begin() != disabled_checkers.end())
+ {
+ delete disabled_checkers.begin()->second;
+ }
+}
+
+check_manager::check_manager()
+{
+ // NOTE: All checks listed here MUST have a corresponding line
+ // in the man page!
+ add_property_type_checker<property_value::STRING_LIST>(
+ "type-compatible", string("compatible"));
+ add_property_type_checker<property_value::STRING>(
+ "type-model", string("model"));
+ add_property_size_checker("type-phandle", string("phandle"), 4);
+}
+
+bool
+check_manager::run_checks(device_tree *tree, bool keep_going)
+{
+ bool success = true;
+ for (std::map<string, checker*>::iterator i=checkers.begin(),
+ e=checkers.end() ; i!=e ; ++i)
+ {
+ success &= i->second->check_tree(tree);
+ if (!(success || keep_going))
+ {
+ break;
+ }
+ }
+ return success;
+}
+
+bool
+check_manager::disable_checker(string name)
+{
+ std::map<string, checker*>::iterator checker = checkers.find(name);
+ if (checker != checkers.end())
+ {
+ disabled_checkers.insert(std::make_pair(name,
+ checker->second));
+ checkers.erase(checker);
+ return true;
+ }
+ return false;
+}
+
+bool
+check_manager::enable_checker(string name)
+{
+ std::map<string, checker*>::iterator checker =
+ disabled_checkers.find(name);
+ if (checker != disabled_checkers.end())
+ {
+ checkers.insert(std::make_pair(name, checker->second));
+ disabled_checkers.erase(checker);
+ return true;
+ }
+ return false;
+}
+
+} // namespace checking
+
+} // namespace fdt
+
+} // namespace dtc
+
diff --git a/usr.bin/dtc/checking.hh b/usr.bin/dtc/checking.hh
new file mode 100644
index 0000000..0de1d60
--- /dev/null
+++ b/usr.bin/dtc/checking.hh
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CHECKING_HH_
+#define _CHECKING_HH_
+#include "string.hh"
+#include "fdt.hh"
+
+namespace dtc
+{
+namespace fdt
+{
+namespace checking
+{
+/**
+ * Base class for all checkers. This will visit the entire tree and perform
+ * semantic checks defined in subclasses. Note that device trees are generally
+ * small (a few dozen nodes at most) and so we optimise for flexibility and
+ * extensibility here, not for performance. Each checker will visit the entire
+ * tree.
+ */
+class checker
+{
+ /**
+ * The path to the current node being checked. This is used for
+ * printing error messages.
+ */
+ device_tree::node_path path;
+ /**
+ * The name of the checker. This is used for printing error messages
+ * and for enabling / disabling specific checkers from the command
+ * line.
+ */
+ const char *checker_name;
+ /**
+ * Visits each node, calling the checker functions on properties and
+ * nodes.
+ */
+ bool visit_node(device_tree *tree, node *n);
+ protected:
+ /**
+ * Prints the error message, along with the path to the node that
+ * caused the error and the name of the checker.
+ */
+ void report_error(const char *errmsg);
+ public:
+ /**
+ * Constructor. Takes the name of this checker, which is which is used
+ * when reporting errors.
+ */
+ checker(const char *name) : checker_name(name) {}
+ /**
+ * Virtual destructor in case any subclasses need to do cleanup.
+ */
+ virtual ~checker() {}
+ /**
+ * Method for checking that a node is valid. The root class version
+ * does nothing, subclasses should override this.
+ */
+ virtual bool check_node(device_tree *tree, node *n)
+ {
+ return true;
+ }
+ /**
+ * Method for checking that a property is valid. The root class
+ * version does nothing, subclasses should override this.
+ */
+ virtual bool check_property(device_tree *tree, node *n, property *p)
+ {
+ return true;
+ }
+ /**
+ * Runs the checker on the specified device tree.
+ */
+ bool check_tree(fdt::device_tree *tree)
+ {
+ return visit_node(tree, tree->get_root());
+ }
+};
+
+/**
+ * Abstract base class for simple property checks. This class defines a check
+ * method for subclasses, which is invoked only when it finds a property with
+ * the matching name. To define simple property checkers, just subclass this
+ * and override the check() method.
+ */
+class property_checker : public checker
+{
+ /**
+ * The name of the property that this checker is looking for.
+ */
+ string key;
+ public:
+ /**
+ * Implementation of the generic property-checking method that checks
+ * for a property with the name specified in the constructor
+ */
+ virtual bool check_property(device_tree *tree, node *n, property *p);
+ /**
+ * Constructor. Takes the name of the checker and the name of the
+ * property to check.
+ */
+ property_checker(const char* name, string property_name)
+ : checker(name), key(property_name) {}
+ /**
+ * The check method, which subclasses should implement.
+ */
+ virtual bool check(device_tree *tree, node *n, property *p) = 0;
+};
+
+/**
+ * Property type checker.
+ */
+template<property_value::value_type T>
+struct property_type_checker : public property_checker
+{
+ /**
+ * Constructor, takes the name of the checker and the name of the
+ * property to check as arguments.
+ */
+ property_type_checker(const char* name, string property_name) :
+ property_checker(name, property_name) {}
+ virtual bool check(device_tree *tree, node *n, property *p) = 0;
+};
+
+/**
+ * Empty property checker. This checks that the property has no value.
+ */
+template<>
+struct property_type_checker <property_value::EMPTY> : public property_checker
+{
+ property_type_checker(const char* name, string property_name) :
+ property_checker(name, property_name) {}
+ virtual bool check(device_tree *tree, node *n, property *p)
+ {
+ return p->begin() == p->end();
+ }
+};
+
+/**
+ * String property checker. This checks that the property has exactly one
+ * value, which is a string.
+ */
+template<>
+struct property_type_checker <property_value::STRING> : public property_checker
+{
+ property_type_checker(const char* name, string property_name) :
+ property_checker(name, property_name) {}
+ virtual bool check(device_tree *tree, node *n, property *p)
+ {
+ return (p->begin() + 1 == p->end()) && p->begin()->is_string();
+ }
+};
+/**
+ * String list property checker. This checks that the property has at least
+ * one value, all of which are strings.
+ */
+template<>
+struct property_type_checker <property_value::STRING_LIST> :
+ public property_checker
+{
+ property_type_checker(const char* name, string property_name) :
+ property_checker(name, property_name) {}
+ virtual bool check(device_tree *tree, node *n, property *p)
+ {
+ for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ;
+ ++i)
+ {
+ if (!(i->is_string() || i->is_string_list()))
+ {
+ return false;
+ }
+ }
+ return p->begin() != p->end();
+ }
+};
+
+/**
+ * Phandle property checker. This checks that the property has exactly one
+ * value, which is a valid phandle.
+ */
+template<>
+struct property_type_checker <property_value::PHANDLE> : public property_checker
+{
+ property_type_checker(const char* name, string property_name) :
+ property_checker(name, property_name) {}
+ virtual bool check(device_tree *tree, node *n, property *p)
+ {
+ return (p->begin() + 1 == p->end()) &&
+ (tree->referenced_node(*p->begin()) != 0);
+ }
+};
+
+/**
+ * Check that a property has the correct size.
+ */
+struct property_size_checker : public property_checker
+{
+ /**
+ * The expected size of the property.
+ */
+ uint32_t size;
+ public:
+ /**
+ * Constructor, takes the name of the checker, the name of the property
+ * to check, and its expected size as arguments.
+ */
+ property_size_checker(const char* name, string property_name, uint32_t bytes)
+ : property_checker(name, property_name), size(bytes) {}
+ /**
+ * Check, validates that the property has the correct size.
+ */
+ virtual bool check(device_tree *tree, node *n, property *p);
+};
+
+
+/**
+ * The check manager is the interface to running the checks. This allows
+ * default checks to be enabled, non-default checks to be enabled, and so on.
+ */
+class check_manager
+{
+ /**
+ * The enabled checkers, indexed by their names. The name is used when
+ * disabling checkers from the command line. When this manager runs,
+ * it will only run the checkers from this map.
+ */
+ std::map<string, checker*> checkers;
+ /**
+ * The disabled checkers. Moving checkers to this list disables them,
+ * but allows them to be easily moved back.
+ */
+ std::map<string, checker*> disabled_checkers;
+ /**
+ * Helper function for adding a property value checker.
+ */
+ template<property_value::value_type T>
+ void add_property_type_checker(const char *name, string prop);
+ /**
+ * Helper function for adding a simple type checker.
+ */
+ void add_property_type_checker(const char *name, string prop);
+ /**
+ * Helper function for adding a property value checker.
+ */
+ void add_property_size_checker(const char *name,
+ string prop,
+ uint32_t size);
+ public:
+ /**
+ * Delete all of the checkers that are part of this checker manager.
+ */
+ ~check_manager();
+ /**
+ * Default constructor, creates check manager containing all of the
+ * default checks.
+ */
+ check_manager();
+ /**
+ * Run all of the checks on the specified tree.
+ */
+ bool run_checks(device_tree *tree, bool keep_going);
+ /**
+ * Disables the named checker.
+ */
+ bool disable_checker(string name);
+ /**
+ * Enables the named checker.
+ */
+ bool enable_checker(string name);
+};
+
+} // namespace checking
+
+} // namespace fdt
+
+} // namespace dtc
+
+#endif // !_CHECKING_HH_
diff --git a/usr.bin/dtc/dtb.cc b/usr.bin/dtc/dtb.cc
new file mode 100644
index 0000000..986ef6f
--- /dev/null
+++ b/usr.bin/dtc/dtb.cc
@@ -0,0 +1,312 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "dtb.hh"
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+namespace dtc
+{
+namespace dtb
+{
+
+void output_writer::write_data(byte_buffer b)
+{
+ for (byte_buffer::iterator i=b.begin(), e=b.end(); i!=e ; i++)
+ {
+ write_data(*i);
+ }
+}
+
+void
+binary_writer::write_string(string name)
+{
+ name.push_to_buffer(buffer);
+ // Trailing nul
+ buffer.push_back(0);
+}
+
+void
+binary_writer::write_data(uint8_t v)
+{
+ buffer.push_back(v);
+}
+
+void
+binary_writer::write_data(uint32_t v)
+{
+ while (buffer.size() % 4 != 0)
+ {
+ buffer.push_back(0);
+ }
+ push_big_endian(buffer, v);
+}
+
+void
+binary_writer::write_data(uint64_t v)
+{
+ while (buffer.size() % 8 != 0)
+ {
+ buffer.push_back(0);
+ }
+ push_big_endian(buffer, v);
+}
+
+void
+binary_writer::write_to_file(int fd)
+{
+ // FIXME: Check return
+ write(fd, buffer.data(), buffer.size());
+}
+
+uint32_t
+binary_writer::size()
+{
+ return buffer.size();
+}
+
+void
+asm_writer::write_string(const char *c)
+{
+ while (*c)
+ {
+ buffer.push_back((uint8_t)*(c++));
+ }
+}
+
+void
+asm_writer::write_line(const char *c)
+{
+ if (byte_count != 0)
+ {
+ byte_count = 0;
+ buffer.push_back('\n');
+ }
+ write_string(c);
+}
+
+void
+asm_writer::write_byte(uint8_t b)
+{
+ char out[3] = {0};
+ if (byte_count++ == 0)
+ {
+ buffer.push_back('\t');
+ }
+ write_string(".byte 0x");
+ snprintf(out, 3, "%.2hhx", b);
+ buffer.push_back(out[0]);
+ buffer.push_back(out[1]);
+ if (byte_count == 4)
+ {
+ buffer.push_back('\n');
+ byte_count = 0;
+ }
+ else
+ {
+ buffer.push_back(';');
+ buffer.push_back(' ');
+ }
+}
+
+void
+asm_writer::write_label(string name)
+{
+ write_line("\t.globl ");
+ name.push_to_buffer(buffer);
+ buffer.push_back('\n');
+ name.push_to_buffer(buffer);
+ buffer.push_back(':');
+ buffer.push_back('\n');
+ buffer.push_back('_');
+ name.push_to_buffer(buffer);
+ buffer.push_back(':');
+ buffer.push_back('\n');
+
+}
+
+void
+asm_writer::write_comment(string name)
+{
+ write_line("\t/* ");
+ name.push_to_buffer(buffer);
+ write_string(" */\n");
+}
+
+void
+asm_writer::write_string(string name)
+{
+ write_line("\t.string \"");
+ name.push_to_buffer(buffer);
+ write_line("\"\n");
+ bytes_written += name.size() + 1;
+}
+
+void
+asm_writer::write_data(uint8_t v)
+{
+ write_byte(v);
+ bytes_written++;
+}
+
+void
+asm_writer::write_data(uint32_t v)
+{
+ if (bytes_written % 4 != 0)
+ {
+ write_line("\t.balign 4\n");
+ bytes_written += (4 - (bytes_written % 4));
+ }
+ write_byte((v >> 24) & 0xff);
+ write_byte((v >> 16) & 0xff);
+ write_byte((v >> 8) & 0xff);
+ write_byte((v >> 0) & 0xff);
+ bytes_written += 4;
+}
+
+void
+asm_writer::write_data(uint64_t v)
+{
+ if (bytes_written % 8 != 0)
+ {
+ write_line("\t.balign 8\n");
+ bytes_written += (8 - (bytes_written % 8));
+ }
+ write_byte((v >> 56) & 0xff);
+ write_byte((v >> 48) & 0xff);
+ write_byte((v >> 40) & 0xff);
+ write_byte((v >> 32) & 0xff);
+ write_byte((v >> 24) & 0xff);
+ write_byte((v >> 16) & 0xff);
+ write_byte((v >> 8) & 0xff);
+ write_byte((v >> 0) & 0xff);
+ bytes_written += 8;
+}
+
+void
+asm_writer::write_to_file(int fd)
+{
+ // FIXME: Check return
+ write(fd, buffer.data(), buffer.size());
+}
+
+uint32_t
+asm_writer::size()
+{
+ return bytes_written;
+}
+
+void
+header::write(output_writer &out)
+{
+ out.write_label(string("dt_blob_start"));
+ out.write_label(string("dt_header"));
+ out.write_comment("magic");
+ out.write_data(magic);
+ out.write_comment("totalsize");
+ out.write_data(totalsize);
+ out.write_comment("off_dt_struct");
+ out.write_data(off_dt_struct);
+ out.write_comment("off_dt_strings");
+ out.write_data(off_dt_strings);
+ out.write_comment("off_mem_rsvmap");
+ out.write_data(off_mem_rsvmap);
+ out.write_comment("version");
+ out.write_data(version);
+ out.write_comment("last_comp_version");
+ out.write_data(last_comp_version);
+ out.write_comment("boot_cpuid_phys");
+ out.write_data(boot_cpuid_phys);
+ out.write_comment("size_dt_strings");
+ out.write_data(size_dt_strings);
+ out.write_comment("size_dt_struct");
+ out.write_data(size_dt_struct);
+}
+
+bool
+header::read_dtb(input_buffer &input)
+{
+ if (!(input.consume_binary(magic) && magic == 0xd00dfeed))
+ {
+ fprintf(stderr, "Missing magic token in header. Got %" PRIx32
+ " expected 0xd00dfeed\n", magic);
+ return false;
+ }
+ return input.consume_binary(totalsize) &&
+ input.consume_binary(off_dt_struct) &&
+ input.consume_binary(off_dt_strings) &&
+ input.consume_binary(off_mem_rsvmap) &&
+ input.consume_binary(version) &&
+ input.consume_binary(last_comp_version) &&
+ input.consume_binary(boot_cpuid_phys) &&
+ input.consume_binary(size_dt_strings) &&
+ input.consume_binary(size_dt_struct);
+}
+uint32_t
+string_table::add_string(string str)
+{
+ std::map<string, uint32_t>::iterator old = string_offsets.find(str);
+ if (old == string_offsets.end())
+ {
+ uint32_t start = size;
+ // Don't forget the trailing nul
+ size += str.size() + 1;
+ string_offsets.insert(std::make_pair(str, start));
+ strings.push_back(str);
+ return start;
+ }
+ else
+ {
+ return old->second;
+ }
+}
+
+void
+string_table::write(dtb::output_writer &writer)
+{
+ writer.write_comment(string("Strings table."));
+ writer.write_label(string("dt_strings_start"));
+ for (std::vector<string>::iterator i=strings.begin(), e=strings.end() ;
+ i!=e ; ++i)
+ {
+ writer.write_string(*i);
+ }
+ writer.write_label(string("dt_strings_end"));
+}
+
+} // namespace dtb
+
+} // namespace dtc
+
diff --git a/usr.bin/dtc/dtb.hh b/usr.bin/dtc/dtb.hh
new file mode 100644
index 0000000..2c5f39e
--- /dev/null
+++ b/usr.bin/dtc/dtb.hh
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DTB_HH_
+#define _DTB_HH_
+#include <map>
+#include "string.hh"
+
+#include <assert.h>
+
+namespace dtc
+{
+/**
+ * The dtb namespace contains code related to the generation of device tree
+ * blobs, the binary representation of flattened device trees. The abstract
+ * tree representation calls into this code to generate the output.
+ */
+namespace dtb
+{
+/** The token types in the DTB, as defined by §7.4.1 of the ePAPR
+ * specification. All of these values are written in big-endian format in the
+ * output.
+ */
+enum token_type
+{
+ /**
+ * Marker indicating the start of a node in the tree. This is followed
+ * by the nul-terminated name. If a unit address is specified, then
+ * the name also contains the address, with an @ symbol between the end
+ * of the name and the start of the address.
+ *
+ * The name is then padded such that the next token begins on a 4-byte
+ * boundary. The node may contain properties, other nodes, both, or be
+ * empty.
+ */
+ FDT_BEGIN_NODE = 0x00000001,
+ /**
+ * Marker indicating the end of a node.
+ */
+ FDT_END_NODE = 0x00000002,
+ /**
+ * The start of a property. This is followed by two 32-bit big-endian
+ * values. The first indicates the length of the property value, the
+ * second its index in the strings table. It is then followed by the
+ * property value, if the value is of non-zero length.
+ */
+ FDT_PROP = 0x00000003,
+ /**
+ * Ignored token. May be used for padding inside DTB nodes.
+ */
+ FDT_NOP = 0x00000004,
+ /**
+ * Marker indicating the end of the tree.
+ */
+ FDT_END = 0x00000009
+};
+
+/**
+ * Returns the token as a string. This is used for debugging and for printing
+ * human-friendly error messages about malformed DTB input.
+ */
+inline const char *token_type_name(token_type t)
+{
+ switch(t)
+ {
+ case FDT_BEGIN_NODE:
+ return "FDT_BEGIN_NODE";
+ case FDT_END_NODE:
+ return "FDT_END_NODE";
+ case FDT_PROP:
+ return "FDT_PROP";
+ case FDT_NOP:
+ return "FDT_NOP";
+ case FDT_END:
+ return "FDT_END";
+ }
+ assert(0);
+}
+
+/**
+ * Abstract class for writing a section of the output. We create one
+ * of these for each section that needs to be written. It is intended to build
+ * a temporary buffer of the output in memory and then write it to a file
+ * stream. The size can be returned after all of the data has been written
+ * into the internal buffer, so the sizes of the three tables can be calculated
+ * before storing them in the buffer.
+ */
+struct output_writer
+{
+ /**
+ * Writes a label into the output stream. This is only applicable for
+ * assembly output, where the labels become symbols that can be
+ * resolved at link time.
+ */
+ virtual void write_label(string name) = 0;
+ /**
+ * Writes a comment into the output stream. Useful only when debugging
+ * the output.
+ */
+ virtual void write_comment(string name) = 0;
+ /**
+ * Writes a string. A nul terminator is implicitly added.
+ */
+ virtual void write_string(string name) = 0;
+ /**
+ * Writes a single 8-bit value.
+ */
+ virtual void write_data(uint8_t) = 0;
+ /**
+ * Writes a single 32-bit value. The value is written in big-endian
+ * format, but should be passed in the host's native endian.
+ */
+ virtual void write_data(uint32_t) = 0;
+ /**
+ * Writes a single 64-bit value. The value is written in big-endian
+ * format, but should be passed in the host's native endian.
+ */
+ virtual void write_data(uint64_t) = 0;
+ /**
+ * Writes the collected output to the specified file descriptor.
+ */
+ virtual void write_to_file(int fd) = 0;
+ /**
+ * Returns the number of bytes.
+ */
+ virtual uint32_t size() = 0;
+ /**
+ * Helper for writing tokens to the output stream. This writes a
+ * comment above the token describing its value, for easier debugging
+ * of the output.
+ */
+ inline void write_token(token_type t)
+ {
+ write_comment(token_type_name(t));
+ write_data((uint32_t)t);
+ }
+ /**
+ * Helper function that writes a byte buffer to the output, one byte at
+ * a time.
+ */
+ void write_data(byte_buffer b);
+};
+
+/**
+ * Binary file writer. This class is responsible for writing the DTB output
+ * directly in blob format.
+ */
+class binary_writer : public output_writer
+{
+ /**
+ * The internal buffer used to store the blob while it is being
+ * constructed.
+ */
+ byte_buffer buffer;
+ public:
+ /**
+ * The binary format does not support labels, so this method
+ * does nothing.
+ */
+ virtual void write_label(string name) {}
+ /**
+ * Comments are ignored by the binary writer.
+ */
+ virtual void write_comment(string name) {}
+ virtual void write_string(string name);
+ virtual void write_data(uint8_t v);
+ virtual void write_data(uint32_t v);
+ virtual void write_data(uint64_t v);
+ virtual void write_to_file(int fd);
+ virtual uint32_t size();
+};
+/**
+ * Assembly writer. This class is responsible for writing the output in an
+ * assembly format that is suitable for linking into a kernel, loader, and so
+ * on.
+ */
+class asm_writer : public output_writer
+{
+ /**
+ * The internal buffer for temporary values. Note that this actually
+ * contains ASCII text, but it is a byte buffer so that we can just
+ * copy strings across as-is.
+ */
+ byte_buffer buffer;
+ /**
+ * The number of bytes written to the current line. This is used to
+ * allow line wrapping, where we aim to write four .byte directives to
+ * make the alignment clearer.
+ */
+ int byte_count;
+ /**
+ * The current number of bytes written. This is the number in binary
+ * format, not the number of bytes in the buffer.
+ */
+ uint32_t bytes_written;
+
+ /**
+ * Writes a C string directly to the ouput as-is. This is mainly used
+ * for writing directives.
+ */
+ void write_string(const char *c);
+ /**
+ * Writes the string, starting on a new line.
+ */
+ void write_line(const char *c);
+ /**
+ * Writes a byte in binary format. This will emit a single .byte
+ * directive, with up to four per line.
+ */
+ void write_byte(uint8_t b);
+ public:
+ asm_writer() : byte_count(0), bytes_written(0) {}
+ virtual void write_label(string name);
+ virtual void write_comment(string name);
+ virtual void write_string(string name);
+ virtual void write_data(uint8_t v);
+ virtual void write_data(uint32_t v);
+ virtual void write_data(uint64_t v);
+ virtual void write_to_file(int fd);
+ virtual uint32_t size();
+};
+
+/**
+ * Class encapsulating the device tree blob header. This class stores all of
+ * the values found in the header and is responsible for writing them to the
+ * output.
+ */
+struct header
+{
+ /**
+ * Magic value, used to validate that this really is a device tree
+ * blob. Should always be set to 0xd00dfeed.
+ */
+ uint32_t magic;
+ /**
+ * The total size of the blob, including header, reservations, strings
+ * table, and padding.
+ */
+ uint32_t totalsize;
+ /**
+ * The offset from the start of the blob of the struct table (i.e. the
+ * part of the blob containing the entire device tree).
+ */
+ uint32_t off_dt_struct;
+ /**
+ * The offset from the start of the blob of the strings table.
+ */
+ uint32_t off_dt_strings;
+ /**
+ * The offset of the reservation map from the start of the blob.
+ */
+ uint32_t off_mem_rsvmap;
+ /**
+ * The version of the blob. This should always be 17.
+ */
+ uint32_t version;
+ /**
+ * The earliest version of the DTB specification with which this blob
+ * is backwards compatible. This should always be 16.
+ */
+ uint32_t last_comp_version;
+ /**
+ * The ID of the CPU where this boots.
+ */
+ uint32_t boot_cpuid_phys;
+ /**
+ * The size of the strings table.
+ */
+ uint32_t size_dt_strings;
+ /**
+ * The size of the struct table.
+ */
+ uint32_t size_dt_struct;
+ /**
+ * Writes the entire header to the specified output buffer.
+ */
+ void write(output_writer &out);
+ /**
+ * Reads the header from bits binary representation in a blob.
+ */
+ bool read_dtb(input_buffer &input);
+ /**
+ * Default constructor. Initialises the values that have sensible
+ * defaults, leaves the others blank.
+ */
+ header() : magic(0xd00dfeed), version(17), last_comp_version(16),
+ boot_cpuid_phys(0) {}
+};
+
+/**
+ * Class encapsulating the string table. FDT strings are stored in a string
+ * section. This maintains a map from strings to their offsets in the strings
+ * section.
+ *
+ * Note: We don't currently do suffix matching, which may save a small amount
+ * of space.
+ */
+class string_table {
+ /**
+ * Map from strings to their offset.
+ */
+ std::map<string, uint32_t> string_offsets;
+ /**
+ * The strings, in the order in which they should be written to the
+ * output. The order must be stable - adding another string must not
+ * change the offset of any that we have already referenced - and so we
+ * simply write the strings in the order that they are passed.
+ */
+ std::vector<string> strings;
+ /**
+ * The current size of the strings section.
+ */
+ uint32_t size;
+ public:
+ /**
+ * Default constructor, creates an empty strings table.
+ */
+ string_table() : size(0) {}
+ /**
+ * Adds a string to the table, returning the offset from the start
+ * where it will be written. If the string is already present, this
+ * will return its existing offset, otherwise it will return a new
+ * offset.
+ */
+ uint32_t add_string(string str);
+ /**
+ * Writes the strings table to the specified output.
+ */
+ void write(dtb::output_writer &writer);
+};
+
+} // namespace dtb
+
+} // namespace dtc
+
+#endif // !_DTB_HH_
diff --git a/usr.bin/dtc/dtc.1 b/usr.bin/dtc/dtc.1
new file mode 100644
index 0000000..567aa03
--- /dev/null
+++ b/usr.bin/dtc/dtc.1
@@ -0,0 +1,309 @@
+.\"-
+.\" Copyright (c) 2013 David Chisnall
+.\" All rights reserved.
+.\"
+.\" This software was developed by SRI International and the University of
+.\" Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+.\" ("CTSRD"), as part of the DARPA CRASH research programme.
+.\"
+.\" This software was developed by SRI International and the University of
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"/
+.Dd January 1, 2013
+.Dt DTC 1
+.Os
+.Sh NAME
+.Nm dtc
+.Nd device tree compiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhsv
+.Op Fl b Ar boot_cpu_id
+.Op Fl d Ar dependency_file
+.Op Fl E Ar [no-]checker_name
+.Op Fl H Ar phandle_format
+.Op Fl I Ar input_format
+.Op Fl O Ar output_format
+.Op Fl o Ar output_file
+.Op Fl R Ar entries
+.Op Fl S Ar bytes
+.Op Fl p Ar bytes
+.Op Fl V Ar blob_version
+.Op Fl W Ar [no-]checker_name
+.Ar input_file
+.Sh DESCRIPTION
+The
+.Nm
+utility converts flattened device tree (FDT) representations.
+ It is most commonly used to generate device tree blobs (DTB), the binary
+representation of an FDT, from device tree sources (DTS), the ASCII text source
+representation.
+.Pp
+The binary can be written in two formats, binary and assembly.
+The binary is identical to the in-memory representation and can be used
+directly by firmware, loaders, and so on.
+The assembly format, documented in
+.Sx "ASM FORMAT" ,
+will produce the same binary format when assembled, but also includes some
+global variables that refer to parts of the table.
+This format is most commonly used to produce a kernel specific to a device,
+with the device tree blob compiled in.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d Ar dependency_file
+Writes a dependency file understandable by make to the specified file.
+This file can be included in a Makefile and will ensure that the output file
+depends on the input file and any files that it includes.
+This argument is only useful when the input is DTS, as only the source format
+has a notion of inclusions.
+.It Fl E Ar [no-]checker_name
+Enable or disable a specified checker.
+The argument is the name of the checker.
+The full list of checkers is given in
+.Sx CHECKERS .
+.It Fl f
+Force the tool to attempt to generate the output, even if the input had errors.
+.It Fl h
+Display the help text and exit.
+.It Fl H Ar phandle_format
+Specifies the type of phandle nodes to generate in the output.
+Valid values are:
+.Pp
+.Bl -tag -width indent -compact
+.It Ar linux
+Generate the legacy linux,phandle nodes expected by older systems.
+.It Ar epapr
+Generate the phandle nodes, as described in the ePAPR specification.
+This is the most sensible option for device trees being used with
+.Fx .
+.It Ar both
+Generate both, for maximum compatibility.
+.El
+.It Fl I Ar input_format
+Specifies the input format.
+Valid values are:
+.Pp
+.Bl -tag -width indent -compact
+.It Ar dtb
+Device tree blob.
+The binary representation of the FDT.
+.It Ar dts
+Device tree source.
+The ASCII representation of the FDT.
+This is the default if the input format is not explicitly stated.
+.El
+.It Fl O Ar output_format
+Specifies the output format.
+Valid values are:
+.Pp
+.Bl -tag -width indent -compact
+.It Ar asm
+Assembler source for generating a device tree blob, as described in
+.Sx "ASM FORMAT" .
+.It Ar dtb
+Device tree blob.
+The binary representation of the FDT.
+This is the default if the output format is not explicitly stated.
+.It Ar dts
+Device tree source.
+The ASCII representation of the FDT.
+.El
+.It Fl o Ar output_file
+The file to which to write the output.
+.It Fl R Ar entries
+The number of empty reservation table entries to pad the table with.
+This is
+useful if you are generating a device tree blob for bootloader or similar that
+needs to reserve some memory before passing control to the operating system.
+.It Fl S Ar bytes
+The minimum size in bytes of the blob.
+The blob will be padded after the strings table to ensure that it is the
+correct size.
+This is useful for environments where the device tree blob must be modified in
+place.
+.It Fl p Ar bytes
+The number of bytes of padding to add to the blob.
+The blob will be padded after the strings table to ensure that it is the
+correct size.
+This is useful for environments where the device tree blob must be modified in
+place.
+.It Fl W Ar [no-]checker_name
+Enable or disable a specified checker.
+This is an alias for
+.Fl E .
+.It Fl s
+Sorts the properties and nodes in the tree.
+This is mainly useful when using tools like
+.Xr diff 1
+to compare two device tree sources.
+.It Fl V Ar output_version
+The version of the format to output.
+This is only relevant for binary outputs, and only a value of 17 is currently
+supported.
+.It Fl v
+Display the tool version and exit.
+.It Ar input_file
+The source file.
+.El
+.Sh "ASM FORMAT"
+The assembly format defines several globals that can be referred to from other
+compilation units, in addition to any labels specified in the source.
+These are:
+.Pp
+.Bl -tag -width "dt_strings_start" -compact -offset indent
+.It dt_blob_start
+start of the device tree blob.
+.It dt_header
+start of the header, usually identical to the start of the blob.
+.It dt_reserve_map
+start of the reservation map.
+.It dt_struct_start
+start of the structure table.
+.It dt_struct_end
+end of the structure table.
+.It dt_strings_start
+start of the strings table.
+.It dt_strings_end
+end of the strings table.
+.It dt_blob_end
+end of the device tree blob.
+.El
+.Sh CHECKERS
+The utility provides a number of semantic checks on the correctness of the
+tree.
+These can be disabled with the
+.Fl W
+flag.
+For example,
+.Fl W Ar no-type-phandle
+will disable the phandle type check.
+The supported checks are:
+.Pp
+.Bl -tag -width "no-type-phandle" -compact -offset indent
+.It type-compatible
+Checks the type of the
+.Va compatible
+property.
+.It type-model
+Checks the type of the
+.Va model
+property.
+.It type-compatible
+Checks the type of the
+.Va compatible
+property.
+.El
+.Sh EXAMPLES
+The command:
+.Pp
+.Dl "dtc -o blob.S -O asm device.dts"
+.Pp
+will generate a
+.Pa blob.S
+file from the device tree source
+.Pa device.dts
+and print errors if any occur during parsing or property checking.
+The
+resulting file can be assembled and linked into a binary.
+.Pp
+The command:
+.Pp
+.Dl "dtc -o - -O dts -I dtb device.dtb"
+.Pp
+will write the device tree source for the device tree blob
+.Pa device.dtb
+to the standard output.
+This is useful when debugging device trees.
+.Sh COMPATIBILITY
+This utility is intended to be compatible with the device tree compiler
+provided by elinux.org.
+Currently, it implements the subset of features
+required to build FreeBSD and others that have been requested by FreeBSD
+developers.
+.Pp
+The
+.Ar fs
+input format is not supported.
+This builds a tree from a Linux
+.Pa /proc/device-tree ,
+a file system hierarchy not found in FreeBSD, which instead exposes the DTB
+directly via a sysctl.
+.Pp
+The warnings and errors supported by the elinux.org tool are not documented.
+This tool supports the warnings described in the
+.Sx CHECKERS
+section.
+.Sh SEE ALSO
+.Xr fdt 4
+.Sh STANDARDS
+The device tree formats understood by this tool conform to the Power.org
+Standard for Embedded Power Architecture Platform Requirements
+.Pq Vt ePAPR ,
+except as noted in the
+.Sx BUGS
+section and with the following exceptions for compatibility with the elinux.org
+tool:
+.Pp
+.Bl -bullet -compact
+.It
+The target of cross references is defined to be a node name in the
+specification, but is in fact a label.
+.El
+.Pp
+The /include/ directive is not part of the standard, however it is implemented
+with the semantics compatible with the elinux.org tool.
+It must appear in the top level of a file, and imports a new root definition.
+If a file, plus all of its inclusions, contains multiple roots then they are
+merged.
+All nodes that are present in the second but not the first are imported.
+Any that appear in both are recursively merged, with properties from the second
+replacing those from the first and properties child nodes being recursively
+merged.
+.Sh HISTORY
+A dtc tool first appeared in
+.Fx 9.0 .
+This version of the tool first appeared in
+.Fx 10.0 .
+.Sh AUTHORS
+.An David T. Chisnall
+.Pp
+Note: The fact that the tool and the author share the same initials is entirely
+coincidental.
+.Sh BUGS
+The device tree compiler does not yet support the following features:
+.Pp
+.Bl -bullet -compact
+.It
+Labels in the middle of property values.
+This is only useful in the assembly output, and only vaguely useful there, so
+is unlikely to be added soon.
+.It
+Full paths, rather than labels, as the targets for phandles.
+This is not very hard to add, but will probably not be added until something
+actually needs it.
+.El
+.Pp
+The current version performs a very limited set of semantic checks on the tree.
+This will be improved in future versions.
diff --git a/usr.bin/dtc/dtc.cc b/usr.bin/dtc/dtc.cc
new file mode 100644
index 0000000..cffb3eb
--- /dev/null
+++ b/usr.bin/dtc/dtc.cc
@@ -0,0 +1,337 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+
+#include "fdt.hh"
+#include "checking.hh"
+
+using namespace dtc;
+
+/**
+ * The current major version of the tool.
+ */
+int version_major = 0;
+/**
+ * The current minor version of the tool.
+ */
+int version_minor = 4;
+/**
+ * The current patch level of the tool.
+ */
+int version_patch = 0;
+
+static void usage(const char* argv0)
+{
+ fprintf(stderr, "Usage:\n"
+ "\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]"
+ "[-E [no-]checker_name]\n"
+ "\t\t[-H phandle_format] [-I input_format]"
+ "[-O output_format]\n"
+ "\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]"
+ "[-V blob_version]\n"
+ "\t\t-W [no-]checker_name] input_file\n", basename(argv0));
+}
+
+/**
+ * Prints the current version of this program..
+ */
+static void version(const char* progname)
+{
+ fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major,
+ version_minor, version_patch);
+}
+
+using fdt::device_tree;
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int outfile = fileno(stdout);
+ const char *outfile_name = "-";
+ const char *in_file = "-";
+ FILE *depfile = 0;
+ bool debug_mode = false;
+ void (device_tree::*write_fn)(int) = &device_tree::write_binary;
+ void (device_tree::*read_fn)(const char*, FILE*) =
+ &device_tree::parse_dts;
+ uint32_t boot_cpu;
+ bool boot_cpu_specified = false;
+ bool keep_going = false;
+ bool sort = false;
+ clock_t c0 = clock();
+ class device_tree tree;
+ fdt::checking::check_manager checks;
+ const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:D";
+
+ // Don't forget to update the man page if any more options are added.
+ while ((ch = getopt(argc, argv, options)) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ case 'v':
+ version(argv[0]);
+ return EXIT_SUCCESS;
+ case 'I':
+ {
+ string arg = string(optarg);
+ if (arg == string("dtb"))
+ {
+ read_fn = &device_tree::parse_dtb;
+ }
+ else if (arg == string("dts"))
+ {
+ read_fn = &device_tree::parse_dts;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown input format: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ case 'O':
+ {
+ string arg = string(optarg);
+ if (arg == string("dtb"))
+ {
+ write_fn = &device_tree::write_binary;
+ }
+ else if (arg == string("asm"))
+ {
+ write_fn = &device_tree::write_asm;
+ }
+ else if (arg == string("dts"))
+ {
+ write_fn = &device_tree::write_dts;
+ }
+ else
+ {
+ fprintf(stderr, "Unknown output format: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ case 'o':
+ {
+ outfile_name = optarg;
+ outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ if (outfile == -1)
+ {
+ perror("Unable to open output file");
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ case 'D':
+ debug_mode = true;
+ break;
+ case 'V':
+ if (string(optarg) != string("17"))
+ {
+ fprintf(stderr, "Unknown output format version: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'd':
+ {
+ if (depfile != 0)
+ {
+ fclose(depfile);
+ }
+ if (string(optarg) == string("-"))
+ {
+ depfile = stdout;
+ }
+ else
+ {
+ depfile = fdopen(open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666), "w");
+ if (depfile == 0)
+ {
+ perror("Unable to open dependency file");
+ return EXIT_FAILURE;
+ }
+ }
+ break;
+ }
+ case 'H':
+ {
+ string arg = string(optarg);
+ if (arg == string("both"))
+ {
+ tree.set_phandle_format(device_tree::BOTH);
+ }
+ else if (arg == string("epapr"))
+ {
+ tree.set_phandle_format(device_tree::EPAPR);
+ }
+ else if (arg == string("linux"))
+ {
+ tree.set_phandle_format(device_tree::LINUX);
+ }
+ else
+ {
+ fprintf(stderr, "Unknown phandle format: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ case 'b':
+ // Don't bother to check if strtoll fails, just
+ // use the 0 it returns.
+ boot_cpu = (uint32_t)strtoll(optarg, 0, 10);
+ boot_cpu_specified = true;
+ break;
+ case 'f':
+ keep_going = true;
+ break;
+ case 'W':
+ case 'E':
+ {
+ string arg = string(optarg);
+ if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0))
+ {
+ arg = string(optarg+3);
+ if (!checks.disable_checker(arg))
+ {
+ fprintf(stderr, "Checker %s either does not exist or is already disabled\n", optarg+3);
+ }
+ break;
+ }
+ if (!checks.enable_checker(arg))
+ {
+ fprintf(stderr, "Checker %s either does not exist or is already enabled\n", optarg);
+ }
+ break;
+ }
+ case 's':
+ {
+ sort = true;
+ break;
+ }
+ case 'i':
+ {
+ tree.add_include_path(optarg);
+ break;
+ }
+ // Should quiet warnings, but for now is silently ignored.
+ case 'q':
+ break;
+ case 'R':
+ tree.set_empty_reserve_map_entries(strtoll(optarg, 0, 10));
+ break;
+ case 'S':
+ tree.set_blob_minimum_size(strtoll(optarg, 0, 10));
+ break;
+ case 'p':
+ tree.set_blob_padding(strtoll(optarg, 0, 10));
+ break;
+ default:
+ fprintf(stderr, "Unknown option %c\n", ch);
+ return EXIT_FAILURE;
+ }
+ }
+ if (optind < argc)
+ {
+ in_file = argv[optind];
+ }
+ if (depfile != 0)
+ {
+ fputs(outfile_name, depfile);
+ fputs(": ", depfile);
+ fputs(in_file, depfile);
+ }
+ clock_t c1 = clock();
+ (tree.*read_fn)(in_file, depfile);
+ // Override the boot CPU found in the header, if we're loading from dtb
+ if (boot_cpu_specified)
+ {
+ tree.set_boot_cpu(boot_cpu);
+ }
+ if (sort)
+ {
+ tree.sort();
+ }
+ if (depfile != 0)
+ {
+ putc('\n', depfile);
+ fclose(depfile);
+ }
+ if (!(tree.is_valid() || keep_going))
+ {
+ fprintf(stderr, "Failed to parse tree. Unhappy face!\n");
+ return EXIT_FAILURE;
+ }
+ clock_t c2 = clock();
+ if (!(checks.run_checks(&tree, true) || keep_going))
+ {
+ return EXIT_FAILURE;
+ }
+ clock_t c3 = clock();
+ (tree.*write_fn)(outfile);
+ close(outfile);
+ clock_t c4 = clock();
+
+ if (debug_mode)
+ {
+ struct rusage r;
+
+ getrusage(RUSAGE_SELF, &r);
+ fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss);
+ fprintf(stderr, "Setup and option parsing took %f seconds\n",
+ ((double)(c1-c0))/CLOCKS_PER_SEC);
+ fprintf(stderr, "Parsing took %f seconds\n",
+ ((double)(c2-c1))/CLOCKS_PER_SEC);
+ fprintf(stderr, "Checking took %f seconds\n",
+ ((double)(c3-c2))/CLOCKS_PER_SEC);
+ fprintf(stderr, "Generating output took %f seconds\n",
+ ((double)(c4-c3))/CLOCKS_PER_SEC);
+ fprintf(stderr, "Total time: %f seconds\n",
+ ((double)(c4-c0))/CLOCKS_PER_SEC);
+ // This is not needed, but keeps valgrind quiet.
+ fclose(stdin);
+ }
+ return EXIT_SUCCESS;
+}
+
diff --git a/usr.bin/dtc/fdt.cc b/usr.bin/dtc/fdt.cc
new file mode 100644
index 0000000..b6e7d56
--- /dev/null
+++ b/usr.bin/dtc/fdt.cc
@@ -0,0 +1,1361 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "fdt.hh"
+
+#include <algorithm>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "dtb.hh"
+
+namespace dtc
+{
+
+namespace fdt
+{
+
+uint32_t
+property_value::get_as_uint32()
+{
+ if (byte_data.size() != 4)
+ {
+ return 0;
+ }
+ uint32_t v = 0;
+ v &= byte_data[0] << 24;
+ v &= byte_data[1] << 16;
+ v &= byte_data[2] << 8;
+ v &= byte_data[3] << 0;
+ return v;
+}
+
+void
+property_value::push_to_buffer(byte_buffer &buffer)
+{
+ if (!byte_data.empty())
+ {
+ buffer.insert(buffer.end(), byte_data.begin(), byte_data.end());
+ }
+ else
+ {
+ string_data.push_to_buffer(buffer, true);
+ // Trailing nul
+ buffer.push_back(0);
+ }
+}
+
+void
+property_value::write_dts(FILE *file)
+{
+ resolve_type();
+ switch (type)
+ {
+ default:
+ assert(0 && "Invalid type");
+ case STRING:
+ case STRING_LIST:
+ case CROSS_REFERENCE:
+ write_as_string(file);
+ break;
+ case PHANDLE:
+ write_as_cells(file);
+ break;
+ case BINARY:
+ if (byte_data.size() % 4 == 0)
+ {
+ write_as_cells(file);
+ break;
+ }
+ write_as_bytes(file);
+ break;
+ }
+}
+
+void
+property_value::resolve_type()
+{
+ if (type != UNKNOWN)
+ {
+ return;
+ }
+ if (byte_data.empty())
+ {
+ type = STRING;
+ return;
+ }
+ if (byte_data.back() == 0)
+ {
+ bool is_all_printable = true;
+ int nuls = 0;
+ int bytes = 0;
+ for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end()-1; i<e ; i++)
+ {
+ bytes++;
+ is_all_printable &= (*i == '\0') || isprint(*i);
+ if (*i == '\0')
+ {
+ nuls++;
+ }
+ if (!is_all_printable)
+ {
+ break;
+ }
+ }
+ if (is_all_printable && (bytes > nuls))
+ {
+ type = STRING;
+ if (nuls > 0)
+ {
+ type = STRING_LIST;
+ }
+ return;
+ }
+ }
+ type = BINARY;
+}
+
+void
+property_value::write_as_string(FILE *file)
+{
+ putc('"', file);
+ if (byte_data.empty())
+ {
+ string_data.print(file);
+ }
+ else
+ {
+ for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end()-1; i!=e ; ++i)
+ {
+ // FIXME Escape tabs, newlines, and so on.
+ if (*i == '\0')
+ {
+ fputs("\", \"", file);
+ continue;
+ }
+ putc(*i, file);
+ }
+ }
+ putc('"', file);
+}
+
+void
+property_value::write_as_cells(FILE *file)
+{
+ putc('<', file);
+ assert((byte_data.size() % 4) == 0);
+ for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end(); i!=e ; ++i)
+ {
+ uint32_t v = 0;
+ v = (v << 8) | *i;
+ ++i;
+ v = (v << 8) | *i;
+ ++i;
+ v = (v << 8) | *i;
+ ++i;
+ v = (v << 8) | *i;
+ fprintf(file, "0x%" PRIx32, v);
+ if (i+1 != e)
+ {
+ putc(' ', file);
+ }
+ }
+ putc('>', file);
+}
+
+void
+property_value::write_as_bytes(FILE *file)
+{
+ putc('[', file);
+ for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end(); i!=e ; i++)
+ {
+ fprintf(file, "%hhx", *i);
+ if (i+1 != e)
+ {
+ putc(' ', file);
+ }
+ }
+ putc(']', file);
+}
+
+void
+property::parse_string(input_buffer &input)
+{
+ property_value v;
+ assert(input[0] == '"');
+ ++input;
+ const char *start = (const char*)input;
+ int length = 0;
+ while (char c = input[0])
+ {
+ if (c == '"' && input[-1] != '\\')
+ {
+ input.consume('"');
+ break;
+ }
+ ++input;
+ ++length;
+ }
+ v.string_data = string(start, length);
+ values.push_back(v);
+}
+
+void
+property::parse_cells(input_buffer &input)
+{
+ assert(input[0] == '<');
+ ++input;
+ property_value v;
+ input.next_token();
+ while (!input.consume('>'))
+ {
+ input.next_token();
+ // If this is a phandle then we need to get the name of the
+ // referenced node
+ if (input.consume('&'))
+ {
+ input.next_token();
+ // FIXME: We should support full paths here, but we
+ // don't.
+ string referenced = string::parse_node_name(input);
+ if (referenced.empty())
+ {
+ input.parse_error("Expected node name");
+ valid = false;
+ return;
+ }
+ input.next_token();
+ // If we already have some bytes, make the phandle a
+ // separate component.
+ if (!v.byte_data.empty())
+ {
+ values.push_back(v);
+ v = property_value();
+ }
+ v.string_data = referenced;
+ v.type = property_value::PHANDLE;
+ values.push_back(v);
+ v = property_value();
+ }
+ else
+ {
+ //FIXME: We should support labels in the middle
+ //of these, but we don't.
+ long long val;
+ if (!input.consume_integer(val))
+ {
+ input.parse_error("Expected numbers in array of cells");
+ valid = false;
+ return;
+ }
+ push_big_endian(v.byte_data, (uint32_t)val);
+ input.next_token();
+ }
+ }
+ // Don't store an empty string value here.
+ if (v.byte_data.size() > 0)
+ {
+ values.push_back(v);
+ }
+}
+
+void
+property::parse_bytes(input_buffer &input)
+{
+ assert(input[0] == '[');
+ ++input;
+ property_value v;
+ input.next_token();
+ while (!input.consume(']'))
+ {
+ {
+ //FIXME: We should support
+ //labels in the middle of
+ //these, but we don't.
+ uint8_t val;
+ if (!input.consume_hex_byte(val))
+ {
+ input.parse_error("Expected hex bytes in array of bytes");
+ valid = false;
+ return;
+ }
+ v.byte_data.push_back(val);
+ input.next_token();
+ }
+ }
+ values.push_back(v);
+}
+
+void
+property::parse_reference(input_buffer &input)
+{
+ assert(input[0] == '&');
+ ++input;
+ input.next_token();
+ property_value v;
+ v.string_data = string::parse_node_name(input);
+ if (v.string_data.empty())
+ {
+ input.parse_error("Expected node name");
+ valid = false;
+ return;
+ }
+ v.type = property_value::CROSS_REFERENCE;
+ values.push_back(v);
+}
+
+property::property(input_buffer &structs, input_buffer &strings)
+{
+ uint32_t name_offset;
+ uint32_t length;
+ valid = structs.consume_binary(length) &&
+ structs.consume_binary(name_offset);
+ if (!valid)
+ {
+ fprintf(stderr, "Failed to read property\n");
+ return;
+ }
+ // Find the name
+ input_buffer name_buffer = strings.buffer_from_offset(name_offset);
+ if (name_buffer.empty())
+ {
+ fprintf(stderr, "Property name offset %" PRIu32
+ " is past the end of the strings table\n",
+ name_offset);
+ valid = false;
+ return;
+ }
+ key = string(name_buffer);
+ // Read the value
+ uint8_t byte;
+ property_value v;
+ for (uint32_t i=0 ; i<length ; i++)
+ {
+ if (!(valid = structs.consume_binary(byte)))
+ {
+ fprintf(stderr, "Failed to read property value\n");
+ return;
+ }
+ v.byte_data.push_back(byte);
+ }
+ values.push_back(v);
+}
+
+property::property(input_buffer &input, string k, string l) : key(k), label(l),
+ valid(true)
+{
+ do {
+ input.next_token();
+ switch (input[0])
+ {
+ default:
+ input.parse_error("Invalid property value.");
+ valid = false;
+ return;
+ case '"':
+ parse_string(input);
+ break;
+ case '<':
+ parse_cells(input);
+ break;
+ case '[':
+ parse_bytes(input);
+ break;
+ case '&':
+ parse_reference(input);
+ break;
+ case ';':
+ {
+ break;
+ }
+ }
+ input.next_token();
+ } while (input.consume(','));
+ if (!input.consume(';'))
+ {
+ input.parse_error("Expected ; at end of property");
+ valid = false;
+ }
+}
+
+property*
+property::parse_dtb(input_buffer &structs, input_buffer &strings)
+{
+ property *p = new property(structs, strings);
+ if (!p->valid)
+ {
+ delete p;
+ p = 0;
+ }
+ return p;
+}
+
+property*
+property::parse(input_buffer &input, string key, string label)
+{
+ property *p = new property(input, key, label);
+ if (!p->valid)
+ {
+ delete p;
+ p = 0;
+ }
+ return p;
+}
+
+void
+property::write(dtb::output_writer &writer, dtb::string_table &strings)
+{
+ writer.write_token(dtb::FDT_PROP);
+ byte_buffer value_buffer;
+ for (value_iterator i=begin(), e=end() ; i!=e ; ++i)
+ {
+ i->push_to_buffer(value_buffer);
+ }
+ writer.write_data((uint32_t)value_buffer.size());
+ writer.write_comment(key);
+ writer.write_data(strings.add_string(key));
+ writer.write_data(value_buffer);
+}
+
+void
+property::write_dts(FILE *file, int indent)
+{
+ for (int i=0 ; i<indent ; i++)
+ {
+ putc('\t', file);
+ }
+ if (label != string())
+ {
+ label.print(file);
+ fputs(": ", file);
+ }
+ if (key != string())
+ {
+ key.print(file);
+ }
+ if (!values.empty())
+ {
+ fputs(" = ", file);
+ for (value_iterator i=begin(), e=end() ; i!=e ; ++i)
+ {
+ i->write_dts(file);
+ if (i+1 != e)
+ {
+ putc(',', file);
+ putc(' ', file);
+ }
+ }
+ }
+ fputs(";\n", file);
+}
+
+string
+node::parse_name(input_buffer &input, bool &is_property, const char *error)
+{
+ if (!valid)
+ {
+ return string();
+ }
+ input.next_token();
+ if (is_property)
+ {
+ return string::parse_property_name(input);
+ }
+ string n = string::parse_node_or_property_name(input, is_property);
+ if (n.empty())
+ {
+ if (n.empty())
+ {
+ input.parse_error(error);
+ valid = false;
+ }
+ }
+ return n;
+}
+
+node::node(input_buffer &structs, input_buffer &strings) : valid(true)
+{
+ const char *name_start = (const char*)structs;
+ int name_length = 0;
+ while (structs[0] != '\0' && structs[0] != '@')
+ {
+ name_length++;
+ ++structs;
+ }
+ name = string(name_start, name_length);
+ if (structs[0] == '@')
+ {
+ ++structs;
+ name_start = (const char*)structs;
+ name_length = 0;
+ while (structs[0] != '\0')
+ {
+ name_length++;
+ ++structs;
+ }
+ unit_address = string(name_start, name_length);
+ }
+ ++structs;
+ uint32_t token;
+ while (structs.consume_binary(token))
+ {
+ switch (token)
+ {
+ default:
+ fprintf(stderr, "Unexpected token 0x%" PRIx32
+ " while parsing node.\n", token);
+ valid = false;
+ return;
+ // Child node, parse it.
+ case dtb::FDT_BEGIN_NODE:
+ {
+ node *child = node::parse_dtb(structs, strings);
+ if (child == 0)
+ {
+ valid = false;
+ return;
+ }
+ children.push_back(child);
+ break;
+ }
+ // End of this node, no errors.
+ case dtb::FDT_END_NODE:
+ return;
+ // Property, parse it.
+ case dtb::FDT_PROP:
+ {
+ property *prop = property::parse_dtb(structs, strings);
+ if (prop == 0)
+ {
+ valid = false;
+ return;
+ }
+ properties.push_back(prop);
+ break;
+ }
+ break;
+ // End of structs table. Should appear after
+ // the end of the last node.
+ case dtb::FDT_END:
+ fprintf(stderr, "Unexpected FDT_END token while parsing node.\n");
+ valid = false;
+ return;
+ // NOPs are padding. Ignore them.
+ case dtb::FDT_NOP:
+ break;
+ }
+ }
+ fprintf(stderr, "Failed to read token from structs table while parsing node.\n");
+ valid = false;
+ return;
+}
+
+node::node(input_buffer &input, string n, string l, string a) :
+ label(l), name(n), unit_address(a), valid(true)
+{
+ if (!input.consume('{'))
+ {
+ input.parse_error("Expected { to start new device tree node.\n");
+ }
+ input.next_token();
+ while (valid && !input.consume('}'))
+ {
+ // flag set if we find any characters that are only in
+ // the property name character set, not the node
+ bool is_property = false;
+ string child_name, child_label, child_address;
+ child_name = parse_name(input, is_property,
+ "Expected property or node name");
+ if (input.consume(':'))
+ {
+ // Node labels can contain any characters? The
+ // spec doesn't say, so we guess so...
+ is_property = false;
+ child_label = child_name;
+ child_name = parse_name(input, is_property, "Expected property or node name");
+ }
+ if (input.consume('@'))
+ {
+ child_address = parse_name(input, is_property, "Expected unit address");
+ }
+ if (!valid)
+ {
+ return;
+ }
+ input.next_token();
+ // If we're parsing a property, then we must actually do that.
+ if (input.consume('='))
+ {
+ property *p= property::parse(input, child_name,
+ child_label);
+ if (p == 0)
+ {
+ valid = false;
+ }
+ else
+ {
+ properties.push_back(p);
+ }
+ }
+ else if (!is_property && input[0] == ('{'))
+ {
+ node *child = node::parse(input, child_name,
+ child_label, child_address);
+ if (child)
+ {
+ children.push_back(child);
+ }
+ else
+ {
+ valid = false;
+ }
+ }
+ else if (input.consume(';'))
+ {
+ properties.push_back(new property(child_name, child_label));
+ }
+ else
+ {
+ input.parse_error("Error parsing property.");
+ valid = false;
+ }
+ input.next_token();
+ }
+ input.consume(';');
+}
+
+bool
+node::cmp_properties(property *p1, property *p2)
+{
+ return p1->get_key() < p2->get_key();
+}
+
+bool
+node::cmp_children(node *c1, node *c2)
+{
+ if (c1->name == c2->name)
+ {
+ return c1->unit_address < c2->unit_address;
+ }
+ return c1->name < c2->name;
+}
+
+void
+node::sort()
+{
+ std::sort(property_begin(), property_end(), cmp_properties);
+ std::sort(child_begin(), child_end(), cmp_children);
+ for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+ {
+ (*i)->sort();
+ }
+}
+
+node*
+node::parse(input_buffer &input, string name, string label, string address)
+{
+ node *n = new node(input, name, label, address);
+ if (!n->valid)
+ {
+ delete n;
+ n = 0;
+ }
+ return n;
+}
+
+node*
+node::parse_dtb(input_buffer &structs, input_buffer &strings)
+{
+ node *n = new node(structs, strings);
+ if (!n->valid)
+ {
+ delete n;
+ n = 0;
+ }
+ return n;
+}
+
+node::~node()
+{
+ while (!children.empty())
+ {
+ delete children.back();
+ children.pop_back();
+ }
+ while (!properties.empty())
+ {
+ delete properties.back();
+ properties.pop_back();
+ }
+}
+
+property*
+node::get_property(string key)
+{
+ for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
+ {
+ if ((*i)->get_key() == key)
+ {
+ return *i;
+ }
+ }
+ return 0;
+}
+
+void
+node::merge_node(node *other)
+{
+ if (!other->label.empty())
+ {
+ label = other->label;
+ }
+ // Note: this is an O(n*m) operation. It might be sensible to
+ // optimise this if we find that there are nodes with very
+ // large numbers of properties, but for typical usage the
+ // entire vector will fit (easily) into cache, so iterating
+ // over it repeatedly isn't that expensive.
+ while (!other->properties.empty())
+ {
+ property *p = other->properties.front();
+ for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
+ {
+ if ((*i)->get_key() == p->get_key())
+ {
+ delete *i;
+ properties.erase(i);
+ break;
+ }
+ }
+ add_property(p);
+ other->properties.erase(other->properties.begin());
+ }
+ while (!other->children.empty())
+ {
+ node *c = other->children.front();
+ bool found = false;
+ for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+ {
+ if ((*i)->name == c->name && (*i)->unit_address == c->unit_address)
+ {
+ (*i)->merge_node(c);
+ delete c;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ children.push_back(c);
+ }
+ other->children.erase(other->children.begin());
+ }
+}
+
+void
+node::write(dtb::output_writer &writer, dtb::string_table &strings)
+{
+ writer.write_token(dtb::FDT_BEGIN_NODE);
+ byte_buffer name_buffer;
+ name.push_to_buffer(name_buffer);
+ if (unit_address != string())
+ {
+ name_buffer.push_back('@');
+ unit_address.push_to_buffer(name_buffer);
+ }
+ writer.write_comment(name);
+ writer.write_data(name_buffer);
+ writer.write_data((uint8_t)0);
+ for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
+ {
+ (*i)->write(writer, strings);
+ }
+ for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+ {
+ (*i)->write(writer, strings);
+ }
+ writer.write_token(dtb::FDT_END_NODE);
+}
+
+void
+node::write_dts(FILE *file, int indent)
+{
+ for (int i=0 ; i<indent ; i++)
+ {
+ putc('\t', file);
+ }
+ if (label != string())
+ {
+ label.print(file);
+ fputs(": ", file);
+ }
+ if (name != string())
+ {
+ name.print(file);
+ }
+ if (unit_address != string())
+ {
+ putc('@', file);
+ unit_address.print(file);
+ }
+ fputs(" {\n\n", file);
+ for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
+ {
+ (*i)->write_dts(file, indent+1);
+ }
+ for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+ {
+ (*i)->write_dts(file, indent+1);
+ }
+ for (int i=0 ; i<indent ; i++)
+ {
+ putc('\t', file);
+ }
+ fputs("};\n", file);
+}
+
+void
+device_tree::collect_names_recursive(node* n, node_path &path)
+{
+ string name = n->label;
+ path.push_back(std::make_pair(n->name, n->unit_address));
+ if (name != string())
+ {
+ if (node_names.find(name) == node_names.end())
+ {
+ node_names.insert(std::make_pair(name, n));
+ node_paths.insert(std::make_pair(name, path));
+ }
+ else
+ {
+ node_names[name] = (node*)-1;
+ std::map<string, node_path>::iterator i = node_paths.find(name);
+ if (i != node_paths.end())
+ {
+ node_paths.erase(name);
+ }
+ fprintf(stderr, "Label not unique: ");
+ name.dump();
+ fprintf(stderr, ". References to this label will not be resolved.");
+ }
+ }
+ for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ; ++i)
+ {
+ collect_names_recursive(*i, path);
+ }
+ path.pop_back();
+ // Now we collect the phandles and properties that reference
+ // other nodes.
+ for (node::property_iterator i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
+ {
+ for (property::value_iterator p=(*i)->begin(),pe=(*i)->end() ; p!=pe ; ++p)
+ {
+ if (p->is_phandle())
+ {
+ phandles.push_back(&*p);
+ }
+ if (p->is_cross_reference())
+ {
+ cross_references.push_back(&*p);
+ }
+ }
+ if ((*i)->get_key() == string("phandle") ||
+ (*i)->get_key() == string("linux,phandle"))
+ {
+ if ((*i)->begin()->byte_data.size() != 4)
+ {
+ fprintf(stderr, "Invalid phandle value for node ");
+ n->name.dump();
+ fprintf(stderr, ". Should be a 4-byte value.\n");
+ valid = false;
+ }
+ else
+ {
+ uint32_t phandle = (*i)->begin()->get_as_uint32();
+ used_phandles.insert(std::make_pair(phandle, n));
+ }
+ }
+ }
+}
+
+void
+device_tree::collect_names()
+{
+ node_path p;
+ collect_names_recursive(root, p);
+}
+
+void
+device_tree::resolve_cross_references()
+{
+ for (std::vector<property_value*>::iterator i=cross_references.begin(), e=cross_references.end() ; i!=e ; ++i)
+ {
+ property_value* pv = *i;
+ node_path path = node_paths[pv->string_data];
+ // Skip the first name in the path. It's always "", and implicitly /
+ for (node_path::iterator p=path.begin()+1, pe=path.end() ; p!=pe ; ++p)
+ {
+ pv->byte_data.push_back('/');
+ p->first.push_to_buffer(pv->byte_data);
+ if (!(p->second.empty()))
+ {
+ pv->byte_data.push_back('@');
+ p->second.push_to_buffer(pv->byte_data);
+ }
+ }
+ pv->byte_data.push_back(0);
+ }
+ uint32_t phandle = 1;
+ for (std::vector<property_value*>::iterator i=phandles.begin(), e=phandles.end() ; i!=e ; ++i)
+ {
+ string target_name = (*i)->string_data;
+ node *target = node_names[target_name];
+ if (target == 0)
+ {
+ fprintf(stderr, "Failed to find node with label:");
+ target_name.dump();
+ fprintf(stderr, "\n");
+ valid = 0;
+ return;
+ }
+ // If there is an existing phandle, use it
+ property *p = target->get_property("phandle");
+ if (p == 0)
+ {
+ p = target->get_property("linux,phandle");
+ }
+ if (p == 0)
+ {
+ // Otherwise insert a new phandle node
+ property_value v;
+ while (used_phandles.find(phandle) != used_phandles.end())
+ {
+ // Note that we only don't need to
+ // store this phandle in the set,
+ // because we are monotonically
+ // increasing the value of phandle and
+ // so will only ever revisit this value
+ // if we have used 2^32 phandles, at
+ // which point our blob won't fit in
+ // any 32-bit system and we've done
+ // something badly wrong elsewhere
+ // already.
+ phandle++;
+ }
+ push_big_endian(v.byte_data, phandle++);
+ if (phandle_node_name == BOTH || phandle_node_name == LINUX)
+ {
+ p = new property(string("linux,phandle"));
+ p->add_value(v);
+ target->add_property(p);
+ }
+ if (phandle_node_name == BOTH || phandle_node_name == EPAPR)
+ {
+ p = new property(string("phandle"));
+ p->add_value(v);
+ target->add_property(p);
+ }
+ }
+ p->begin()->push_to_buffer((*i)->byte_data);
+ assert((*i)->byte_data.size() == 4);
+ }
+}
+
+void
+device_tree::parse_roots(input_buffer &input, std::vector<node*> &roots)
+{
+ input.next_token();
+ while (valid && input.consume('/'))
+ {
+ input.next_token();
+ node *n = node::parse(input, string("", 1));
+ if (n)
+ {
+ roots.push_back(n);
+ }
+ else
+ {
+ valid = false;
+ }
+ }
+}
+
+input_buffer*
+device_tree::buffer_for_file(const char *path)
+{
+ if (string(path) == string("-"))
+ {
+ input_buffer *b = new stream_input_buffer();
+ buffers.push_back(b);
+ return b;
+ }
+ int source = open(path, O_RDONLY);
+ if (source == -1)
+ {
+ fprintf(stderr, "Unable to open file %s\n", path);
+ return 0;
+ }
+ input_buffer *b = new mmap_input_buffer(source);
+ // Keep the buffer that owns the memory around for the lifetime
+ // of this FDT. Ones simply referring to it may have shorter
+ // lifetimes.
+ buffers.push_back(b);
+ close(source);
+ return b;
+}
+
+template<class writer> void
+device_tree::write(int fd)
+{
+ dtb::string_table st;
+ dtb::header head;
+ writer head_writer;
+ writer reservation_writer;
+ writer struct_writer;
+ writer strings_writer;
+
+ // Build the reservation table
+ reservation_writer.write_comment(string("Memory reservations"));
+ reservation_writer.write_label(string("dt_reserve_map"));
+ for (std::vector<reservation>::iterator i=reservations.begin(),
+ e=reservations.end() ; i!=e ; ++i)
+ {
+ reservation_writer.write_comment(string("Reservation start"));
+ reservation_writer.write_data(i->first);
+ reservation_writer.write_comment(string("Reservation length"));
+ reservation_writer.write_data(i->first);
+ }
+ // Write n spare reserve map entries, plus the trailing 0.
+ for (uint32_t i=0 ; i<=spare_reserve_map_entries ; i++)
+ {
+ reservation_writer.write_data((uint64_t)0);
+ reservation_writer.write_data((uint64_t)0);
+ }
+
+
+ struct_writer.write_comment(string("Device tree"));
+ struct_writer.write_label(string("dt_struct_start"));
+ root->write(struct_writer, st);
+ struct_writer.write_token(dtb::FDT_END);
+ struct_writer.write_label(string("dt_struct_end"));
+
+ st.write(strings_writer);
+ // Find the strings size before we stick padding on the end.
+ // Note: We should possibly use a new writer for the padding.
+ head.size_dt_strings = strings_writer.size();
+
+ // Stick the padding in the strings writer, but after the
+ // marker indicating that it's the end.
+ // Note: We probably should add a padding call to the writer so
+ // that the asm back end can write padding directives instead
+ // of a load of 0 bytes.
+ for (uint32_t i=0 ; i<blob_padding ; i++)
+ {
+ strings_writer.write_data((uint8_t)0);
+ }
+ head.totalsize = sizeof(head) + strings_writer.size() +
+ struct_writer.size() + reservation_writer.size();
+ while (head.totalsize < minimum_blob_size)
+ {
+ head.totalsize++;
+ strings_writer.write_data((uint8_t)0);
+ }
+ head.off_dt_struct = sizeof(head) + reservation_writer.size();;
+ head.off_dt_strings = head.off_dt_struct + struct_writer.size();
+ head.off_mem_rsvmap = sizeof(head);
+ head.boot_cpuid_phys = boot_cpu;
+ head.size_dt_struct = struct_writer.size();
+ head.write(head_writer);
+
+ head_writer.write_to_file(fd);
+ reservation_writer.write_to_file(fd);
+ struct_writer.write_to_file(fd);
+ strings_writer.write_label(string("dt_blob_end"));
+ strings_writer.write_to_file(fd);
+}
+
+node*
+device_tree::referenced_node(property_value &v)
+{
+ if (v.is_phandle())
+ {
+ return node_names[v.string_data];
+ }
+ if (v.is_binary())
+ {
+ return used_phandles[v.get_as_uint32()];
+ }
+ return 0;
+}
+
+void
+device_tree::write_binary(int fd)
+{
+ write<dtb::binary_writer>(fd);
+}
+
+void
+device_tree::write_asm(int fd)
+{
+ write<dtb::asm_writer>(fd);
+}
+
+void
+device_tree::write_dts(int fd)
+{
+ FILE *file = fdopen(fd, "w");
+ fputs("/dtc-v1/;\n\n", file);
+
+ if (!reservations.empty())
+ {
+ const char msg[] = "/memreserve/";
+ fwrite(msg, sizeof(msg), 1, file);
+ for (std::vector<reservation>::iterator i=reservations.begin(),
+ e=reservations.end() ; i!=e ; ++i)
+ {
+ fprintf(stderr, " %" PRIx64 " %" PRIx64, i->first, i->second);
+ }
+ fputs(";\n\n", file);
+ }
+ putc('/', file);
+ putc(' ', file);
+ root->write_dts(file, 0);
+ fclose(file);
+}
+
+void
+device_tree::parse_dtb(const char *fn, FILE *depfile)
+{
+ input_buffer *in = buffer_for_file(fn);
+ if (in == 0)
+ {
+ valid = false;
+ return;
+ }
+ input_buffer &input = *in;
+ dtb::header h;
+ valid = h.read_dtb(input);
+ boot_cpu = h.boot_cpuid_phys;
+ if (h.last_comp_version > 17)
+ {
+ fprintf(stderr, "Don't know how to read this version of the device tree blob");
+ valid = false;
+ }
+ if (!valid)
+ {
+ return;
+ }
+ input_buffer reservation_map =
+ input.buffer_from_offset(h.off_mem_rsvmap, 0);
+ uint64_t start, length;
+ do
+ {
+ if (!(reservation_map.consume_binary(start) &&
+ reservation_map.consume_binary(length)))
+ {
+ fprintf(stderr, "Failed to read memory reservation table\n");
+ valid = false;
+ return;
+ }
+ } while (!((start == 0) && (length == 0)));
+ input_buffer struct_table =
+ input.buffer_from_offset(h.off_dt_struct, h.size_dt_struct);
+ input_buffer strings_table =
+ input.buffer_from_offset(h.off_dt_strings, h.size_dt_strings);
+ uint32_t token;
+ if (!(struct_table.consume_binary(token) &&
+ (token == dtb::FDT_BEGIN_NODE)))
+ {
+ fprintf(stderr, "Expected FDT_BEGIN_NODE token.\n");
+ valid = false;
+ return;
+ }
+ root = node::parse_dtb(struct_table, strings_table);
+ if (!(struct_table.consume_binary(token) && (token == dtb::FDT_END)))
+ {
+ fprintf(stderr, "Expected FDT_END token after parsing root node.\n");
+ valid = false;
+ return;
+ }
+ valid = (root != 0);
+}
+
+void
+device_tree::parse_dts(const char *fn, FILE *depfile)
+{
+ input_buffer *in = buffer_for_file(fn);
+ if (in == 0)
+ {
+ valid = false;
+ return;
+ }
+ std::vector<node*> roots;
+ input_buffer &input = *in;
+ input.next_token();
+ bool read_header = false;
+ // Read the header
+ if (input.consume("/dts-v1/;"))
+ {
+ read_header = true;
+ }
+ input.next_token();
+ while(input.consume("/include/"))
+ {
+ input.next_token();
+ if (!input.consume('"'))
+ {
+ input.parse_error("Expected quoted filename");
+ valid = false;
+ return;
+ }
+ int length = 0;
+ while (input[length] != '"') length++;
+
+ const char *file = (const char*)input;
+ const char *dir = dirname(fn);
+ int dir_length = strlen(dir);
+ char *include_file = (char*)malloc(strlen(dir) + length + 2);
+ memcpy(include_file, dir, dir_length);
+ include_file[dir_length] = '/';
+ memcpy(include_file+dir_length+1, file, length);
+ include_file[dir_length+length+1] = 0;
+ input_buffer *include_buffer = buffer_for_file(include_file);
+
+ if (include_buffer == 0)
+ {
+ for (std::vector<const char*>::iterator i=include_paths.begin(), e=include_paths.end() ; e!=i ; ++i)
+ {
+ free(include_file);
+ dir = *i;
+ dir_length = strlen(dir);
+ include_file = (char*)malloc(strlen(dir) +
+ length + 2);
+ memcpy(include_file, dir, dir_length);
+ include_file[dir_length] = '/';
+ memcpy(include_file+dir_length+1, file, length);
+ include_file[dir_length+length+1] = 0;
+ include_buffer = buffer_for_file(include_file);
+ if (include_buffer != 0)
+ {
+ break;
+ }
+ }
+ }
+ if (depfile != 0)
+ {
+ putc(' ', depfile);
+ fputs(include_file, depfile);
+ }
+ if (include_buffer == 0)
+ {
+ valid = false;
+ return;
+ }
+ input_buffer &include = *include_buffer;
+ input.consume(include_file+dir_length+1);
+ input.consume('"');
+ free((void*)include_file);
+
+ if (!read_header)
+ {
+ include.next_token();
+ read_header = include.consume("/dts-v1/;");
+ }
+ parse_roots(include, roots);
+ }
+ input.next_token();
+ if (!read_header)
+ {
+ input.parse_error("Expected /dts-v1/; version string");
+ }
+ // Read any memory reservations
+ while(input.consume("/memreserve/"))
+ {
+ long long start, len;
+ input.next_token();
+ // Read the start and length.
+ if (!(input.consume_integer(start) &&
+ (input.next_token(),
+ input.consume_integer(len))))
+ {
+ input.parse_error("Expected /dts-v1/; version string");
+ }
+ input.next_token();
+ input.consume(';');
+ reservations.push_back(reservation(start, len));
+ }
+ parse_roots(input, roots);
+ switch (roots.size())
+ {
+ case 0:
+ valid = false;
+ input.parse_error("Failed to find root node /.");
+ return;
+ case 1:
+ root = roots[0];
+ break;
+ default:
+ {
+ root = roots[0];
+ for (std::vector<node*>::iterator i=roots.begin()+1,
+ e=roots.end() ; i!=e ; ++i)
+ {
+ root->merge_node(*i);
+ delete *i;
+ }
+ roots.resize(1);
+ }
+ }
+ collect_names();
+ resolve_cross_references();
+}
+
+device_tree::~device_tree()
+{
+ if (root != 0)
+ {
+ delete root;
+ }
+ while (!buffers.empty())
+ {
+ delete buffers.back();
+ buffers.pop_back();
+ }
+}
+
+} // namespace fdt
+
+} // namespace dtc
+
diff --git a/usr.bin/dtc/fdt.hh b/usr.bin/dtc/fdt.hh
new file mode 100644
index 0000000..b00ea48
--- /dev/null
+++ b/usr.bin/dtc/fdt.hh
@@ -0,0 +1,782 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FDT_HH_
+#define _FDT_HH_
+#include <map>
+
+#include "util.hh"
+#include "string.hh"
+
+namespace dtc
+{
+
+namespace dtb
+{
+struct output_writer;
+class string_table;
+}
+
+namespace fdt
+{
+/**
+ * Properties may contain a number of different value, each with a different
+ * label. This class encapsulates a single value.
+ */
+struct property_value
+{
+ /**
+ * The label for this data. This is usually empty.
+ */
+ string label;
+ /**
+ * If this value is a string, or something resolved from a string (a
+ * reference) then this contains the source string.
+ */
+ string string_data;
+ /**
+ * The data that should be written to the final output.
+ */
+ byte_buffer byte_data;
+ /**
+ * Enumeration describing the possible types of a value. Note that
+ * property-coded arrays will appear simply as binary (or possibly
+ * string, if they happen to be nul-terminated and printable), and must
+ * be checked separately.
+ */
+ enum value_type
+ {
+ /**
+ * This is a list of strings. When read from source, string
+ * lists become one property value for each string, however
+ * when read from binary we have a single property value
+ * incorporating the entire text, with nul bytes separating the
+ * strings.
+ */
+ STRING_LIST,
+ /**
+ * This property contains a single string.
+ */
+ STRING,
+ /**
+ * This is a binary value. Check the size of byte_data to
+ * determine how many bytes this contains.
+ */
+ BINARY,
+ /** This contains a short-form address that should be replaced
+ * by a fully-qualified version. This will only appear when
+ * the input is a device tree source. When parsed from a
+ * device tree blob, the cross reference will have already been
+ * resolved and the property value will be a string containing
+ * the full path of the target node. */
+ CROSS_REFERENCE,
+ /**
+ * This is a phandle reference. When parsed from source, the
+ * string_data will contain the node label for the target and,
+ * after cross references have been resolved, the binary data
+ * will contain a 32-bit integer that should match the phandle
+ * property of the target node.
+ */
+ PHANDLE,
+ /**
+ * An empty property value. This will never appear on a real
+ * property value, it is used by checkers to indicate that no
+ * property values should exist for a property.
+ */
+ EMPTY,
+ /**
+ * The type of this property has not yet been determined.
+ */
+ UNKNOWN
+ };
+ /**
+ * The type of this property.
+ */
+ value_type type;
+ /**
+ * Returns true if this value is a cross reference, false otherwise.
+ */
+ inline bool is_cross_reference()
+ {
+ return is_type(CROSS_REFERENCE);
+ }
+ /**
+ * Returns true if this value is a phandle reference, false otherwise.
+ */
+ inline bool is_phandle()
+ {
+ return is_type(PHANDLE);
+ }
+ /**
+ * Returns true if this value is a string, false otherwise.
+ */
+ inline bool is_string()
+ {
+ return is_type(STRING);
+ }
+ /**
+ * Returns true if this value is a string list (a nul-separated
+ * sequence of strings), false otherwise.
+ */
+ inline bool is_string_list()
+ {
+ return is_type(STRING_LIST);
+ }
+ /**
+ * Returns true if this value is binary, false otherwise.
+ */
+ inline bool is_binary()
+ {
+ return is_type(BINARY);
+ }
+ /**
+ * Returns this property value as a 32-bit integer. Returns 0 if this
+ * property value is not 32 bits long. The bytes in the property value
+ * are assumed to be in big-endian format, but the return value is in
+ * the host native endian.
+ */
+ uint32_t get_as_uint32();
+ /**
+ * Default constructor, specifying the label of the value.
+ */
+ property_value(string l=string()) : label(l), type(UNKNOWN) {}
+ /**
+ * Writes the data for this value into an output buffer.
+ */
+ void push_to_buffer(byte_buffer &buffer);
+
+ /**
+ * Writes the property value to the standard output. This uses the
+ * following heuristics for deciding how to print the output:
+ *
+ * - If the value is nul-terminated and only contains printable
+ * characters, it is written as a string.
+ * - If it is a multiple of 4 bytes long, then it is printed as cells.
+ * - Otherwise, it is printed as a byte buffer.
+ */
+ void write_dts(FILE *file);
+ private:
+ /**
+ * Returns whether the value is of the specified type. If the type of
+ * the value has not yet been determined, then this calculates it.
+ */
+ inline bool is_type(value_type v)
+ {
+ if (type == UNKNOWN)
+ {
+ resolve_type();
+ }
+ return type == v;
+ }
+ /**
+ * Determines the type of the value based on its contents.
+ */
+ void resolve_type();
+ /**
+ * Writes the property value to the specified file as a quoted string.
+ * This is used when generating DTS.
+ */
+ void write_as_string(FILE *file);
+ /**
+ * Writes the property value to the specified file as a sequence of
+ * 32-bit big-endian cells. This is used when generating DTS.
+ */
+ void write_as_cells(FILE *file);
+ /**
+ * Writes the property value to the specified file as a sequence of
+ * bytes. This is used when generating DTS.
+ */
+ void write_as_bytes(FILE *file);
+};
+
+/**
+ * A value encapsulating a single property. This contains a key, optionally a
+ * label, and optionally one or more values.
+ */
+class property
+{
+ /**
+ * The name of this property.
+ */
+ string key;
+ /**
+ * An optional label.
+ */
+ string label;
+ /**
+ * The values in this property.
+ */
+ std::vector<property_value> values;
+ /**
+ * Value indicating that this is a valid property. If a parse error
+ * occurs, then this value is false.
+ */
+ bool valid;
+ /**
+ * Parses a string property value, i.e. a value enclosed in double quotes.
+ */
+ void parse_string(input_buffer &input);
+ /**
+ * Parses one or more 32-bit values enclosed in angle brackets.
+ */
+ void parse_cells(input_buffer &input);
+ /**
+ * Parses an array of bytes, contained within square brackets.
+ */
+ void parse_bytes(input_buffer &input);
+ /**
+ * Parses a reference. This is a node label preceded by an ampersand
+ * symbol, which should expand to the full path to that node.
+ *
+ * Note: The specification says that the target of such a reference is
+ * a node name, however dtc assumes that it is a label, and so we
+ * follow their interpretation for compatibility.
+ */
+ void parse_reference(input_buffer &input);
+ /**
+ * Constructs a new property from two input buffers, pointing to the
+ * struct and strings tables in the device tree blob, respectively.
+ * The structs input buffer is assumed to have just consumed the
+ * FDT_PROP token.
+ */
+ property(input_buffer &structs, input_buffer &strings);
+ /**
+ * Parses a new property from the input buffer.
+ */
+ property(input_buffer &input, string k, string l);
+ public:
+ /**
+ * Creates an empty property.
+ */
+ property(string k, string l=string()) : key(k), label(l), valid(true)
+ {}
+ /**
+ * Copy constructor.
+ */
+ property(property &p) : key(p.key), label(p.label), values(p.values),
+ valid(p.valid) {}
+ /**
+ * Factory method for constructing a new property. Attempts to parse a
+ * property from the input, and returns it on success. On any parse
+ * error, this will return 0.
+ */
+ static property* parse_dtb(input_buffer &structs,
+ input_buffer &strings);
+ /**
+ * Factory method for constructing a new property. Attempts to parse a
+ * property from the input, and returns it on success. On any parse
+ * error, this will return 0.
+ */
+ static property* parse(input_buffer &input,
+ string key,
+ string label=string());
+ /**
+ * Iterator type used for accessing the values of a property.
+ */
+ typedef std::vector<property_value>::iterator value_iterator;
+ /**
+ * Returns an iterator referring to the first value in this property.
+ */
+ inline value_iterator begin()
+ {
+ return values.begin();
+ }
+ /**
+ * Returns an iterator referring to the last value in this property.
+ */
+ inline value_iterator end()
+ {
+ return values.end();
+ }
+ /**
+ * Adds a new value to an existing property.
+ */
+ inline void add_value(property_value v)
+ {
+ values.push_back(v);
+ }
+ /**
+ * Returns the key for this property.
+ */
+ inline string get_key()
+ {
+ return key;
+ }
+ /**
+ * Writes the property to the specified writer. The property name is a
+ * reference into the strings table.
+ */
+ void write(dtb::output_writer &writer, dtb::string_table &strings);
+ /**
+ * Writes in DTS format to the specified file, at the given indent
+ * level. This will begin the line with the number of tabs specified
+ * as the indent level and then write the property in the most
+ * applicable way that it can determine.
+ */
+ void write_dts(FILE *file, int indent);
+};
+
+/**
+ * Class encapsulating a device tree node. Nodes may contain properties and
+ * other nodes.
+ */
+class node
+{
+ public:
+ /**
+ * The label for this node, if any. Node labels are used as the
+ * targets for cross references.
+ */
+ string label;
+ /**
+ * The name of the node.
+ */
+ string name;
+ /**
+ * The unit address of the node, which is optionally written after the
+ * name followed by an at symbol.
+ */
+ string unit_address;
+ private:
+ /**
+ * The properties contained within this node.
+ */
+ std::vector<property*> properties;
+ /**
+ * The children of this node.
+ */
+ std::vector<node*> children;
+ /**
+ * A flag indicating whether this node is valid. This is set to false
+ * if an error occurs during parsing.
+ */
+ bool valid;
+ /**
+ * Parses a name inside a node, writing the string passed as the last
+ * argument as an error if it fails.
+ */
+ string parse_name(input_buffer &input,
+ bool &is_property,
+ const char *error);
+ /**
+ * Constructs a new node from two input buffers, pointing to the struct
+ * and strings tables in the device tree blob, respectively.
+ */
+ node(input_buffer &structs, input_buffer &strings);
+ /**
+ * Parses a new node from the specified input buffer. This is called
+ * when the input cursor is on the open brace for the start of the
+ * node. The name, and optionally label and unit address, should have
+ * already been parsed.
+ */
+ node(input_buffer &input, string n, string l, string a);
+ /**
+ * Comparison function for properties, used when sorting the properties
+ * vector. Orders the properties based on their names.
+ */
+ static inline bool cmp_properties(property *p1, property *p2);
+ /*
+ {
+ return p1->get_key() < p2->get_key();
+ }
+ */
+ /**
+ * Comparison function for nodes, used when sorting the children
+ * vector. Orders the nodes based on their names or, if the names are
+ * the same, by the unit addresses.
+ */
+ static inline bool cmp_children(node *c1, node *c2);
+ /*
+ {
+ if (c1->name == c2->name)
+ {
+ return c1->unit_address < c2->unit_address;
+ }
+ return c1->name < c2->name;
+ }
+ */
+ public:
+ /**
+ * Sorts the node's properties and children into alphabetical order and
+ * recursively sorts the children.
+ */
+ void sort();
+ /**
+ * Iterator type for child nodes.
+ */
+ typedef std::vector<node*>::iterator child_iterator;
+ /**
+ * Returns an iterator for the first child of this node.
+ */
+ inline child_iterator child_begin()
+ {
+ return children.begin();
+ }
+ /**
+ * Returns an iterator after the last child of this node.
+ */
+ inline child_iterator child_end()
+ {
+ return children.end();
+ }
+ /**
+ * Iterator type for properties of a node.
+ */
+ typedef std::vector<property*>::iterator property_iterator;
+ /**
+ * Returns an iterator after the last property of this node.
+ */
+ inline property_iterator property_begin()
+ {
+ return properties.begin();
+ }
+ /**
+ * Returns an iterator for the first property of this node.
+ */
+ inline property_iterator property_end()
+ {
+ return properties.end();
+ }
+ /**
+ * Factory method for constructing a new node. Attempts to parse a
+ * node in DTS format from the input, and returns it on success. On
+ * any parse error, this will return 0. This should be called with the
+ * cursor on the open brace of the property, after the name and so on
+ * have been parsed.
+ */
+ static node* parse(input_buffer &input,
+ string name,
+ string label=string(),
+ string address=string());
+ /**
+ * Factory method for constructing a new node. Attempts to parse a
+ * node in DTB format from the input, and returns it on success. On
+ * any parse error, this will return 0. This should be called with the
+ * cursor on the open brace of the property, after the name and so on
+ * have been parsed.
+ */
+ static node* parse_dtb(input_buffer &structs, input_buffer &strings);
+ /**
+ * Destroys the node, recursively deleting all of its properties and
+ * children.
+ */
+ ~node();
+ /**
+ * Returns a property corresponding to the specified key, or 0 if this
+ * node does not contain a property of that name.
+ */
+ property *get_property(string key);
+ /**
+ * Adds a new property to this node.
+ */
+ inline void add_property(property *p)
+ {
+ properties.push_back(p);
+ }
+ /**
+ * Merges a node into this one. Any properties present in both are
+ * overridden, any properties present in only one are preserved.
+ */
+ void merge_node(node *other);
+ /**
+ * Write this node to the specified output. Although nodes do not
+ * refer to a string table directly, their properties do. The string
+ * table passed as the second argument is used for the names of
+ * properties within this node and its children.
+ */
+ void write(dtb::output_writer &writer, dtb::string_table &strings);
+ /**
+ * Writes the current node as DTS to the specified file. The second
+ * parameter is the indent level. This function will start every line
+ * with this number of tabs.
+ */
+ void write_dts(FILE *file, int indent);
+};
+
+/**
+ * Class encapsulating the entire parsed FDT. This is the top-level class,
+ * which parses the entire DTS representation and write out the finished
+ * version.
+ */
+class device_tree
+{
+ public:
+ /**
+ * Type used for node paths. A node path is sequence of names and unit
+ * addresses.
+ */
+ typedef std::vector<std::pair<string,string> > node_path;
+ /**
+ * Name that we should use for phandle nodes.
+ */
+ enum phandle_format
+ {
+ /** linux,phandle */
+ LINUX,
+ /** phandle */
+ EPAPR,
+ /** Create both nodes. */
+ BOTH
+ };
+ private:
+ /**
+ * The format that we should use for writing phandles.
+ */
+ phandle_format phandle_node_name;
+ /**
+ * Flag indicating that this tree is valid. This will be set to false
+ * on parse errors.
+ */
+ bool valid;
+ /**
+ * Type used for memory reservations. A reservation is two 64-bit
+ * values indicating a base address and length in memory that the
+ * kernel should not use. The high 32 bits are ignored on 32-bit
+ * platforms.
+ */
+ typedef std::pair<uint64_t, uint64_t> reservation;
+ /**
+ * The memory reserves table.
+ */
+ std::vector<reservation> reservations;
+ /**
+ * Root node. All other nodes are children of this node.
+ */
+ node *root;
+ /**
+ * Mapping from names to nodes. Only unambiguous names are recorded,
+ * duplicate names are stored as (node*)-1.
+ */
+ std::map<string, node*> node_names;
+ /**
+ * A map from labels to node paths. When resolving cross references,
+ * we look up referenced nodes in this and replace the cross reference
+ * with the full path to its target.
+ */
+ std::map<string, node_path> node_paths;
+ /**
+ * A collection of property values that are references to other nodes.
+ * These should be expanded to the full path of their targets.
+ */
+ std::vector<property_value*> cross_references;
+ /**
+ * A collection of property values that refer to phandles. These will
+ * be replaced by the value of the phandle property in their
+ * destination.
+ */
+ std::vector<property_value*> phandles;
+ /**
+ * A collection of input buffers that we are using. These input
+ * buffers are the ones that own their memory, and so we must preserve
+ * them for the lifetime of the device tree.
+ */
+ std::vector<input_buffer*> buffers;
+ /**
+ * A map of used phandle values to nodes. All phandles must be unique,
+ * so we keep a set of ones that the user explicitly provides in the
+ * input to ensure that we don't reuse them.
+ *
+ * This is a map, rather than a set, because we also want to be able to
+ * find phandles that were provided by the user explicitly when we are
+ * doing checking.
+ */
+ std::map<uint32_t, node*> used_phandles;
+ /**
+ * Paths to search for include files. This contains a set of
+ * nul-terminated strings, which are not owned by this class and so
+ * must be freed separately.
+ */
+ std::vector<const char*> include_paths;
+ /**
+ * The default boot CPU, specified in the device tree header.
+ */
+ uint32_t boot_cpu;
+ /**
+ * The number of empty reserve map entries to generate in the blob.
+ */
+ uint32_t spare_reserve_map_entries;
+ /**
+ * The minimum size in bytes of the blob.
+ */
+ uint32_t minimum_blob_size;
+ /**
+ * The number of bytes of padding to add to the end of the blob.
+ */
+ uint32_t blob_padding;
+ /**
+ * Visit all of the nodes recursively, and if they have labels then add
+ * them to the node_paths and node_names vectors so that they can be
+ * used in resolving cross references. Also collects phandle
+ * properties that have been explicitly added.
+ */
+ void collect_names_recursive(node* n, node_path &path);
+ /**
+ * Calls the recursive version of this method on every root node.
+ */
+ void collect_names();
+ /**
+ * Resolves all cross references. Any properties that refer to another
+ * node must have their values replaced by either the node path or
+ * phandle value.
+ */
+ void resolve_cross_references();
+ /**
+ * Parses root nodes from the top level of a dts file.
+ */
+ void parse_roots(input_buffer &input, std::vector<node*> &roots);
+ /**
+ * Allocates a new mmap()'d input buffer for use in parsing. This
+ * object then keeps a reference to it, ensuring that it is not
+ * deallocated until the device tree is destroyed.
+ */
+ input_buffer *buffer_for_file(const char *path);
+ /**
+ * Template function that writes a dtb blob using the specified writer.
+ * The writer defines the output format (assembly, blob).
+ */
+ template<class writer>
+ void write(int fd);
+ public:
+ /**
+ * Returns the node referenced by the property. If this is a tree that
+ * is in source form, then we have a string that we can use to index
+ * the cross_references array and so we can just look that up.
+ */
+ node *referenced_node(property_value &v);
+ /**
+ * Writes this FDT as a DTB to the specified output.
+ */
+ void write_binary(int fd);
+ /**
+ * Writes this FDT as an assembly representation of the DTB to the
+ * specified output. The result can then be assembled and linked into
+ * a program.
+ */
+ void write_asm(int fd);
+ /**
+ * Writes the tree in DTS (source) format.
+ */
+ void write_dts(int fd);
+ /**
+ * Default constructor. Creates a valid, but empty FDT.
+ */
+ device_tree() : phandle_node_name(EPAPR), valid(true), root(0),
+ boot_cpu(0), spare_reserve_map_entries(0),
+ minimum_blob_size(0), blob_padding(0) {}
+ /**
+ * Constructs a device tree from the specified file name, referring to
+ * a file that contains a device tree blob.
+ */
+ void parse_dtb(const char *fn, FILE *depfile);
+ /**
+ * Constructs a device tree from the specified file name, referring to
+ * a file that contains device tree source.
+ */
+ void parse_dts(const char *fn, FILE *depfile);
+ /**
+ * Destroy the tree and any input buffers that it holds.
+ */
+ ~device_tree();
+ /**
+ * Returns whether this tree is valid.
+ */
+ inline bool is_valid()
+ {
+ return valid;
+ }
+ /**
+ * Sets the format for writing phandle properties.
+ */
+ inline void set_phandle_format(phandle_format f)
+ {
+ phandle_node_name = f;
+ }
+ /**
+ * Returns a pointer to the root node of this tree. No ownership
+ * transfer.
+ */
+ inline node *get_root() const
+ {
+ return root;
+ }
+ /**
+ * Sets the physical boot CPU.
+ */
+ void set_boot_cpu(uint32_t cpu)
+ {
+ boot_cpu = cpu;
+ }
+ /**
+ * Sorts the tree. Useful for debugging device trees.
+ */
+ void sort()
+ {
+ root->sort();
+ }
+ /**
+ * Adds a path to search for include files. The argument must be a
+ * nul-terminated string representing the path. The device tree keeps
+ * a pointer to this string, but does not own it: the caller is
+ * responsible for freeing it if required.
+ */
+ void add_include_path(const char *path)
+ {
+ include_paths.push_back(path);
+ }
+ /**
+ * Sets the number of empty reserve map entries to add.
+ */
+ void set_empty_reserve_map_entries(uint32_t e)
+ {
+ spare_reserve_map_entries = e;
+ }
+ /**
+ * Sets the minimum size, in bytes, of the blob.
+ */
+ void set_blob_minimum_size(uint32_t s)
+ {
+ minimum_blob_size = s;
+ }
+ /**
+ * Sets the amount of padding to add to the blob.
+ */
+ void set_blob_padding(uint32_t p)
+ {
+ blob_padding = p;
+ }
+};
+
+} // namespace fdt
+
+} // namespace dtc
+
+#endif // !_FDT_HH_
diff --git a/usr.bin/dtc/input_buffer.cc b/usr.bin/dtc/input_buffer.cc
new file mode 100644
index 0000000..2a483f3
--- /dev/null
+++ b/usr.bin/dtc/input_buffer.cc
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "input_buffer.hh"
+#include <ctype.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <assert.h>
+
+namespace dtc
+{
+
+void
+input_buffer::skip_spaces()
+{
+ if (cursor >= size) { return; }
+ if (cursor < 0) { return; }
+ char c = buffer[cursor];
+ while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f')
+ || (c == '\v') || (c == '\r'))
+ {
+ cursor++;
+ if (cursor > size)
+ {
+ c = '\0';
+ }
+ else
+ {
+ c = buffer[cursor];
+ }
+ }
+}
+
+input_buffer
+input_buffer::buffer_from_offset(int offset, int s)
+{
+ if (s == 0)
+ {
+ s = size - offset;
+ }
+ if (offset > size)
+ {
+ return input_buffer();
+ }
+ if (s > (size-offset))
+ {
+ return input_buffer();
+ }
+ return input_buffer(&buffer[offset], s);
+}
+
+bool
+input_buffer::consume(const char *str)
+{
+ int len = strlen(str);
+ if (len > size - cursor)
+ {
+ return false;
+ }
+ else
+ {
+ for (int i=0 ; i<len ; ++i)
+ {
+ if (str[i] != buffer[cursor + i])
+ {
+ return false;
+ }
+ }
+ cursor += len;
+ return true;
+ }
+ return false;
+}
+
+bool
+input_buffer::consume_integer(long long &outInt)
+{
+ // The first character must be a digit. Hex and octal strings
+ // are prefixed by 0 and 0x, respectively.
+ if (!isdigit((*this)[0]))
+ {
+ return false;
+ }
+ char *end=0;
+ outInt = strtoll(&buffer[cursor], &end, 0);
+ if (end == &buffer[cursor])
+ {
+ return false;
+ }
+ cursor = end - buffer;
+ return true;
+}
+
+bool
+input_buffer::consume_hex_byte(uint8_t &outByte)
+{
+ if (!ishexdigit((*this)[0]) && !ishexdigit((*this)[1]))
+ {
+ return false;
+ }
+ outByte = (digittoint((*this)[0]) << 4) | digittoint((*this)[1]);
+ cursor += 2;
+ return true;
+}
+
+input_buffer&
+input_buffer::next_token()
+{
+ int start;
+ do {
+ start = cursor;
+ skip_spaces();
+ // Parse /* comments
+ if (((*this)[0] == '/') && ((*this)[1] == '*'))
+ {
+ // eat the start of the comment
+ ++(*this);
+ ++(*this);
+ do {
+ // Find the ending * of */
+ while ((**this != '\0') && (**this != '*'))
+ {
+ ++(*this);
+ }
+ // Eat the *
+ ++(*this);
+ } while ((**this != '\0') && (**this != '/'));
+ // Eat the /
+ ++(*this);
+ }
+ // Parse // comments
+ if (((*this)[0] == '/') && ((*this)[1] == '/'))
+ {
+ // eat the start of the comment
+ ++(*this);
+ ++(*this);
+ // Find the ending * of */
+ while (**this != '\n')
+ {
+ ++(*this);
+ }
+ // Eat the \n
+ ++(*this);
+ }
+ } while (start != cursor);
+ return *this;
+}
+
+void
+input_buffer::parse_error(const char *msg)
+{
+ int line_count = 1;
+ int line_start = 0;
+ int line_end = cursor;
+ for (int i=cursor ; i>0 ; --i)
+ {
+ if (buffer[i] == '\n')
+ {
+ line_count++;
+ if (line_start == 0)
+ {
+ line_start = i+1;
+ }
+ }
+ }
+ for (int i=cursor+1 ; i<size ; ++i)
+ {
+ if (buffer[i] == '\n')
+ {
+ line_end = i;
+ break;
+ }
+ }
+ fprintf(stderr, "Error on line %d: %s\n", line_count, msg);
+ fwrite(&buffer[line_start], line_end-line_start, 1, stderr);
+ putc('\n', stderr);
+ for (int i=0 ; i<(cursor-line_start) ; ++i)
+ {
+ putc(' ', stderr);
+ }
+ putc('^', stderr);
+ putc('\n', stderr);
+}
+void
+input_buffer::dump()
+{
+ fprintf(stderr, "Current cursor: %d\n", cursor);
+ fwrite(&buffer[cursor], size-cursor, 1, stderr);
+}
+
+mmap_input_buffer::mmap_input_buffer(int fd) : input_buffer(0, 0)
+{
+ struct stat sb;
+ if (fstat(fd, &sb))
+ {
+ perror("Failed to stat file");
+ }
+ size = sb.st_size;
+ buffer = (const char*)mmap(0, size, PROT_READ,
+ MAP_PREFAULT_READ, fd, 0);
+ if (buffer == 0)
+ {
+ perror("Failed to mmap file");
+ }
+}
+
+mmap_input_buffer::~mmap_input_buffer()
+{
+ if (buffer != 0)
+ {
+ munmap((void*)buffer, size);
+ }
+}
+
+stream_input_buffer::stream_input_buffer() : input_buffer(0, 0)
+{
+ int c;
+ while ((c = fgetc(stdin)) != EOF)
+ {
+ b.push_back(c);
+ }
+ buffer = b.data();
+ size = b.size();
+}
+
+} // namespace dtc
+
diff --git a/usr.bin/dtc/input_buffer.hh b/usr.bin/dtc/input_buffer.hh
new file mode 100644
index 0000000..9a38312
--- /dev/null
+++ b/usr.bin/dtc/input_buffer.hh
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _INPUT_BUFFER_HH_
+#define _INPUT_BUFFER_HH_
+#include "util.hh"
+#include <assert.h>
+
+namespace dtc
+{
+
+/**
+ * Class encapsulating the input file. Can be used as a const char*, but has
+ * range checking. Attempting to access anything out of range will return a 0
+ * byte. The input buffer can be cheaply copied, without copying the
+ * underlying memory, however it is the user's responsibility to ensure that
+ * such copies do not persist beyond the lifetime of the underlying memory.
+ *
+ * This also contains methods for reporting errors and for consuming the token
+ * stream.
+ */
+class input_buffer
+{
+ protected:
+ /**
+ * The buffer. This class doesn't own the buffer, but the
+ * mmap_input_buffer subclass does.
+ */
+ const char* buffer;
+ /**
+ * The size of the buffer.
+ */
+ int size;
+ private:
+ /**
+ * The current place in the buffer where we are reading. This class
+ * keeps a separate size, pointer, and cursor so that we can move
+ * forwards and backwards and still have checks that we haven't fallen
+ * off either end.
+ */
+ int cursor;
+ /**
+ * Private constructor. This is used to create input buffers that
+ * refer to the same memory, but have different cursors.
+ */
+ input_buffer(const char* b, int s, int c) : buffer(b), size(s),
+ cursor(c) {}
+ /**
+ * Reads forward past any spaces. The DTS format is not whitespace
+ * sensitive and so we want to scan past whitespace when reading it.
+ */
+ void skip_spaces();
+ public:
+ /**
+ * Virtual destructor. Does nothing, but exists so that subclasses
+ * that own the memory can run cleanup code for deallocating it.
+ */
+ virtual ~input_buffer() {};
+ /**
+ * Constructs an empty buffer.
+ */
+ input_buffer() : buffer(0), size(0), cursor(0) {}
+ /**
+ * Constructs a new buffer with a specified memory region and size.
+ */
+ input_buffer(const char* b, int s) : buffer(b), size(s), cursor(0){}
+ /**
+ * Returns a new input buffer referring into this input, clamped to the
+ * specified size. If the requested buffer would fall outside the
+ * range of this one, then it returns an empty buffer.
+ *
+ * The returned buffer shares the same underlying storage as the
+ * original. This is intended to be used for splitting up the various
+ * sections of a device tree blob. Requesting a size of 0 will give a
+ * buffer that extends to the end of the available memory.
+ */
+ input_buffer buffer_from_offset(int offset, int s=0);
+ /**
+ * Returns true if this buffer has no unconsumed space in it.
+ */
+ inline bool empty()
+ {
+ return cursor >= size;
+ }
+ /**
+ * Dereferencing operator, allows the buffer to be treated as a char*
+ * and dereferenced to give a character. This returns a null byte if
+ * the cursor is out of range.
+ */
+ inline char operator*()
+ {
+ if (cursor >= size) { return '\0'; }
+ if (cursor < 0) { return '\0'; }
+ return buffer[cursor];
+ }
+ /**
+ * Array subscripting operator, returns a character at the specified
+ * index offset from the current cursor. The offset may be negative,
+ * to reread characters that have already been read. If the current
+ * cursor plus offset is outside of the range, this returns a nul
+ * byte.
+ */
+ inline char operator[](int offset)
+ {
+ if (cursor + offset >= size) { return '\0'; }
+ if (cursor + offset < 0) { return '\0'; }
+ return buffer[cursor + offset];
+ }
+ /**
+ * Increments the cursor, iterating forward in the buffer.
+ */
+ inline input_buffer &operator++()
+ {
+ cursor++;
+ return *this;
+ }
+ /**
+ * Cast to char* operator. Returns a pointer into the buffer that can
+ * be used for constructing strings.
+ */
+ inline operator const char*()
+ {
+ if (cursor >= size) { return 0; }
+ if (cursor < 0) { return 0; }
+ return &buffer[cursor];
+ }
+ /**
+ * Consumes a character. Moves the cursor one character forward if the
+ * next character matches the argument, returning true. If the current
+ * character does not match the argument, returns false.
+ */
+ inline bool consume(char c)
+ {
+ if ((*this)[0] == c)
+ {
+ ++(*this);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Consumes a string. If the (null-terminated) string passed as the
+ * argument appears in the input, advances the cursor to the end and
+ * returns true. Returns false if the string does not appear at the
+ * current point in the input.
+ */
+ bool consume(const char *str);
+ /**
+ * Reads an integer in base 8, 10, or 16. Returns true and advances
+ * the cursor to the end of the integer if the cursor points to an
+ * integer, returns false and does not move the cursor otherwise.
+ *
+ * The parsed value is returned via the argument.
+ */
+ bool consume_integer(long long &outInt);
+ /**
+ * Template function that consumes a binary value in big-endian format
+ * from the input stream. Returns true and advances the cursor if
+ * there is a value of the correct size. This function assumes that
+ * all values must be natively aligned, and so advances the cursor to
+ * the correct alignment before reading.
+ */
+ template<typename T>
+ bool consume_binary(T &out)
+ {
+ int align = 0;
+ int type_size = sizeof(T);
+ if (cursor % type_size != 0)
+ {
+ align = type_size - (cursor % type_size);
+ }
+ if (size < cursor + align + type_size)
+ {
+ return false;
+ }
+ cursor += align;
+ assert(cursor % type_size == 0);
+ out = 0;
+ for (int i=0 ; i<type_size ; ++i)
+ {
+ out <<= 8;
+ out |= (((T)buffer[cursor++]) & 0xff);
+ }
+ return true;
+ }
+ /**
+ * Consumes two hex digits and return the resulting byte via the first
+ * argument. If the next two characters are hex digits, returns true
+ * and advances the cursor. If not, then returns false and leaves the
+ * cursor in place.
+ */
+ bool consume_hex_byte(uint8_t &outByte);
+ /**
+ * Advances the cursor to the start of the next token, skipping
+ * comments and whitespace. If the cursor already points to the start
+ * of a token, then this function does nothing.
+ */
+ input_buffer &next_token();
+ /**
+ * Prints a message indicating the location of a parse error.
+ */
+ void parse_error(const char *msg);
+ /**
+ * Dumps the current cursor value and the unconsumed values in the
+ * input buffer to the standard error. This method is intended solely
+ * for debugging.
+ */
+ void dump();
+};
+/**
+ * Explicit specialisation for reading a single byte.
+ */
+template<>
+inline bool input_buffer::consume_binary(uint8_t &out)
+{
+ if (size < cursor + 1)
+ {
+ return false;
+ }
+ out = buffer[cursor++];
+ return true;
+}
+
+/**
+ * Subclass of input_buffer that mmap()s a file and owns the resulting memory.
+ * When this object is destroyed, the memory is unmapped.
+ */
+struct mmap_input_buffer : public input_buffer
+{
+ /**
+ * Constructs a new buffer from the file passed in as a file
+ * descriptor.
+ */
+ mmap_input_buffer(int fd);
+ /**
+ * Unmaps the buffer, if one exists.
+ */
+ virtual ~mmap_input_buffer();
+};
+/**
+ * Input buffer read from standard input. This is used for reading device tree
+ * blobs and source from standard input. It reads the entire input into
+ * malloc'd memory, so will be very slow for large inputs. DTS and DTB files
+ * are very rarely more than 10KB though, so this is probably not a problem.
+ */
+struct stream_input_buffer : public input_buffer
+{
+ /**
+ * The buffer that will store the data read from the standard input.
+ */
+ std::vector<char> b;
+ /**
+ * Constructs a new buffer from the standard input.
+ */
+ stream_input_buffer();
+};
+
+} // namespace dtc
+
+#endif // !_INPUT_BUFFER_HH_
diff --git a/usr.bin/dtc/string.cc b/usr.bin/dtc/string.cc
new file mode 100644
index 0000000..283bafa
--- /dev/null
+++ b/usr.bin/dtc/string.cc
@@ -0,0 +1,258 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "string.hh"
+#include <ctype.h>
+#include <stdio.h>
+
+namespace
+{
+/**
+ * The source files are ASCII, so we provide a non-locale-aware version of
+ * isalpha. This is a class so that it can be used with a template function
+ * for parsing strings.
+ */
+struct is_alpha
+{
+ static inline bool check(const char c)
+ {
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') &&
+ (c <= 'Z'));
+ }
+};
+/**
+ * Check whether a character is in the set allowed for node names. This is a
+ * class so that it can be used with a template function for parsing strings.
+ */
+struct is_node_name_character
+{
+ static inline bool check(const char c)
+ {
+ switch(c)
+ {
+ default:
+ return false;
+ case 'a'...'z': case 'A'...'Z': case '0'...'9':
+ case ',': case '.': case '+': case '-':
+ case '_':
+ return true;
+ }
+ }
+};
+/**
+ * Check whether a character is in the set allowed for property names. This is
+ * a class so that it can be used with a template function for parsing strings.
+ */
+struct is_property_name_character
+{
+ static inline bool check(const char c)
+ {
+ switch(c)
+ {
+ default:
+ return false;
+ case 'a'...'z': case 'A'...'Z': case '0'...'9':
+ case ',': case '.': case '+': case '-':
+ case '_': case '#':
+ return true;
+ }
+ }
+};
+
+}
+
+namespace dtc
+{
+
+template<class T> string
+string::parse(input_buffer &s)
+{
+ const char *start = s;
+ int l=0;
+ while (T::check(*s)) { l++; ++s; }
+ return string(start, l);
+}
+
+string::string(input_buffer &s) : start((const char*)s), length(0)
+{
+ while(s[length] != '\0')
+ {
+ length++;
+ }
+}
+
+string
+string::parse_node_name(input_buffer &s)
+{
+ return parse<is_node_name_character>(s);
+}
+
+string
+string::parse_property_name(input_buffer &s)
+{
+ return parse<is_property_name_character>(s);
+}
+string
+string::parse_node_or_property_name(input_buffer &s, bool &is_property)
+{
+ if (is_property)
+ {
+ return parse_property_name(s);
+ }
+ const char *start = s;
+ int l=0;
+ while (is_node_name_character::check(*s))
+ {
+ l++;
+ ++s;
+ }
+ while (is_property_name_character::check(*s))
+ {
+ l++;
+ ++s;
+ is_property = true;
+ }
+ return string(start, l);
+}
+
+bool
+string::operator==(const string& other) const
+{
+ return (length == other.length) &&
+ (memcmp(start, other.start, length) == 0);
+}
+
+bool
+string::operator==(const char *other) const
+{
+ return strncmp(other, start, length) == 0;
+}
+
+bool
+string::operator<(const string& other) const
+{
+ if (length < other.length) { return true; }
+ if (length > other.length) { return false; }
+ return memcmp(start, other.start, length) < 0;
+}
+
+void
+string::push_to_buffer(byte_buffer &buffer, bool escapes)
+{
+ for (int i=0 ; i<length ; ++i)
+ {
+ uint8_t c = start[i];
+ if (escapes && c == '\\' && i+1 < length)
+ {
+ c = start[++i];
+ switch (c)
+ {
+ // For now, we just ignore invalid escape sequences.
+ default:
+ case '"':
+ case '\'':
+ case '\\':
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '0'...'7':
+ {
+ int v = digittoint(c);
+ if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
+ {
+ v <<= 3;
+ v |= digittoint(start[i+1]);
+ i++;
+ if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
+ {
+ v <<= 3;
+ v |= digittoint(start[i+1]);
+ }
+ }
+ c = (uint8_t)v;
+ break;
+ }
+ case 'x':
+ {
+ ++i;
+ if (i >= length)
+ {
+ break;
+ }
+ int v = digittoint(start[i]);
+ if (i+1 < length && ishexdigit(start[i+1]))
+ {
+ v <<= 4;
+ v |= digittoint(start[++i]);
+ }
+ c = (uint8_t)v;
+ break;
+ }
+ }
+ }
+ buffer.push_back(c);
+ }
+}
+
+void
+string::print(FILE *file)
+{
+ fwrite(start, length, 1, file);
+}
+
+void
+string::dump()
+{
+ print(stderr);
+}
+
+} // namespace dtc
+
diff --git a/usr.bin/dtc/string.hh b/usr.bin/dtc/string.hh
new file mode 100644
index 0000000..45bc4fd
--- /dev/null
+++ b/usr.bin/dtc/string.hh
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _STRING_HH_
+#define _STRING_HH_
+#include "input_buffer.hh"
+
+namespace dtc
+{
+
+/**
+ * String, referring to a place in the input file. We don't bother copying
+ * strings until we write them to the final output. These strings should be
+ * two words long: a start and a length. They are intended to be cheap to copy
+ * and store in collections. Copying the string object does not copy the
+ * underlying storage.
+ *
+ * Strings are not nul-terminated.
+ */
+class string
+{
+ /** Start address. Contained within the mmap()'d input file and not
+ * owned by this object. */
+ const char *start;
+ /** length of the string. DTS strings are allowed to contain nuls */
+ int length;
+ /** Generic function for parsing strings matching the character set
+ * defined by the template argument. */
+ template<class T>
+ static string parse(input_buffer &s);
+ public:
+ /**
+ * Constructs a string referring into another buffer.
+ */
+ string(const char *s, int l) : start(s), length(l) {}
+ /** Constructs a string from a C string. */
+ string(const char *s) : start(s), length(strlen(s)) {}
+ /** Default constructor, returns an empty string. */
+ string() : start(0), length(0) {}
+ /** Construct a from an input buffer, ending with a nul terminator. */
+ string(input_buffer &s);
+ /**
+ * Returns the longest string in the input buffer starting at the
+ * current cursor and composed entirely of characters that are valid in
+ * node names.
+ */
+ static string parse_node_name(input_buffer &s);
+ /**
+ * Returns the longest string in the input buffer starting at the
+ * current cursor and composed entirely of characters that are valid in
+ * property names.
+ */
+ static string parse_property_name(input_buffer &s);
+ /**
+ * Parses either a node or a property name. If is_property is true on
+ * entry, then only property names are parsed. If it is false, then it
+ * will be set, on return, to indicate whether the parsed name is only
+ * valid as a property.
+ */
+ static string parse_node_or_property_name(input_buffer &s,
+ bool &is_property);
+ /**
+ * Compares two strings for equality. Strings are equal if they refer
+ * to identical byte sequences.
+ */
+ bool operator==(const string& other) const;
+ /**
+ * Compares a string against a C string. The trailing nul in the C
+ * string is ignored for the purpose of comparison, so this will always
+ * fail if the string contains nul bytes.
+ */
+ bool operator==(const char *other) const;
+ /**
+ * Inequality operator, defined as the inverse of the equality
+ * operator.
+ */
+ template <typename T>
+ inline bool operator!=(T other)
+ {
+ return !(*this == other);
+ }
+ /**
+ * Comparison operator, defined to allow strings to be used as keys in
+ * maps.
+ */
+ bool operator<(const string& other) const;
+ /**
+ * Returns true if this is the empty string, false otherwise.
+ */
+ inline bool empty() const
+ {
+ return length == 0;
+ }
+ /**
+ * Returns the size of the string, in bytes.
+ */
+ inline size_t size()
+ {
+ return length;
+ }
+ /**
+ * Writes the string to the specified buffer.
+ */
+ void push_to_buffer(byte_buffer &buffer, bool escapes=false);
+ /**
+ * Prints the string to the specified output stream.
+ */
+ void print(FILE *file);
+ /**
+ * Dumps the string to the standard error stream. Intended to be used
+ * for debugging.
+ */
+ void dump();
+};
+
+} // namespace dtc
+
+#endif // !_STRING_HH_
diff --git a/usr.bin/dtc/util.hh b/usr.bin/dtc/util.hh
new file mode 100644
index 0000000..7f2ec67
--- /dev/null
+++ b/usr.bin/dtc/util.hh
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2013 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _UTIL_HH_
+#define _UTIL_HH_
+
+#include <vector>
+
+// If we aren't using C++11, then just ignore static asserts.
+#if __cplusplus < 201103L
+#ifndef static_assert
+#define static_assert(x, y) ((void)0)
+#endif
+#endif
+
+namespace dtc {
+
+/**
+ * Type for a buffer of bytes. This is used for a lot of short-lived temporary
+ * variables, so may eventually be changed to something like LLVM's
+ * SmallVector, but currently the program runs in a tiny fraction of a second,
+ * so this is not an issue.
+ */
+typedef std::vector<uint8_t> byte_buffer;
+
+/**
+ * Helper function to push a big endian value into a byte buffer. We use
+ * native-endian values for all of the in-memory data structures and only
+ * transform them into big endian form for output.
+ */
+template<typename T>
+inline void push_big_endian(byte_buffer &v, T val)
+{
+ static_assert(sizeof(T) > 1,
+ "Big endian doesn't make sense for single-byte values");
+ for (int bit=(sizeof(T) - 1)*8 ; bit>=0 ; bit-= 8)
+ {
+ v.push_back((val >> bit) & 0xff);
+ }
+}
+
+/**
+ * Simple inline non-locale-aware check that this is a valid ASCII
+ * digit.
+ */
+inline bool isdigit(char c)
+{
+ return (c >= '0') && (c <= '9');
+}
+
+/**
+ * Simple inline non-locale-aware check that this is a valid ASCII
+ * hex digit.
+ */
+inline bool ishexdigit(char c)
+{
+ return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) ||
+ ((c >= 'A') && (c <= 'Z'));
+}
+
+}// namespace dtc
+
+#endif // !_UTIL_HH_
diff --git a/usr.bin/ee/Makefile b/usr.bin/ee/Makefile
index ffa47b9..2236777 100644
--- a/usr.bin/ee/Makefile
+++ b/usr.bin/ee/Makefile
@@ -13,7 +13,7 @@ LDADD= -lncurses
WARNS?= 2
-NLS= en_US.US-ASCII fr_FR.ISO8859-1 de_DE.ISO8859-1 pl_PL.ISO8859-2 \
+NLS= C fr_FR.ISO8859-1 de_DE.ISO8859-1 pl_PL.ISO8859-2 \
uk_UA.KOI8-U pt_BR.ISO8859-1 ru_RU.KOI8-R hu_HU.ISO8859-2
NLSLINKS_en_US.US-ASCII= en_US.ISO8859-1 en_US.ISO8859-15
diff --git a/usr.bin/grep/grep.c b/usr.bin/grep/grep.c
index 70fd9bd..117a5a9 100644
--- a/usr.bin/grep/grep.c
+++ b/usr.bin/grep/grep.c
@@ -481,7 +481,7 @@ main(int argc, char *argv[])
case 'e':
{
char *token;
- char *string = strdup(optarg);
+ char *string = optarg;
while ((token = strsep(&string, "\n")) != NULL)
add_pattern(token, strlen(token));
@@ -675,7 +675,7 @@ main(int argc, char *argv[])
/* Process patterns from command line */
if (aargc != 0 && needpattern) {
char *token;
- char *string = strdup(*aargv);
+ char *string = *aargv;
while ((token = strsep(&string, "\n")) != NULL)
add_pattern(token, strlen(token));
diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1
index d8486bd..4763cc6 100644
--- a/usr.bin/man/man.1
+++ b/usr.bin/man/man.1
@@ -84,7 +84,7 @@ environment variable.
.It Fl S Ar mansect
Restricts manual sections searched to the specified colon delimited list.
Defaults to
-.Dq Li 1:1aout:8:2:3:n:4:5:6:7:9:l .
+.Dq Li 1:8:2:3:n:4:5:6:7:9:l .
Overrides the
.Ev MANSECT
environment variable.
diff --git a/usr.bin/man/man.sh b/usr.bin/man/man.sh
index 0725e0b..0f6249a 100755
--- a/usr.bin/man/man.sh
+++ b/usr.bin/man/man.sh
@@ -945,7 +945,7 @@ STTY=/bin/stty
SYSCTL=/sbin/sysctl
debug=0
-man_default_sections='1:1aout:8:2:3:n:4:5:6:7:9:l'
+man_default_sections='1:8:2:3:n:4:5:6:7:9:l'
man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/man'
cattool='/usr/bin/zcat -f'
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
index 9d8c97e..8094e5b 100644
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -699,7 +699,7 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
p(tcps_sc_sendcookie, "\t%lu cookie%s sent\n");
p(tcps_sc_recvcookie, "\t%lu cookie%s received\n");
- p(tcps_hc_added, "\t%lu hostcache entrie%s added\n");
+ p3(tcps_hc_added, "\t%lu hostcache entr%s added\n");
p1a(tcps_hc_bucketoverflow, "\t\t%lu bucket overflow\n");
p(tcps_sack_recovery_episode, "\t%lu SACK recovery episode%s\n");
diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile
new file mode 100644
index 0000000..382a05f
--- /dev/null
+++ b/usr.bin/patch/Makefile
@@ -0,0 +1,18 @@
+# $OpenBSD: Makefile,v 1.4 2005/05/16 15:22:46 espie Exp $
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.if ${MK_BSD_PATCH} == "yes"
+PROG= patch
+.else
+PROG= bsdpatch
+CLEANFILES+= bsdpatch.1
+
+bsdpatch.1: patch.1
+ cp ${.ALLSRC} ${.TARGET}
+.endif
+
+SRCS= backupfile.c inp.c mkpath.c patch.c pch.c util.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/patch/backupfile.c b/usr.bin/patch/backupfile.c
new file mode 100644
index 0000000..d22d607
--- /dev/null
+++ b/usr.bin/patch/backupfile.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (C) 1990 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * without restriction.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * backupfile.c -- make Emacs style backup file names
+ *
+ * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
+ *
+ * $OpenBSD: backupfile.c,v 1.20 2009/10/27 23:59:41 deraadt Exp $
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "backupfile.h"
+
+
+#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
+
+/* Which type of backup file names are generated. */
+enum backup_type backup_type = none;
+
+/*
+ * The extension added to file names to produce a simple (as opposed to
+ * numbered) backup file name.
+ */
+const char *simple_backup_suffix = "~";
+
+static char *concat(const char *, const char *);
+static char *make_version_name(const char *, int);
+static int max_backup_version(const char *, const char *);
+static int version_number(const char *, const char *, size_t);
+static int argmatch(const char *, const char **);
+static void invalid_arg(const char *, const char *, int);
+
+/*
+ * Return the name of the new backup file for file FILE, allocated with
+ * malloc. Return 0 if out of memory. FILE must not end with a '/' unless it
+ * is the root directory. Do not call this function if backup_type == none.
+ */
+char *
+find_backup_file_name(const char *file)
+{
+ char *dir, *base_versions, *tmp_file;
+ int highest_backup;
+
+ if (backup_type == simple)
+ return concat(file, simple_backup_suffix);
+ tmp_file = strdup(file);
+ if (tmp_file == NULL)
+ return NULL;
+ base_versions = concat(basename(tmp_file), ".~");
+ free(tmp_file);
+ if (base_versions == NULL)
+ return NULL;
+ tmp_file = strdup(file);
+ if (tmp_file == NULL) {
+ free(base_versions);
+ return NULL;
+ }
+ dir = dirname(tmp_file);
+ if (dir == NULL) {
+ free(base_versions);
+ free(tmp_file);
+ return NULL;
+ }
+ highest_backup = max_backup_version(base_versions, dir);
+ free(base_versions);
+ free(tmp_file);
+ if (backup_type == numbered_existing && highest_backup == 0)
+ return concat(file, simple_backup_suffix);
+ return make_version_name(file, highest_backup + 1);
+}
+
+/*
+ * Return the number of the highest-numbered backup file for file FILE in
+ * directory DIR. If there are no numbered backups of FILE in DIR, or an
+ * error occurs reading DIR, return 0. FILE should already have ".~" appended
+ * to it.
+ */
+static int
+max_backup_version(const char *file, const char *dir)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ int highest_version, this_version;
+ size_t file_name_length;
+
+ dirp = opendir(dir);
+ if (dirp == NULL)
+ return 0;
+
+ highest_version = 0;
+ file_name_length = strlen(file);
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_namlen <= file_name_length)
+ continue;
+
+ this_version = version_number(file, dp->d_name, file_name_length);
+ if (this_version > highest_version)
+ highest_version = this_version;
+ }
+ closedir(dirp);
+ return highest_version;
+}
+
+/*
+ * Return a string, allocated with malloc, containing "FILE.~VERSION~".
+ * Return 0 if out of memory.
+ */
+static char *
+make_version_name(const char *file, int version)
+{
+ char *backup_name;
+
+ if (asprintf(&backup_name, "%s.~%d~", file, version) == -1)
+ return NULL;
+ return backup_name;
+}
+
+/*
+ * If BACKUP is a numbered backup of BASE, return its version number;
+ * otherwise return 0. BASE_LENGTH is the length of BASE. BASE should
+ * already have ".~" appended to it.
+ */
+static int
+version_number(const char *base, const char *backup, size_t base_length)
+{
+ int version;
+ const char *p;
+
+ version = 0;
+ if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
+ for (p = &backup[base_length]; ISDIGIT(*p); ++p)
+ version = version * 10 + *p - '0';
+ if (p[0] != '~' || p[1])
+ version = 0;
+ }
+ return version;
+}
+
+/*
+ * Return the newly-allocated concatenation of STR1 and STR2. If out of
+ * memory, return 0.
+ */
+static char *
+concat(const char *str1, const char *str2)
+{
+ char *newstr;
+
+ if (asprintf(&newstr, "%s%s", str1, str2) == -1)
+ return NULL;
+ return newstr;
+}
+
+/*
+ * If ARG is an unambiguous match for an element of the null-terminated array
+ * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it
+ * does not match any element or -2 if it is ambiguous (is a prefix of more
+ * than one element).
+ */
+static int
+argmatch(const char *arg, const char **optlist)
+{
+ int i; /* Temporary index in OPTLIST. */
+ size_t arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen(arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; optlist[i]; i++) {
+ if (!strncmp(optlist[i], arg, arglen)) {
+ if (strlen(optlist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ /* Second nonexact match found. */
+ ambiguous = 1;
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/*
+ * Error reporting for argmatch. KIND is a description of the type of entity
+ * that was being matched. VALUE is the invalid value that was given. PROBLEM
+ * is the return value from argmatch.
+ */
+static void
+invalid_arg(const char *kind, const char *value, int problem)
+{
+ fprintf(stderr, "patch: ");
+ if (problem == -1)
+ fprintf(stderr, "invalid");
+ else /* Assume -2. */
+ fprintf(stderr, "ambiguous");
+ fprintf(stderr, " %s `%s'\n", kind, value);
+}
+
+static const char *backup_args[] = {
+ "never", "simple", "nil", "existing", "t", "numbered", 0
+};
+
+static enum backup_type backup_types[] = {
+ simple, simple, numbered_existing,
+ numbered_existing, numbered, numbered
+};
+
+/*
+ * Return the type of backup indicated by VERSION. Unique abbreviations are
+ * accepted.
+ */
+enum backup_type
+get_version(const char *version)
+{
+ int i;
+
+ if (version == NULL || *version == '\0')
+ return numbered_existing;
+ i = argmatch(version, backup_args);
+ if (i >= 0)
+ return backup_types[i];
+ invalid_arg("version control type", version, i);
+ exit(2);
+}
diff --git a/usr.bin/patch/backupfile.h b/usr.bin/patch/backupfile.h
new file mode 100644
index 0000000..71999f7
--- /dev/null
+++ b/usr.bin/patch/backupfile.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (C) 1990 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * without restriction.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * backupfile.h -- declarations for making Emacs style backup file names
+ *
+ * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $
+ * $FreeBSD$
+ */
+
+/* When to make backup files. */
+enum backup_type {
+ /* Never make backups. */
+ none,
+
+ /* Make simple backups of every file. */
+ simple,
+
+ /*
+ * Make numbered backups of files that already have numbered backups,
+ * and simple backups of the others.
+ */
+ numbered_existing,
+
+ /* Make numbered backups of every file. */
+ numbered
+};
+
+extern enum backup_type backup_type;
+extern const char *simple_backup_suffix;
+
+char *find_backup_file_name(const char *file);
+enum backup_type get_version(const char *version);
diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h
new file mode 100644
index 0000000..9425968
--- /dev/null
+++ b/usr.bin/patch/common.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: common.h,v 1.26 2006/03/11 19:41:30 otto Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define DEBUGGING
+
+/* constants */
+
+#define MAXHUNKSIZE 200000 /* is this enough lines? */
+#define INITHUNKMAX 125 /* initial dynamic allocation size */
+#define INITLINELEN 4096
+#define BUFFERSIZE 4096
+
+#define SCCSPREFIX "s."
+#define GET "get -e %s"
+#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
+
+#define RCSSUFFIX ",v"
+#define CHECKOUT "co -l %s"
+#define RCSDIFF "rcsdiff %s > /dev/null"
+
+#define ORIGEXT ".orig"
+#define REJEXT ".rej"
+
+/* handy definitions */
+
+#define strNE(s1,s2) (strcmp(s1, s2))
+#define strEQ(s1,s2) (!strcmp(s1, s2))
+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
+
+/* typedefs */
+
+typedef long LINENUM; /* must be signed */
+
+/* globals */
+
+extern mode_t filemode;
+
+extern char *buf; /* general purpose buffer */
+extern size_t buf_size; /* size of general purpose buffer */
+
+extern bool using_plan_a; /* try to keep everything in memory */
+extern bool out_of_mem; /* ran out of memory in plan a */
+
+#define MAXFILEC 2
+
+extern char *filearg[MAXFILEC];
+extern bool ok_to_create_file;
+extern char *outname;
+extern char *origprae;
+
+extern char *TMPOUTNAME;
+extern char *TMPINNAME;
+extern char *TMPREJNAME;
+extern char *TMPPATNAME;
+extern bool toutkeep;
+extern bool trejkeep;
+
+#ifdef DEBUGGING
+extern int debug;
+#endif
+
+extern bool force;
+extern bool batch;
+extern bool verbose;
+extern bool reverse;
+extern bool noreverse;
+extern bool skip_rest_of_patch;
+extern int strippath;
+extern bool canonicalize;
+/* TRUE if -C was specified on command line. */
+extern bool check_only;
+extern bool warn_on_invalid_line;
+extern bool last_line_missing_eol;
+
+
+#define CONTEXT_DIFF 1
+#define NORMAL_DIFF 2
+#define ED_DIFF 3
+#define NEW_CONTEXT_DIFF 4
+#define UNI_DIFF 5
+
+extern int diff_type;
+extern char *revision; /* prerequisite revision, if any */
+extern LINENUM input_lines; /* how long is input file in lines */
+
+extern int posix;
+
diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c
new file mode 100644
index 0000000..54b0bf2
--- /dev/null
+++ b/usr.bin/patch/inp.c
@@ -0,0 +1,484 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: inp.c,v 1.36 2012/04/10 14:46:34 ajacoutot Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+
+
+/* Input-file-with-indexable-lines abstract type */
+
+static size_t i_size; /* size of the input file */
+static char *i_womp; /* plan a buffer for entire file */
+static char **i_ptr; /* pointers to lines in i_womp */
+static char empty_line[] = { '\0' };
+
+static int tifd = -1; /* plan b virtual string array */
+static char *tibuf[2]; /* plan b buffers */
+static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
+static LINENUM lines_per_buf; /* how many lines per buffer */
+static int tireclen; /* length of records in tmp file */
+
+static bool rev_in_string(const char *);
+static bool reallocate_lines(size_t *);
+
+/* returns false if insufficient memory */
+static bool plan_a(const char *);
+
+static void plan_b(const char *);
+
+/* New patch--prepare to edit another file. */
+
+void
+re_input(void)
+{
+ if (using_plan_a) {
+ free(i_ptr);
+ i_ptr = NULL;
+ if (i_womp != NULL) {
+ munmap(i_womp, i_size);
+ i_womp = NULL;
+ }
+ i_size = 0;
+ } else {
+ using_plan_a = true; /* maybe the next one is smaller */
+ close(tifd);
+ tifd = -1;
+ free(tibuf[0]);
+ free(tibuf[1]);
+ tibuf[0] = tibuf[1] = NULL;
+ tiline[0] = tiline[1] = -1;
+ tireclen = 0;
+ }
+}
+
+/* Construct the line index, somehow or other. */
+
+void
+scan_input(const char *filename)
+{
+ if (!plan_a(filename))
+ plan_b(filename);
+ if (verbose) {
+ say("Patching file %s using Plan %s...\n", filename,
+ (using_plan_a ? "A" : "B"));
+ }
+}
+
+static bool
+reallocate_lines(size_t *lines_allocated)
+{
+ char **p;
+ size_t new_size;
+
+ new_size = *lines_allocated * 3 / 2;
+ p = realloc(i_ptr, (new_size + 2) * sizeof(char *));
+ if (p == NULL) { /* shucks, it was a near thing */
+ munmap(i_womp, i_size);
+ i_womp = NULL;
+ free(i_ptr);
+ i_ptr = NULL;
+ *lines_allocated = 0;
+ return false;
+ }
+ *lines_allocated = new_size;
+ i_ptr = p;
+ return true;
+}
+
+/* Try keeping everything in memory. */
+
+static bool
+plan_a(const char *filename)
+{
+ int ifd, statfailed;
+ char *p, *s, lbuf[INITLINELEN];
+ struct stat filestat;
+ ptrdiff_t sz;
+ size_t i;
+ size_t iline, lines_allocated;
+
+#ifdef DEBUGGING
+ if (debug & 8)
+ return false;
+#endif
+
+ if (filename == NULL || *filename == '\0')
+ return false;
+
+ statfailed = stat(filename, &filestat);
+ if (statfailed && ok_to_create_file) {
+ if (verbose)
+ say("(Creating file %s...)\n", filename);
+
+ /*
+ * in check_patch case, we still display `Creating file' even
+ * though we're not. The rule is that -C should be as similar
+ * to normal patch behavior as possible
+ */
+ if (check_only)
+ return true;
+ makedirs(filename, true);
+ close(creat(filename, 0666));
+ statfailed = stat(filename, &filestat);
+ }
+ if (statfailed && check_only)
+ fatal("%s not found, -C mode, can't probe further\n", filename);
+ /* For nonexistent or read-only files, look for RCS or SCCS versions. */
+ if (statfailed ||
+ /* No one can write to it. */
+ (filestat.st_mode & 0222) == 0 ||
+ /* I can't write to it. */
+ ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
+ const char *cs = NULL, *filebase, *filedir;
+ struct stat cstat;
+ char *tmp_filename1, *tmp_filename2;
+
+ tmp_filename1 = strdup(filename);
+ tmp_filename2 = strdup(filename);
+ if (tmp_filename1 == NULL || tmp_filename2 == NULL)
+ fatal("strdupping filename");
+ filebase = basename(tmp_filename1);
+ filedir = dirname(tmp_filename2);
+
+ /* Leave room in lbuf for the diff command. */
+ s = lbuf + 20;
+
+#define try(f, a1, a2, a3) \
+ (snprintf(s, buf_size - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
+
+ if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
+ try("%s/RCS/%s%s", filedir, filebase, "") ||
+ try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
+ snprintf(buf, buf_size, CHECKOUT, filename);
+ snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
+ cs = "RCS";
+ } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
+ try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
+ snprintf(buf, buf_size, GET, s);
+ snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
+ cs = "SCCS";
+ } else if (statfailed)
+ fatal("can't find %s\n", filename);
+
+ free(tmp_filename1);
+ free(tmp_filename2);
+
+ /*
+ * else we can't write to it but it's not under a version
+ * control system, so just proceed.
+ */
+ if (cs) {
+ if (!statfailed) {
+ if ((filestat.st_mode & 0222) != 0)
+ /* The owner can write to it. */
+ fatal("file %s seems to be locked "
+ "by somebody else under %s\n",
+ filename, cs);
+ /*
+ * It might be checked out unlocked. See if
+ * it's safe to check out the default version
+ * locked.
+ */
+ if (verbose)
+ say("Comparing file %s to default "
+ "%s version...\n",
+ filename, cs);
+ if (system(lbuf))
+ fatal("can't check out file %s: "
+ "differs from default %s version\n",
+ filename, cs);
+ }
+ if (verbose)
+ say("Checking out file %s from %s...\n",
+ filename, cs);
+ if (system(buf) || stat(filename, &filestat))
+ fatal("can't check out file %s from %s\n",
+ filename, cs);
+ }
+ }
+ filemode = filestat.st_mode;
+ if (!S_ISREG(filemode))
+ fatal("%s is not a normal file--can't patch\n", filename);
+ if ((uint64_t)filestat.st_size > SIZE_MAX) {
+ say("block too large to mmap\n");
+ return false;
+ }
+ i_size = (size_t)filestat.st_size;
+ if (out_of_mem) {
+ set_hunkmax(); /* make sure dynamic arrays are allocated */
+ out_of_mem = false;
+ return false; /* force plan b because plan a bombed */
+ }
+ if ((ifd = open(filename, O_RDONLY)) < 0)
+ pfatal("can't open file %s", filename);
+
+ if (i_size) {
+ i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
+ if (i_womp == MAP_FAILED) {
+ perror("mmap failed");
+ i_womp = NULL;
+ close(ifd);
+ return false;
+ }
+ } else {
+ i_womp = NULL;
+ }
+
+ close(ifd);
+ if (i_size)
+ madvise(i_womp, i_size, MADV_SEQUENTIAL);
+
+ /* estimate the number of lines */
+ lines_allocated = i_size / 25;
+ if (lines_allocated < 100)
+ lines_allocated = 100;
+
+ if (!reallocate_lines(&lines_allocated))
+ return false;
+
+ /* now scan the buffer and build pointer array */
+ iline = 1;
+ i_ptr[iline] = i_womp;
+ /* test for NUL too, to maintain the behavior of the original code */
+ for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
+ if (*s == '\n') {
+ if (iline == lines_allocated) {
+ if (!reallocate_lines(&lines_allocated))
+ return false;
+ }
+ /* these are NOT NUL terminated */
+ i_ptr[++iline] = s + 1;
+ }
+ }
+ /* if the last line contains no EOL, append one */
+ if (i_size > 0 && i_womp[i_size - 1] != '\n') {
+ last_line_missing_eol = true;
+ /* fix last line */
+ sz = s - i_ptr[iline];
+ p = malloc(sz + 1);
+ if (p == NULL) {
+ free(i_ptr);
+ i_ptr = NULL;
+ munmap(i_womp, i_size);
+ i_womp = NULL;
+ return false;
+ }
+
+ memcpy(p, i_ptr[iline], sz);
+ p[sz] = '\n';
+ i_ptr[iline] = p;
+ /* count the extra line and make it point to some valid mem */
+ i_ptr[++iline] = empty_line;
+ } else
+ last_line_missing_eol = false;
+
+ input_lines = iline - 1;
+
+ /* now check for revision, if any */
+
+ if (revision != NULL) {
+ if (!rev_in_string(i_womp)) {
+ if (force) {
+ if (verbose)
+ say("Warning: this file doesn't appear "
+ "to be the %s version--patching anyway.\n",
+ revision);
+ } else if (batch) {
+ fatal("this file doesn't appear to be the "
+ "%s version--aborting.\n",
+ revision);
+ } else {
+ ask("This file doesn't appear to be the "
+ "%s version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal("aborted\n");
+ }
+ } else if (verbose)
+ say("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ return true; /* plan a will work */
+}
+
+/* Keep (virtually) nothing in memory. */
+
+static void
+plan_b(const char *filename)
+{
+ FILE *ifp;
+ size_t i = 0, j, maxlen = 1;
+ char *p;
+ bool found_revision = (revision == NULL);
+
+ using_plan_a = false;
+ if ((ifp = fopen(filename, "r")) == NULL)
+ pfatal("can't open file %s", filename);
+ unlink(TMPINNAME);
+ if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
+ pfatal("can't open file %s", TMPINNAME);
+ while (fgets(buf, buf_size, ifp) != NULL) {
+ if (revision != NULL && !found_revision && rev_in_string(buf))
+ found_revision = true;
+ if ((i = strlen(buf)) > maxlen)
+ maxlen = i; /* find longest line */
+ }
+ last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
+ if (last_line_missing_eol && maxlen == i)
+ maxlen++;
+
+ if (revision != NULL) {
+ if (!found_revision) {
+ if (force) {
+ if (verbose)
+ say("Warning: this file doesn't appear "
+ "to be the %s version--patching anyway.\n",
+ revision);
+ } else if (batch) {
+ fatal("this file doesn't appear to be the "
+ "%s version--aborting.\n",
+ revision);
+ } else {
+ ask("This file doesn't appear to be the %s "
+ "version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal("aborted\n");
+ }
+ } else if (verbose)
+ say("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ fseek(ifp, 0L, SEEK_SET); /* rewind file */
+ lines_per_buf = BUFFERSIZE / maxlen;
+ tireclen = maxlen;
+ tibuf[0] = malloc(BUFFERSIZE + 1);
+ if (tibuf[0] == NULL)
+ fatal("out of memory\n");
+ tibuf[1] = malloc(BUFFERSIZE + 1);
+ if (tibuf[1] == NULL)
+ fatal("out of memory\n");
+ for (i = 1;; i++) {
+ p = tibuf[0] + maxlen * (i % lines_per_buf);
+ if (i % lines_per_buf == 0) /* new block */
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ pfatal("can't write temp file");
+ if (fgets(p, maxlen + 1, ifp) == NULL) {
+ input_lines = i - 1;
+ if (i % lines_per_buf != 0)
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ pfatal("can't write temp file");
+ break;
+ }
+ j = strlen(p);
+ /* These are '\n' terminated strings, so no need to add a NUL */
+ if (j == 0 || p[j - 1] != '\n')
+ p[j] = '\n';
+ }
+ fclose(ifp);
+ close(tifd);
+ if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
+ pfatal("can't reopen file %s", TMPINNAME);
+}
+
+/*
+ * Fetch a line from the input file, \n terminated, not necessarily \0.
+ */
+char *
+ifetch(LINENUM line, int whichbuf)
+{
+ if (line < 1 || line > input_lines) {
+ if (warn_on_invalid_line) {
+ say("No such line %ld in input file, ignoring\n", line);
+ warn_on_invalid_line = false;
+ }
+ return NULL;
+ }
+ if (using_plan_a)
+ return i_ptr[line];
+ else {
+ LINENUM offline = line % lines_per_buf;
+ LINENUM baseline = line - offline;
+
+ if (tiline[0] == baseline)
+ whichbuf = 0;
+ else if (tiline[1] == baseline)
+ whichbuf = 1;
+ else {
+ tiline[whichbuf] = baseline;
+
+ if (lseek(tifd, (off_t) (baseline / lines_per_buf *
+ BUFFERSIZE), SEEK_SET) < 0)
+ pfatal("cannot seek in the temporary input file");
+
+ if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
+ pfatal("error reading tmp file %s", TMPINNAME);
+ }
+ return tibuf[whichbuf] + (tireclen * offline);
+ }
+}
+
+/*
+ * True if the string argument contains the revision number we want.
+ */
+static bool
+rev_in_string(const char *string)
+{
+ const char *s;
+ size_t patlen;
+
+ if (revision == NULL)
+ return true;
+ patlen = strlen(revision);
+ if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
+ return true;
+ for (s = string; *s; s++) {
+ if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
+ isspace((unsigned char)s[patlen + 1])) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/usr.bin/patch/inp.h b/usr.bin/patch/inp.h
new file mode 100644
index 0000000..062798a
--- /dev/null
+++ b/usr.bin/patch/inp.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $
+ * $FreeBSD$
+ */
+
+void re_input(void);
+void scan_input(const char *);
+char *ifetch(LINENUM, int);
diff --git a/usr.bin/patch/mkpath.c b/usr.bin/patch/mkpath.c
new file mode 100644
index 0000000..04fd537
--- /dev/null
+++ b/usr.bin/patch/mkpath.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1983, 1992, 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.
+ *
+ * $OpenBSD: mkpath.c,v 1.2 2005/06/20 07:14:06 otto Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+int mkpath(char *);
+
+/* Code taken directly from mkdir(1).
+
+ * mkpath -- create directories.
+ * path - path
+ */
+int
+mkpath(char *path)
+{
+ struct stat sb;
+ char *slash;
+ int done = 0;
+
+ slash = path;
+
+ while (!done) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ done = (*slash == '\0');
+ *slash = '\0';
+
+ if (stat(path, &sb)) {
+ if (errno != ENOENT || (mkdir(path, 0777) &&
+ errno != EEXIST)) {
+ warn("%s", path);
+ return (-1);
+ }
+ } else if (!S_ISDIR(sb.st_mode)) {
+ warnx("%s: %s", path, strerror(ENOTDIR));
+ return (-1);
+ }
+
+ *slash = '/';
+ }
+
+ return (0);
+}
+
diff --git a/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1
new file mode 100644
index 0000000..312b8a2
--- /dev/null
+++ b/usr.bin/patch/patch.1
@@ -0,0 +1,700 @@
+.\"-
+.\" Copyright 1986, Larry Wall
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following condition
+.\" is met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this condition and the following disclaimer.
+.\"
+.\" 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.
+.\"
+.\" $OpenBSD: patch.1,v 1.26 2010/09/03 11:09:29 jmc Exp $
+.\" $FreeBSD$
+.Dd January 29, 2013
+.Dt PATCH 1
+.Os
+.Sh NAME
+.Nm patch
+.Nd apply a diff file to an original
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl bCcEeflNnRstuv
+.Op Fl B Ar backup-prefix
+.Op Fl D Ar symbol
+.Op Fl d Ar directory
+.Op Fl F Ar max-fuzz
+.Op Fl i Ar patchfile
+.Op Fl o Ar out-file
+.Op Fl p Ar strip-count
+.Op Fl r Ar rej-name
+.Op Fl V Cm t | nil | never
+.Op Fl x Ar number
+.Op Fl z Ar backup-ext
+.Op Fl Fl posix
+.Op Ar origfile Op Ar patchfile
+.Ek
+.Nm
+.Pf \*(Lt Ar patchfile
+.Sh DESCRIPTION
+.Nm
+will take a patch file containing any of the four forms of difference
+listing produced by the
+.Xr diff 1
+program and apply those differences to an original file,
+producing a patched version.
+If
+.Ar patchfile
+is omitted, or is a hyphen, the patch will be read from the standard input.
+.Pp
+.Nm
+will attempt to determine the type of the diff listing, unless overruled by a
+.Fl c ,
+.Fl e ,
+.Fl n ,
+or
+.Fl u
+option.
+Context diffs (old-style, new-style, and unified) and
+normal diffs are applied directly by the
+.Nm
+program itself, whereas ed diffs are simply fed to the
+.Xr ed 1
+editor via a pipe.
+.Pp
+If the
+.Ar patchfile
+contains more than one patch,
+.Nm
+will try to apply each of them as if they came from separate patch files.
+This means, among other things, that it is assumed that the name of the file
+to patch must be determined for each diff listing, and that the garbage before
+each diff listing will be examined for interesting things such as file names
+and revision level (see the section on
+.Sx Filename Determination
+below).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Xo
+.Fl B Ar backup-prefix ,
+.Fl Fl prefix Ar backup-prefix
+.Xc
+Causes the next argument to be interpreted as a prefix to the backup file
+name.
+If this argument is specified, any argument to
+.Fl z
+will be ignored.
+.It Fl b , Fl Fl backup
+Save a backup copy of the file before it is modified.
+By default the original file is saved with a backup extension of
+.Qq .orig
+unless the file already has a numbered backup, in which case a numbered
+backup is made.
+This is equivalent to specifying
+.Qo Fl V Cm existing Qc .
+This option is currently the default, unless
+.Fl -posix
+is specified.
+.It Fl C , Fl Fl check
+Checks that the patch would apply cleanly, but does not modify anything.
+.It Fl c , Fl Fl context
+Forces
+.Nm
+to interpret the patch file as a context diff.
+.It Xo
+.Fl D Ar symbol ,
+.Fl Fl ifdef Ar symbol
+.Xc
+Causes
+.Nm
+to use the
+.Qq #ifdef...#endif
+construct to mark changes.
+The argument following will be used as the differentiating symbol.
+Note that, unlike the C compiler, there must be a space between the
+.Fl D
+and the argument.
+.It Xo
+.Fl d Ar directory ,
+.Fl Fl directory Ar directory
+.Xc
+Causes
+.Nm
+to interpret the next argument as a directory,
+and change the working directory to it before doing anything else.
+.It Fl E , Fl Fl remove-empty-files
+Causes
+.Nm
+to remove output files that are empty after the patches have been applied.
+This option is useful when applying patches that create or remove files.
+.It Fl e , Fl Fl ed
+Forces
+.Nm
+to interpret the patch file as an
+.Xr ed 1
+script.
+.It Xo
+.Fl F Ar max-fuzz ,
+.Fl Fl fuzz Ar max-fuzz
+.Xc
+Sets the maximum fuzz factor.
+This option only applies to context diffs, and causes
+.Nm
+to ignore up to that many lines in looking for places to install a hunk.
+Note that a larger fuzz factor increases the odds of a faulty patch.
+The default fuzz factor is 2, and it may not be set to more than
+the number of lines of context in the context diff, ordinarily 3.
+.It Fl f , Fl Fl force
+Forces
+.Nm
+to assume that the user knows exactly what he or she is doing, and to not
+ask any questions.
+It assumes the following:
+skip patches for which a file to patch can't be found;
+patch files even though they have the wrong version for the
+.Qq Prereq:
+line in the patch;
+and assume that patches are not reversed even if they look like they are.
+This option does not suppress commentary; use
+.Fl s
+for that.
+.It Xo
+.Fl i Ar patchfile ,
+.Fl Fl input Ar patchfile
+.Xc
+Causes the next argument to be interpreted as the input file name
+(i.e. a patchfile).
+This option may be specified multiple times.
+.It Fl l , Fl Fl ignore-whitespace
+Causes the pattern matching to be done loosely, in case the tabs and
+spaces have been munged in your input file.
+Any sequence of whitespace in the pattern line will match any sequence
+in the input file.
+Normal characters must still match exactly.
+Each line of the context must still match a line in the input file.
+.It Fl N , Fl Fl forward
+Causes
+.Nm
+to ignore patches that it thinks are reversed or already applied.
+See also
+.Fl R .
+.It Fl n , Fl Fl normal
+Forces
+.Nm
+to interpret the patch file as a normal diff.
+.It Xo
+.Fl o Ar out-file ,
+.Fl Fl output Ar out-file
+.Xc
+Causes the next argument to be interpreted as the output file name.
+.It Xo
+.Fl p Ar strip-count ,
+.Fl Fl strip Ar strip-count
+.Xc
+Sets the pathname strip count,
+which controls how pathnames found in the patch file are treated,
+in case you keep your files in a different directory than the person who sent
+out the patch.
+The strip count specifies how many slashes are to be stripped from
+the front of the pathname.
+(Any intervening directory names also go away.)
+For example, supposing the file name in the patch file was
+.Pa /u/howard/src/blurfl/blurfl.c :
+.Pp
+Setting
+.Fl p Ns Ar 0
+gives the entire pathname unmodified.
+.Pp
+.Fl p Ns Ar 1
+gives
+.Pp
+.D1 Pa u/howard/src/blurfl/blurfl.c
+.Pp
+without the leading slash.
+.Pp
+.Fl p Ns Ar 4
+gives
+.Pp
+.D1 Pa blurfl/blurfl.c
+.Pp
+Not specifying
+.Fl p
+at all just gives you
+.Pa blurfl.c ,
+unless all of the directories in the leading path
+.Pq Pa u/howard/src/blurfl
+exist and that path is relative,
+in which case you get the entire pathname unmodified.
+Whatever you end up with is looked for either in the current directory,
+or the directory specified by the
+.Fl d
+option.
+.It Fl R , Fl Fl reverse
+Tells
+.Nm
+that this patch was created with the old and new files swapped.
+(Yes, I'm afraid that does happen occasionally, human nature being what it
+is.)
+.Nm
+will attempt to swap each hunk around before applying it.
+Rejects will come out in the swapped format.
+The
+.Fl R
+option will not work with ed diff scripts because there is too little
+information to reconstruct the reverse operation.
+.Pp
+If the first hunk of a patch fails,
+.Nm
+will reverse the hunk to see if it can be applied that way.
+If it can, you will be asked if you want to have the
+.Fl R
+option set.
+If it can't, the patch will continue to be applied normally.
+(Note: this method cannot detect a reversed patch if it is a normal diff
+and if the first command is an append (i.e. it should have been a delete)
+since appends always succeed, due to the fact that a null context will match
+anywhere.
+Luckily, most patches add or change lines rather than delete them, so most
+reversed normal diffs will begin with a delete, which will fail, triggering
+the heuristic.)
+.It Xo
+.Fl r Ar rej-name ,
+.Fl Fl reject-file Ar rej-name
+.Xc
+Causes the next argument to be interpreted as the reject file name.
+.It Xo
+.Fl s , Fl Fl quiet ,
+.Fl Fl silent
+.Xc
+Makes
+.Nm
+do its work silently, unless an error occurs.
+.It Fl t , Fl Fl batch
+Similar to
+.Fl f ,
+in that it suppresses questions, but makes some different assumptions:
+skip patches for which a file to patch can't be found (the same as
+.Fl f ) ;
+skip patches for which the file has the wrong version for the
+.Qq Prereq:
+line in the patch;
+and assume that patches are reversed if they look like they are.
+.It Fl u , Fl Fl unified
+Forces
+.Nm
+to interpret the patch file as a unified context diff (a unidiff).
+.It Xo
+.Fl V Cm t | nil | never ,
+.Fl Fl version-control Cm t | nil | never
+.Xc
+Causes the next argument to be interpreted as a method for creating
+backup file names.
+The type of backups made can also be given in the
+.Ev PATCH_VERSION_CONTROL
+or
+.Ev VERSION_CONTROL
+environment variables, which are overridden by this option.
+The
+.Fl B
+option overrides this option, causing the prefix to always be used for
+making backup file names.
+The values of the
+.Ev PATCH_VERSION_CONTROL
+and
+.Ev VERSION_CONTROL
+environment variables and the argument to the
+.Fl V
+option are like the GNU Emacs
+.Dq version-control
+variable; they also recognize synonyms that are more descriptive.
+The valid values are (unique abbreviations are accepted):
+.Bl -tag -width Ds -offset indent
+.It Cm t , numbered
+Always make numbered backups.
+.It Cm nil , existing
+Make numbered backups of files that already have them,
+simple backups of the others.
+.It Cm never , simple
+Always make simple backups.
+.El
+.It Fl v , Fl Fl version
+Causes
+.Nm
+to print out its revision header and patch level.
+.It Xo
+.Fl x Ar number ,
+.Fl Fl debug Ar number
+.Xc
+Sets internal debugging flags, and is of interest only to
+.Nm
+patchers.
+.It Xo
+.Fl z Ar backup-ext ,
+.Fl Fl suffix Ar backup-ext
+.Xc
+Causes the next argument to be interpreted as the backup extension, to be
+used in place of
+.Qq .orig .
+.It Fl Fl posix
+Enables strict
+.St -p1003.1-2008
+conformance, specifically:
+.Bl -enum
+.It
+Backup files are not created unless the
+.Fl b
+option is specified.
+.It
+If unspecified, the file name used is the first of the old, new and
+index files that exists.
+.El
+.El
+.Ss Patch Application
+.Nm
+will try to skip any leading garbage, apply the diff,
+and then skip any trailing garbage.
+Thus you could feed an article or message containing a
+diff listing to
+.Nm ,
+and it should work.
+If the entire diff is indented by a consistent amount,
+this will be taken into account.
+.Pp
+With context diffs, and to a lesser extent with normal diffs,
+.Nm
+can detect when the line numbers mentioned in the patch are incorrect,
+and will attempt to find the correct place to apply each hunk of the patch.
+As a first guess, it takes the line number mentioned for the hunk, plus or
+minus any offset used in applying the previous hunk.
+If that is not the correct place,
+.Nm
+will scan both forwards and backwards for a set of lines matching the context
+given in the hunk.
+First
+.Nm
+looks for a place where all lines of the context match.
+If no such place is found, and it's a context diff, and the maximum fuzz factor
+is set to 1 or more, then another scan takes place ignoring the first and last
+line of context.
+If that fails, and the maximum fuzz factor is set to 2 or more,
+the first two and last two lines of context are ignored,
+and another scan is made.
+.Pq The default maximum fuzz factor is 2.
+.Pp
+If
+.Nm
+cannot find a place to install that hunk of the patch, it will put the hunk
+out to a reject file, which normally is the name of the output file plus
+.Qq .rej .
+(Note that the rejected hunk will come out in context diff form whether the
+input patch was a context diff or a normal diff.
+If the input was a normal diff, many of the contexts will simply be null.)
+The line numbers on the hunks in the reject file may be different than
+in the patch file: they reflect the approximate location patch thinks the
+failed hunks belong in the new file rather than the old one.
+.Pp
+As each hunk is completed, you will be told whether the hunk succeeded or
+failed, and which line (in the new file)
+.Nm
+thought the hunk should go on.
+If this is different from the line number specified in the diff,
+you will be told the offset.
+A single large offset MAY be an indication that a hunk was installed in the
+wrong place.
+You will also be told if a fuzz factor was used to make the match, in which
+case you should also be slightly suspicious.
+.Ss Filename Determination
+If no original file is specified on the command line,
+.Nm
+will try to figure out from the leading garbage what the name of the file
+to edit is.
+When checking a prospective file name, pathname components are stripped
+as specified by the
+.Fl p
+option and the file's existence and writability are checked relative
+to the current working directory (or the directory specified by the
+.Fl d
+option).
+.Pp
+If the diff is a context or unified diff,
+.Nm
+is able to determine the old and new file names from the diff header.
+For context diffs, the
+.Dq old
+file is specified in the line beginning with
+.Qq ***
+and the
+.Dq new
+file is specified in the line beginning with
+.Qq --- .
+For a unified diff, the
+.Dq old
+file is specified in the line beginning with
+.Qq ---
+and the
+.Dq new
+file is specified in the line beginning with
+.Qq +++ .
+If there is an
+.Qq Index:
+line in the leading garbage (regardless of the diff type),
+.Nm
+will use the file name from that line as the
+.Dq index
+file.
+.Pp
+.Nm
+will choose the file name by performing the following steps, with the first
+match used:
+.Bl -enum
+.It
+If
+.Nm
+is operating in strict
+.St -p1003.1-2008
+mode, the first of the
+.Dq old ,
+.Dq new
+and
+.Dq index
+file names that exist is used.
+Otherwise,
+.Nm
+will examine either the
+.Dq old
+and
+.Dq new
+file names or, for a non-context diff, the
+.Dq index
+file name, and choose the file name with the fewest path components,
+the shortest basename, and the shortest total file name length (in that order).
+.It
+If no file exists,
+.Nm
+checks for the existence of the files in an SCCS or RCS directory
+(using the appropriate prefix or suffix) using the criteria specified
+above.
+If found,
+.Nm
+will attempt to get or check out the file.
+.It
+If no suitable file was found to patch, the patch file is a context or
+unified diff, and the old file was zero length, the new file name is
+created and used.
+.It
+If the file name still cannot be determined,
+.Nm
+will prompt the user for the file name to use.
+.El
+.Pp
+Additionally, if the leading garbage contains a
+.Qq Prereq:\ \&
+line,
+.Nm
+will take the first word from the prerequisites line (normally a version
+number) and check the input file to see if that word can be found.
+If not,
+.Nm
+will ask for confirmation before proceeding.
+.Pp
+The upshot of all this is that you should be able to say, while in a news
+interface, the following:
+.Pp
+.Dl | patch -d /usr/src/local/blurfl
+.Pp
+and patch a file in the blurfl directory directly from the article containing
+the patch.
+.Ss Backup Files
+By default, the patched version is put in place of the original, with
+the original file backed up to the same name with the extension
+.Qq .orig ,
+or as specified by the
+.Fl B ,
+.Fl V ,
+or
+.Fl z
+options.
+The extension used for making backup files may also be specified in the
+.Ev SIMPLE_BACKUP_SUFFIX
+environment variable, which is overridden by the options above.
+.Pp
+If the backup file is a symbolic or hard link to the original file,
+.Nm
+creates a new backup file name by changing the first lowercase letter
+in the last component of the file's name into uppercase.
+If there are no more lowercase letters in the name,
+it removes the first character from the name.
+It repeats this process until it comes up with a
+backup file that does not already exist or is not linked to the original file.
+.Pp
+You may also specify where you want the output to go with the
+.Fl o
+option; if that file already exists, it is backed up first.
+.Ss Notes For Patch Senders
+There are several things you should bear in mind if you are going to
+be sending out patches:
+.Pp
+First, you can save people a lot of grief by keeping a
+.Pa patchlevel.h
+file which is patched to increment the patch level as the first diff in the
+patch file you send out.
+If you put a
+.Qq Prereq:
+line in with the patch, it won't let them apply
+patches out of order without some warning.
+.Pp
+Second, make sure you've specified the file names right, either in a
+context diff header, or with an
+.Qq Index:
+line.
+If you are patching something in a subdirectory, be sure to tell the patch
+user to specify a
+.Fl p
+option as needed.
+.Pp
+Third, you can create a file by sending out a diff that compares a
+null file to the file you want to create.
+This will only work if the file you want to create doesn't exist already in
+the target directory.
+.Pp
+Fourth, take care not to send out reversed patches, since it makes people wonder
+whether they already applied the patch.
+.Pp
+Fifth, while you may be able to get away with putting 582 diff listings into
+one file, it is probably wiser to group related patches into separate files in
+case something goes haywire.
+.Sh ENVIRONMENT
+.Bl -tag -width "PATCH_VERSION_CONTROL" -compact
+.It Ev POSIXLY_CORRECT
+When set,
+.Nm
+behaves as if the
+.Fl Fl posix
+option has been specified.
+.It Ev SIMPLE_BACKUP_SUFFIX
+Extension to use for backup file names instead of
+.Qq .orig .
+.It Ev TMPDIR
+Directory to put temporary files in; default is
+.Pa /tmp .
+.It Ev PATCH_VERSION_CONTROL
+Selects when numbered backup files are made.
+.It Ev VERSION_CONTROL
+Same as
+.Ev PATCH_VERSION_CONTROL .
+.El
+.Sh FILES
+.Bl -tag -width "$TMPDIR/patch*" -compact
+.It Pa $TMPDIR/patch*
+.Nm
+temporary files
+.It Pa /dev/tty
+used to read input when
+.Nm
+prompts the user
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It 0
+Successful completion.
+.It 1
+One or more lines were written to a reject file.
+.It \*(Gt1
+An error occurred.
+.El
+.Pp
+When applying a set of patches in a loop it behooves you to check this
+exit status so you don't apply a later patch to a partially patched file.
+.Sh DIAGNOSTICS
+Too many to list here, but generally indicative that
+.Nm
+couldn't parse your patch file.
+.Pp
+The message
+.Qq Hmm...
+indicates that there is unprocessed text in the patch file and that
+.Nm
+is attempting to intuit whether there is a patch in that text and, if so,
+what kind of patch it is.
+.Sh SEE ALSO
+.Xr diff 1
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification
+(except as detailed above for the
+.Fl -posix
+option),
+though the presence of
+.Nm
+itself is optional.
+.Pp
+The flags
+.Op Fl BCEFfstVvxz
+and
+.Op Fl -posix
+are extensions to that specification.
+.Sh AUTHORS
+.An Larry Wall
+with many other contributors.
+.Sh CAVEATS
+.Nm
+cannot tell if the line numbers are off in an ed script, and can only detect
+bad line numbers in a normal diff when it finds a
+.Qq change
+or a
+.Qq delete
+command.
+A context diff using fuzz factor 3 may have the same problem.
+Until a suitable interactive interface is added, you should probably do
+a context diff in these cases to see if the changes made sense.
+Of course, compiling without errors is a pretty good indication that the patch
+worked, but not always.
+.Pp
+.Nm
+usually produces the correct results, even when it has to do a lot of
+guessing.
+However, the results are guaranteed to be correct only when the patch is
+applied to exactly the same version of the file that the patch was
+generated from.
+.Sh BUGS
+Could be smarter about partial matches, excessively deviant offsets and
+swapped code, but that would take an extra pass.
+.Pp
+Check patch mode
+.Pq Fl C
+will fail if you try to check several patches in succession that build on
+each other.
+The entire
+.Nm
+code would have to be restructured to keep temporary files around so that it
+can handle this situation.
+.Pp
+If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
+#endif),
+.Nm
+is incapable of patching both versions, and, if it works at all, will likely
+patch the wrong one, and tell you that it succeeded to boot.
+.Pp
+If you apply a patch you've already applied,
+.Nm
+will think it is a reversed patch, and offer to un-apply the patch.
+This could be construed as a feature.
diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c
new file mode 100644
index 0000000..d008c0f
--- /dev/null
+++ b/usr.bin/patch/patch.c
@@ -0,0 +1,1069 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: patch.c,v 1.50 2012/05/15 19:32:02 millert Exp $
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+#include "backupfile.h"
+#include "pathnames.h"
+
+mode_t filemode = 0644;
+
+char *buf; /* general purpose buffer */
+size_t buf_size; /* size of the general purpose buffer */
+
+bool using_plan_a = true; /* try to keep everything in memory */
+bool out_of_mem = false; /* ran out of memory in plan a */
+
+#define MAXFILEC 2
+
+char *filearg[MAXFILEC];
+bool ok_to_create_file = false;
+char *outname = NULL;
+char *origprae = NULL;
+char *TMPOUTNAME;
+char *TMPINNAME;
+char *TMPREJNAME;
+char *TMPPATNAME;
+bool toutkeep = false;
+bool trejkeep = false;
+bool warn_on_invalid_line;
+bool last_line_missing_eol;
+
+#ifdef DEBUGGING
+int debug = 0;
+#endif
+
+bool force = false;
+bool batch = false;
+bool verbose = true;
+bool reverse = false;
+bool noreverse = false;
+bool skip_rest_of_patch = false;
+int strippath = 957;
+bool canonicalize = false;
+bool check_only = false;
+int diff_type = 0;
+char *revision = NULL; /* prerequisite revision, if any */
+LINENUM input_lines = 0; /* how long is input file in lines */
+int posix = 0; /* strict POSIX mode? */
+
+static void reinitialize_almost_everything(void);
+static void get_some_switches(void);
+static LINENUM locate_hunk(LINENUM);
+static void abort_context_hunk(void);
+static void rej_line(int, LINENUM);
+static void abort_hunk(void);
+static void apply_hunk(LINENUM);
+static void init_output(const char *);
+static void init_reject(const char *);
+static void copy_till(LINENUM, bool);
+static bool spew_output(void);
+static void dump_line(LINENUM, bool);
+static bool patch_match(LINENUM, LINENUM, LINENUM);
+static bool similar(const char *, const char *, int);
+static void usage(void);
+
+/* true if -E was specified on command line. */
+static bool remove_empty_files = false;
+
+/* true if -R was specified on command line. */
+static bool reverse_flag_specified = false;
+
+/* buffer holding the name of the rejected patch file. */
+static char rejname[NAME_MAX + 1];
+
+/* how many input lines have been irretractibly output */
+static LINENUM last_frozen_line = 0;
+
+static int Argc; /* guess */
+static char **Argv;
+static int Argc_last; /* for restarting plan_b */
+static char **Argv_last;
+
+static FILE *ofp = NULL; /* output file pointer */
+static FILE *rejfp = NULL; /* reject file pointer */
+
+static int filec = 0; /* how many file arguments? */
+static LINENUM last_offset = 0;
+static LINENUM maxfuzz = 2;
+
+/* patch using ifdef, ifndef, etc. */
+static bool do_defines = false;
+/* #ifdef xyzzy */
+static char if_defined[128];
+/* #ifndef xyzzy */
+static char not_defined[128];
+/* #else */
+static const char else_defined[] = "#else\n";
+/* #endif xyzzy */
+static char end_defined[128];
+
+
+/* Apply a set of diffs as appropriate. */
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0, hunk, failed, i, fd;
+ bool patch_seen;
+ LINENUM where = 0, newwhere, fuzz, mymaxfuzz;
+ const char *tmpdir;
+ char *v;
+
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+ for (i = 0; i < MAXFILEC; i++)
+ filearg[i] = NULL;
+
+ buf_size = INITLINELEN;
+ buf = malloc((unsigned)(buf_size));
+ if (buf == NULL)
+ fatal("out of memory\n");
+
+ /* Cons up the names of the temporary files. */
+ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
+ tmpdir = _PATH_TMP;
+ for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
+ ;
+ i++;
+ if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
+ fatal("cannot allocate memory");
+ if ((fd = mkstemp(TMPOUTNAME)) < 0)
+ pfatal("can't create %s", TMPOUTNAME);
+ close(fd);
+
+ if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
+ fatal("cannot allocate memory");
+ if ((fd = mkstemp(TMPINNAME)) < 0)
+ pfatal("can't create %s", TMPINNAME);
+ close(fd);
+
+ if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
+ fatal("cannot allocate memory");
+ if ((fd = mkstemp(TMPREJNAME)) < 0)
+ pfatal("can't create %s", TMPREJNAME);
+ close(fd);
+
+ if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
+ fatal("cannot allocate memory");
+ if ((fd = mkstemp(TMPPATNAME)) < 0)
+ pfatal("can't create %s", TMPPATNAME);
+ close(fd);
+
+ v = getenv("SIMPLE_BACKUP_SUFFIX");
+ if (v)
+ simple_backup_suffix = v;
+ else
+ simple_backup_suffix = ORIGEXT;
+
+ /* parse switches */
+ Argc = argc;
+ Argv = argv;
+ get_some_switches();
+
+ if (backup_type == none) {
+ if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
+ v = getenv("VERSION_CONTROL");
+ if (v != NULL || !posix)
+ backup_type = get_version(v); /* OK to pass NULL. */
+ }
+
+ /* make sure we clean up /tmp in case of disaster */
+ set_signals(0);
+
+ patch_seen = false;
+ for (open_patch_file(filearg[1]); there_is_another_patch();
+ reinitialize_almost_everything()) {
+ /* for each patch in patch file */
+
+ patch_seen = true;
+
+ warn_on_invalid_line = true;
+
+ if (outname == NULL)
+ outname = savestr(filearg[0]);
+
+ /* for ed script just up and do it and exit */
+ if (diff_type == ED_DIFF) {
+ do_ed_script();
+ continue;
+ }
+ /* initialize the patched file */
+ if (!skip_rest_of_patch)
+ init_output(TMPOUTNAME);
+
+ /* initialize reject file */
+ init_reject(TMPREJNAME);
+
+ /* find out where all the lines are */
+ if (!skip_rest_of_patch)
+ scan_input(filearg[0]);
+
+ /* from here on, open no standard i/o files, because malloc */
+ /* might misfire and we can't catch it easily */
+
+ /* apply each hunk of patch */
+ hunk = 0;
+ failed = 0;
+ out_of_mem = false;
+ while (another_hunk()) {
+ hunk++;
+ fuzz = 0;
+ mymaxfuzz = pch_context();
+ if (maxfuzz < mymaxfuzz)
+ mymaxfuzz = maxfuzz;
+ if (!skip_rest_of_patch) {
+ do {
+ where = locate_hunk(fuzz);
+ if (hunk == 1 && where == 0 && !force) {
+ /* dwim for reversed patch? */
+ if (!pch_swap()) {
+ if (fuzz == 0)
+ say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
+ continue;
+ }
+ reverse = !reverse;
+ /* try again */
+ where = locate_hunk(fuzz);
+ if (where == 0) {
+ /* didn't find it swapped */
+ if (!pch_swap())
+ /* put it back to normal */
+ fatal("lost hunk on alloc error!\n");
+ reverse = !reverse;
+ } else if (noreverse) {
+ if (!pch_swap())
+ /* put it back to normal */
+ fatal("lost hunk on alloc error!\n");
+ reverse = !reverse;
+ say("Ignoring previously applied (or reversed) patch.\n");
+ skip_rest_of_patch = true;
+ } else if (batch) {
+ if (verbose)
+ say("%seversed (or previously applied) patch detected! %s -R.",
+ reverse ? "R" : "Unr",
+ reverse ? "Assuming" : "Ignoring");
+ } else {
+ ask("%seversed (or previously applied) patch detected! %s -R? [y] ",
+ reverse ? "R" : "Unr",
+ reverse ? "Assume" : "Ignore");
+ if (*buf == 'n') {
+ ask("Apply anyway? [n] ");
+ if (*buf != 'y')
+ skip_rest_of_patch = true;
+ where = 0;
+ reverse = !reverse;
+ if (!pch_swap())
+ /* put it back to normal */
+ fatal("lost hunk on alloc error!\n");
+ }
+ }
+ }
+ } while (!skip_rest_of_patch && where == 0 &&
+ ++fuzz <= mymaxfuzz);
+
+ if (skip_rest_of_patch) { /* just got decided */
+ if (ferror(ofp) || fclose(ofp)) {
+ say("Error writing %s\n",
+ TMPOUTNAME);
+ error = 1;
+ }
+ ofp = NULL;
+ }
+ }
+ newwhere = pch_newfirst() + last_offset;
+ if (skip_rest_of_patch) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say("Hunk #%d ignored at %ld.\n",
+ hunk, newwhere);
+ } else if (where == 0) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say("Hunk #%d failed at %ld.\n",
+ hunk, newwhere);
+ } else {
+ apply_hunk(where);
+ if (verbose) {
+ say("Hunk #%d succeeded at %ld",
+ hunk, newwhere);
+ if (fuzz != 0)
+ say(" with fuzz %ld", fuzz);
+ if (last_offset)
+ say(" (offset %ld line%s)",
+ last_offset,
+ last_offset == 1L ? "" : "s");
+ say(".\n");
+ }
+ }
+ }
+
+ if (out_of_mem && using_plan_a) {
+ Argc = Argc_last;
+ Argv = Argv_last;
+ say("\n\nRan out of memory using Plan A--trying again...\n\n");
+ if (ofp)
+ fclose(ofp);
+ ofp = NULL;
+ if (rejfp)
+ fclose(rejfp);
+ rejfp = NULL;
+ continue;
+ }
+ if (hunk == 0)
+ fatal("Internal error: hunk should not be 0\n");
+
+ /* finish spewing out the new file */
+ if (!skip_rest_of_patch && !spew_output()) {
+ say("Can't write %s\n", TMPOUTNAME);
+ error = 1;
+ }
+
+ /* and put the output where desired */
+ ignore_signals();
+ if (!skip_rest_of_patch) {
+ struct stat statbuf;
+ char *realout = outname;
+
+ if (!check_only) {
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = true;
+ realout = TMPOUTNAME;
+ chmod(TMPOUTNAME, filemode);
+ } else
+ chmod(outname, filemode);
+
+ if (remove_empty_files &&
+ stat(realout, &statbuf) == 0 &&
+ statbuf.st_size == 0) {
+ if (verbose)
+ say("Removing %s (empty after patching).\n",
+ realout);
+ unlink(realout);
+ }
+ }
+ }
+ if (ferror(rejfp) || fclose(rejfp)) {
+ say("Error writing %s\n", rejname);
+ error = 1;
+ }
+ rejfp = NULL;
+ if (failed) {
+ error = 1;
+ if (*rejname == '\0') {
+ if (strlcpy(rejname, outname,
+ sizeof(rejname)) >= sizeof(rejname))
+ fatal("filename %s is too long\n", outname);
+ if (strlcat(rejname, REJEXT,
+ sizeof(rejname)) >= sizeof(rejname))
+ fatal("filename %s is too long\n", outname);
+ }
+ if (!check_only)
+ say("%d out of %d hunks %s--saving rejects to %s\n",
+ failed, hunk, skip_rest_of_patch ? "ignored" : "failed", rejname);
+ else
+ say("%d out of %d hunks %s\n",
+ failed, hunk, skip_rest_of_patch ? "ignored" : "failed");
+ if (!check_only && move_file(TMPREJNAME, rejname) < 0)
+ trejkeep = true;
+ }
+ set_signals(1);
+ }
+
+ if (!patch_seen)
+ error = 2;
+
+ my_exit(error);
+ /* NOTREACHED */
+}
+
+/* Prepare to find the next patch to do in the patch file. */
+
+static void
+reinitialize_almost_everything(void)
+{
+ re_patch();
+ re_input();
+
+ input_lines = 0;
+ last_frozen_line = 0;
+
+ filec = 0;
+ if (!out_of_mem) {
+ free(filearg[0]);
+ filearg[0] = NULL;
+ }
+
+ free(outname);
+ outname = NULL;
+
+ last_offset = 0;
+ diff_type = 0;
+
+ free(revision);
+ revision = NULL;
+
+ reverse = reverse_flag_specified;
+ skip_rest_of_patch = false;
+
+ get_some_switches();
+}
+
+/* Process switches and filenames. */
+
+static void
+get_some_switches(void)
+{
+ const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
+ static struct option longopts[] = {
+ {"backup", no_argument, 0, 'b'},
+ {"batch", no_argument, 0, 't'},
+ {"check", no_argument, 0, 'C'},
+ {"context", no_argument, 0, 'c'},
+ {"debug", required_argument, 0, 'x'},
+ {"directory", required_argument, 0, 'd'},
+ {"ed", no_argument, 0, 'e'},
+ {"force", no_argument, 0, 'f'},
+ {"forward", no_argument, 0, 'N'},
+ {"fuzz", required_argument, 0, 'F'},
+ {"ifdef", required_argument, 0, 'D'},
+ {"input", required_argument, 0, 'i'},
+ {"ignore-whitespace", no_argument, 0, 'l'},
+ {"normal", no_argument, 0, 'n'},
+ {"output", required_argument, 0, 'o'},
+ {"prefix", required_argument, 0, 'B'},
+ {"quiet", no_argument, 0, 's'},
+ {"reject-file", required_argument, 0, 'r'},
+ {"remove-empty-files", no_argument, 0, 'E'},
+ {"reverse", no_argument, 0, 'R'},
+ {"silent", no_argument, 0, 's'},
+ {"strip", required_argument, 0, 'p'},
+ {"suffix", required_argument, 0, 'z'},
+ {"unified", no_argument, 0, 'u'},
+ {"version", no_argument, 0, 'v'},
+ {"version-control", required_argument, 0, 'V'},
+ {"posix", no_argument, &posix, 1},
+ {NULL, 0, 0, 0}
+ };
+ int ch;
+
+ rejname[0] = '\0';
+ Argc_last = Argc;
+ Argv_last = Argv;
+ if (!Argc)
+ return;
+ optreset = optind = 1;
+ while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
+ switch (ch) {
+ case 'b':
+ if (backup_type == none)
+ backup_type = numbered_existing;
+ if (optarg == NULL)
+ break;
+ if (verbose)
+ say("Warning, the ``-b suffix'' option has been"
+ " obsoleted by the -z option.\n");
+ /* FALLTHROUGH */
+ case 'z':
+ /* must directly follow 'b' case for backwards compat */
+ simple_backup_suffix = savestr(optarg);
+ break;
+ case 'B':
+ origprae = savestr(optarg);
+ break;
+ case 'c':
+ diff_type = CONTEXT_DIFF;
+ break;
+ case 'C':
+ check_only = true;
+ break;
+ case 'd':
+ if (chdir(optarg) < 0)
+ pfatal("can't cd to %s", optarg);
+ break;
+ case 'D':
+ do_defines = true;
+ if (!isalpha((unsigned char)*optarg) && *optarg != '_')
+ fatal("argument to -D is not an identifier\n");
+ snprintf(if_defined, sizeof if_defined,
+ "#ifdef %s\n", optarg);
+ snprintf(not_defined, sizeof not_defined,
+ "#ifndef %s\n", optarg);
+ snprintf(end_defined, sizeof end_defined,
+ "#endif /* %s */\n", optarg);
+ break;
+ case 'e':
+ diff_type = ED_DIFF;
+ break;
+ case 'E':
+ remove_empty_files = true;
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'F':
+ maxfuzz = atoi(optarg);
+ break;
+ case 'i':
+ if (++filec == MAXFILEC)
+ fatal("too many file arguments\n");
+ filearg[filec] = savestr(optarg);
+ break;
+ case 'l':
+ canonicalize = true;
+ break;
+ case 'n':
+ diff_type = NORMAL_DIFF;
+ break;
+ case 'N':
+ noreverse = true;
+ break;
+ case 'o':
+ outname = savestr(optarg);
+ break;
+ case 'p':
+ strippath = atoi(optarg);
+ break;
+ case 'r':
+ if (strlcpy(rejname, optarg,
+ sizeof(rejname)) >= sizeof(rejname))
+ fatal("argument for -r is too long\n");
+ break;
+ case 'R':
+ reverse = true;
+ reverse_flag_specified = true;
+ break;
+ case 's':
+ verbose = false;
+ break;
+ case 't':
+ batch = true;
+ break;
+ case 'u':
+ diff_type = UNI_DIFF;
+ break;
+ case 'v':
+ version();
+ break;
+ case 'V':
+ backup_type = get_version(optarg);
+ break;
+#ifdef DEBUGGING
+ case 'x':
+ debug = atoi(optarg);
+ break;
+#endif
+ default:
+ if (ch != '\0')
+ usage();
+ break;
+ }
+ }
+ Argc -= optind;
+ Argv += optind;
+
+ if (Argc > 0) {
+ filearg[0] = savestr(*Argv++);
+ Argc--;
+ while (Argc > 0) {
+ if (++filec == MAXFILEC)
+ fatal("too many file arguments\n");
+ filearg[filec] = savestr(*Argv++);
+ Argc--;
+ }
+ }
+
+ if (getenv("POSIXLY_CORRECT") != NULL)
+ posix = 1;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+"usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n"
+" [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
+" [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n"
+" [--posix] [origfile [patchfile]]\n"
+" patch <patchfile\n");
+ my_exit(EXIT_SUCCESS);
+}
+
+/*
+ * Attempt to find the right place to apply this hunk of patch.
+ */
+static LINENUM
+locate_hunk(LINENUM fuzz)
+{
+ LINENUM first_guess = pch_first() + last_offset;
+ LINENUM offset;
+ LINENUM pat_lines = pch_ptrn_lines();
+ LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1;
+ LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
+
+ if (pat_lines == 0) { /* null range matches always */
+ if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
+ || diff_type == NEW_CONTEXT_DIFF
+ || diff_type == UNI_DIFF)) {
+ say("Empty context always matches.\n");
+ }
+ return (first_guess);
+ }
+ if (max_neg_offset >= first_guess) /* do not try lines < 0 */
+ max_neg_offset = first_guess - 1;
+ if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
+ return first_guess;
+ for (offset = 1; ; offset++) {
+ bool check_after = (offset <= max_pos_offset);
+ bool check_before = (offset <= max_neg_offset);
+
+ if (check_after && patch_match(first_guess, offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say("Offset changing from %ld to %ld\n",
+ last_offset, offset);
+#endif
+ last_offset = offset;
+ return first_guess + offset;
+ } else if (check_before && patch_match(first_guess, -offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say("Offset changing from %ld to %ld\n",
+ last_offset, -offset);
+#endif
+ last_offset = -offset;
+ return first_guess - offset;
+ } else if (!check_before && !check_after)
+ return 0;
+ }
+}
+
+/* We did not find the pattern, dump out the hunk so they can handle it. */
+
+static void
+abort_context_hunk(void)
+{
+ LINENUM i;
+ const LINENUM pat_end = pch_end();
+ /*
+ * add in last_offset to guess the same as the previous successful
+ * hunk
+ */
+ const LINENUM oldfirst = pch_first() + last_offset;
+ const LINENUM newfirst = pch_newfirst() + last_offset;
+ const LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
+ const LINENUM newlast = newfirst + pch_repl_lines() - 1;
+ const char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
+ const char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
+
+ fprintf(rejfp, "***************\n");
+ for (i = 0; i <= pat_end; i++) {
+ switch (pch_char(i)) {
+ case '*':
+ if (oldlast < oldfirst)
+ fprintf(rejfp, "*** 0%s\n", stars);
+ else if (oldlast == oldfirst)
+ fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
+ else
+ fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
+ oldlast, stars);
+ break;
+ case '=':
+ if (newlast < newfirst)
+ fprintf(rejfp, "--- 0%s\n", minuses);
+ else if (newlast == newfirst)
+ fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
+ else
+ fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
+ newlast, minuses);
+ break;
+ case '\n':
+ fprintf(rejfp, "%s", pfetch(i));
+ break;
+ case ' ':
+ case '-':
+ case '+':
+ case '!':
+ fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
+ break;
+ default:
+ fatal("fatal internal error in abort_context_hunk\n");
+ }
+ }
+}
+
+static void
+rej_line(int ch, LINENUM i)
+{
+ size_t len;
+ const char *line = pfetch(i);
+
+ len = strlen(line);
+
+ fprintf(rejfp, "%c%s", ch, line);
+ if (len == 0 || line[len-1] != '\n')
+ fprintf(rejfp, "\n\\ No newline at end of file\n");
+}
+
+static void
+abort_hunk(void)
+{
+ LINENUM i, j, split;
+ int ch1, ch2;
+ const LINENUM pat_end = pch_end();
+ const LINENUM oldfirst = pch_first() + last_offset;
+ const LINENUM newfirst = pch_newfirst() + last_offset;
+
+ if (diff_type != UNI_DIFF) {
+ abort_context_hunk();
+ return;
+ }
+ split = -1;
+ for (i = 0; i <= pat_end; i++) {
+ if (pch_char(i) == '=') {
+ split = i;
+ break;
+ }
+ }
+ if (split == -1) {
+ fprintf(rejfp, "malformed hunk: no split found\n");
+ return;
+ }
+ i = 0;
+ j = split + 1;
+ fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
+ pch_ptrn_lines() ? oldfirst : 0,
+ pch_ptrn_lines(), newfirst, pch_repl_lines());
+ while (i < split || j <= pat_end) {
+ ch1 = i < split ? pch_char(i) : -1;
+ ch2 = j <= pat_end ? pch_char(j) : -1;
+ if (ch1 == '-') {
+ rej_line('-', i);
+ i++;
+ } else if (ch1 == ' ' && ch2 == ' ') {
+ rej_line(' ', i);
+ i++;
+ j++;
+ } else if (ch1 == '!' && ch2 == '!') {
+ while (i < split && ch1 == '!') {
+ rej_line('-', i);
+ i++;
+ ch1 = i < split ? pch_char(i) : -1;
+ }
+ while (j <= pat_end && ch2 == '!') {
+ rej_line('+', j);
+ j++;
+ ch2 = j <= pat_end ? pch_char(j) : -1;
+ }
+ } else if (ch1 == '*') {
+ i++;
+ } else if (ch2 == '+' || ch2 == ' ') {
+ rej_line(ch2, j);
+ j++;
+ } else {
+ fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
+ i, split, j);
+ rej_line(ch1, i);
+ rej_line(ch2, j);
+ return;
+ }
+ }
+}
+
+/* We found where to apply it (we hope), so do it. */
+
+static void
+apply_hunk(LINENUM where)
+{
+ LINENUM old = 1;
+ const LINENUM lastline = pch_ptrn_lines();
+ LINENUM new = lastline + 1;
+#define OUTSIDE 0
+#define IN_IFNDEF 1
+#define IN_IFDEF 2
+#define IN_ELSE 3
+ int def_state = OUTSIDE;
+ const LINENUM pat_end = pch_end();
+
+ where--;
+ while (pch_char(new) == '=' || pch_char(new) == '\n')
+ new++;
+
+ while (old <= lastline) {
+ if (pch_char(old) == '-') {
+ copy_till(where + old - 1, false);
+ if (do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ } else if (def_state == IN_IFDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ } else if (new > pat_end) {
+ break;
+ } else if (pch_char(new) == '+') {
+ copy_till(where + old - 1, false);
+ if (do_defines) {
+ if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ } else if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ }
+ }
+ fputs(pfetch(new), ofp);
+ new++;
+ } else if (pch_char(new) != pch_char(old)) {
+ say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
+ pch_hunk_beg() + old,
+ pch_hunk_beg() + new);
+#ifdef DEBUGGING
+ say("oldchar = '%c', newchar = '%c'\n",
+ pch_char(old), pch_char(new));
+#endif
+ my_exit(2);
+ } else if (pch_char(new) == '!') {
+ copy_till(where + old - 1, false);
+ if (do_defines) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ }
+ while (pch_char(old) == '!') {
+ if (do_defines) {
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ }
+ if (do_defines) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ while (pch_char(new) == '!') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ } else {
+ if (pch_char(new) != ' ')
+ fatal("Internal error: expected ' '\n");
+ old++;
+ new++;
+ if (do_defines && def_state != OUTSIDE) {
+ fputs(end_defined, ofp);
+ def_state = OUTSIDE;
+ }
+ }
+ }
+ if (new <= pat_end && pch_char(new) == '+') {
+ copy_till(where + old - 1, false);
+ if (do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ } else if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ }
+ while (new <= pat_end && pch_char(new) == '+') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ }
+ if (do_defines && def_state != OUTSIDE) {
+ fputs(end_defined, ofp);
+ }
+}
+
+/*
+ * Open the new file.
+ */
+static void
+init_output(const char *name)
+{
+ ofp = fopen(name, "w");
+ if (ofp == NULL)
+ pfatal("can't create %s", name);
+}
+
+/*
+ * Open a file to put hunks we can't locate.
+ */
+static void
+init_reject(const char *name)
+{
+ rejfp = fopen(name, "w");
+ if (rejfp == NULL)
+ pfatal("can't create %s", name);
+}
+
+/*
+ * Copy input file to output, up to wherever hunk is to be applied.
+ * If endoffile is true, treat the last line specially since it may
+ * lack a newline.
+ */
+static void
+copy_till(LINENUM lastline, bool endoffile)
+{
+ if (last_frozen_line > lastline)
+ fatal("misordered hunks! output would be garbled\n");
+ while (last_frozen_line < lastline) {
+ if (++last_frozen_line == lastline && endoffile)
+ dump_line(last_frozen_line, !last_line_missing_eol);
+ else
+ dump_line(last_frozen_line, true);
+ }
+}
+
+/*
+ * Finish copying the input file to the output file.
+ */
+static bool
+spew_output(void)
+{
+ int rv;
+
+#ifdef DEBUGGING
+ if (debug & 256)
+ say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
+#endif
+ if (input_lines)
+ copy_till(input_lines, true); /* dump remainder of file */
+ rv = ferror(ofp) == 0 && fclose(ofp) == 0;
+ ofp = NULL;
+ return rv;
+}
+
+/*
+ * Copy one line from input to output.
+ */
+static void
+dump_line(LINENUM line, bool write_newline)
+{
+ char *s;
+
+ s = ifetch(line, 0);
+ if (s == NULL)
+ return;
+ /* Note: string is not NUL terminated. */
+ for (; *s != '\n'; s++)
+ putc(*s, ofp);
+ if (write_newline)
+ putc('\n', ofp);
+}
+
+/*
+ * Does the patch pattern match at line base+offset?
+ */
+static bool
+patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
+{
+ LINENUM pline = 1 + fuzz;
+ LINENUM iline;
+ LINENUM pat_lines = pch_ptrn_lines() - fuzz;
+ const char *ilineptr;
+ const char *plineptr;
+ short plinelen;
+
+ for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
+ ilineptr = ifetch(iline, offset >= 0);
+ if (ilineptr == NULL)
+ return false;
+ plineptr = pfetch(pline);
+ plinelen = pch_line_len(pline);
+ if (canonicalize) {
+ if (!similar(ilineptr, plineptr, plinelen))
+ return false;
+ } else if (strnNE(ilineptr, plineptr, plinelen))
+ return false;
+ if (iline == input_lines) {
+ /*
+ * We are looking at the last line of the file.
+ * If the file has no eol, the patch line should
+ * not have one either and vice-versa. Note that
+ * plinelen > 0.
+ */
+ if (last_line_missing_eol) {
+ if (plineptr[plinelen - 1] == '\n')
+ return false;
+ } else {
+ if (plineptr[plinelen - 1] != '\n')
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * Do two lines match with canonicalized white space?
+ */
+static bool
+similar(const char *a, const char *b, int len)
+{
+ while (len) {
+ if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */
+ if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */
+ return false;
+ while (len && isspace((unsigned char)*b) && *b != '\n')
+ b++, len--; /* skip pattern whitespace */
+ while (isspace((unsigned char)*a) && *a != '\n')
+ a++; /* skip target whitespace */
+ if (*a == '\n' || *b == '\n')
+ return (*a == *b); /* should end in sync */
+ } else if (*a++ != *b++) /* match non-whitespace chars */
+ return false;
+ else
+ len--; /* probably not necessary */
+ }
+ return true; /* actually, this is not reached */
+ /* since there is always a \n */
+}
diff --git a/usr.bin/patch/pathnames.h b/usr.bin/patch/pathnames.h
new file mode 100644
index 0000000..d31300e
--- /dev/null
+++ b/usr.bin/patch/pathnames.h
@@ -0,0 +1,12 @@
+/*-
+ * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
+ * on July 29, 2003.
+ *
+ * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
+ * $FreeBSD$
+ */
+
+
+#include <paths.h>
+
+#define _PATH_ED "/bin/ed"
diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c
new file mode 100644
index 0000000..4c06bbf
--- /dev/null
+++ b/usr.bin/patch/pch.c
@@ -0,0 +1,1593 @@
+
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: pch.c,v 1.39 2012/04/11 08:07:13 ajacoutot Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "pathnames.h"
+
+/* Patch (diff listing) abstract type. */
+
+static long p_filesize; /* size of the patch file */
+static LINENUM p_first; /* 1st line number */
+static LINENUM p_newfirst; /* 1st line number of replacement */
+static LINENUM p_ptrn_lines; /* # lines in pattern */
+static LINENUM p_repl_lines; /* # lines in replacement text */
+static LINENUM p_end = -1; /* last line in hunk */
+static LINENUM p_max; /* max allowed value of p_end */
+static LINENUM p_context = 3; /* # of context lines */
+static LINENUM p_input_line = 0; /* current line # from patch file */
+static char **p_line = NULL;/* the text of the hunk */
+static short *p_len = NULL; /* length of each line */
+static char *p_char = NULL; /* +, -, and ! */
+static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */
+static int p_indent; /* indent to patch */
+static LINENUM p_base; /* where to intuit this time */
+static LINENUM p_bline; /* line # of p_base */
+static LINENUM p_start; /* where intuit found a patch */
+static LINENUM p_sline; /* and the line number for it */
+static LINENUM p_hunk_beg; /* line number of current hunk */
+static LINENUM p_efake = -1; /* end of faked up lines--don't free */
+static LINENUM p_bfake = -1; /* beg of faked up lines */
+static FILE *pfp = NULL; /* patch file pointer */
+static char *bestguess = NULL; /* guess at correct filename */
+
+static void grow_hunkmax(void);
+static int intuit_diff_type(void);
+static void next_intuit_at(LINENUM, LINENUM);
+static void skip_to(LINENUM, LINENUM);
+static size_t pgets(bool _do_indent);
+static char *best_name(const struct file_name *, bool);
+static char *posix_name(const struct file_name *, bool);
+static size_t num_components(const char *);
+
+/*
+ * Prepare to look for the next patch in the patch file.
+ */
+void
+re_patch(void)
+{
+ p_first = 0;
+ p_newfirst = 0;
+ p_ptrn_lines = 0;
+ p_repl_lines = 0;
+ p_end = (LINENUM) - 1;
+ p_max = 0;
+ p_indent = 0;
+}
+
+/*
+ * Open the patch file at the beginning of time.
+ */
+void
+open_patch_file(const char *filename)
+{
+ struct stat filestat;
+
+ if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
+ pfp = fopen(TMPPATNAME, "w");
+ if (pfp == NULL)
+ pfatal("can't create %s", TMPPATNAME);
+ while (fgets(buf, buf_size, stdin) != NULL)
+ fputs(buf, pfp);
+ if (ferror(pfp) || fclose(pfp))
+ pfatal("can't write %s", TMPPATNAME);
+ filename = TMPPATNAME;
+ }
+ pfp = fopen(filename, "r");
+ if (pfp == NULL)
+ pfatal("patch file %s not found", filename);
+ fstat(fileno(pfp), &filestat);
+ p_filesize = filestat.st_size;
+ next_intuit_at(0L, 1L); /* start at the beginning */
+ set_hunkmax();
+}
+
+/*
+ * Make sure our dynamically realloced tables are malloced to begin with.
+ */
+void
+set_hunkmax(void)
+{
+ if (p_line == NULL)
+ p_line = calloc((size_t) hunkmax, sizeof(char *));
+ if (p_len == NULL)
+ p_len = calloc((size_t) hunkmax, sizeof(short));
+ if (p_char == NULL)
+ p_char = calloc((size_t) hunkmax, sizeof(char));
+}
+
+/*
+ * Enlarge the arrays containing the current hunk of patch.
+ */
+static void
+grow_hunkmax(void)
+{
+ int new_hunkmax;
+ char **new_p_line;
+ short *new_p_len;
+ char *new_p_char;
+
+ new_hunkmax = hunkmax * 2;
+
+ if (p_line == NULL || p_len == NULL || p_char == NULL)
+ fatal("Internal memory allocation error\n");
+
+ new_p_line = realloc(p_line, new_hunkmax * sizeof(char *));
+ if (new_p_line == NULL)
+ free(p_line);
+
+ new_p_len = realloc(p_len, new_hunkmax * sizeof(short));
+ if (new_p_len == NULL)
+ free(p_len);
+
+ new_p_char = realloc(p_char, new_hunkmax * sizeof(char));
+ if (new_p_char == NULL)
+ free(p_char);
+
+ p_char = new_p_char;
+ p_len = new_p_len;
+ p_line = new_p_line;
+
+ if (p_line != NULL && p_len != NULL && p_char != NULL) {
+ hunkmax = new_hunkmax;
+ return;
+ }
+
+ if (!using_plan_a)
+ fatal("out of memory\n");
+ out_of_mem = true; /* whatever is null will be allocated again */
+ /* from within plan_a(), of all places */
+}
+
+/* True if the remainder of the patch file contains a diff of some sort. */
+
+bool
+there_is_another_patch(void)
+{
+ bool exists = false;
+
+ if (p_base != 0L && p_base >= p_filesize) {
+ if (verbose)
+ say("done\n");
+ return false;
+ }
+ if (verbose)
+ say("Hmm...");
+ diff_type = intuit_diff_type();
+ if (!diff_type) {
+ if (p_base != 0L) {
+ if (verbose)
+ say(" Ignoring the trailing garbage.\ndone\n");
+ } else
+ say(" I can't seem to find a patch in there anywhere.\n");
+ return false;
+ }
+ if (verbose)
+ say(" %sooks like %s to me...\n",
+ (p_base == 0L ? "L" : "The next patch l"),
+ diff_type == UNI_DIFF ? "a unified diff" :
+ diff_type == CONTEXT_DIFF ? "a context diff" :
+ diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
+ diff_type == NORMAL_DIFF ? "a normal diff" :
+ "an ed script");
+ if (p_indent && verbose)
+ say("(Patch is indented %d space%s.)\n", p_indent,
+ p_indent == 1 ? "" : "s");
+ skip_to(p_start, p_sline);
+ while (filearg[0] == NULL) {
+ if (force || batch) {
+ say("No file to patch. Skipping...\n");
+ filearg[0] = savestr(bestguess);
+ skip_rest_of_patch = true;
+ return true;
+ }
+ ask("File to patch: ");
+ if (*buf != '\n') {
+ free(bestguess);
+ bestguess = savestr(buf);
+ filearg[0] = fetchname(buf, &exists, 0);
+ }
+ if (!exists) {
+ ask("No file found--skip this patch? [n] ");
+ if (*buf != 'y')
+ continue;
+ if (verbose)
+ say("Skipping patch...\n");
+ free(filearg[0]);
+ filearg[0] = fetchname(bestguess, &exists, 0);
+ skip_rest_of_patch = true;
+ return true;
+ }
+ }
+ return true;
+}
+
+static void
+p4_fetchname(struct file_name *name, char *str)
+{
+ char *t, *h;
+
+ /* Skip leading whitespace. */
+ while (isspace((unsigned char)*str))
+ str++;
+
+ /* Remove the file revision number. */
+ for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++)
+ if (*t == '#')
+ h = t;
+ if (h != NULL)
+ *h = '\0';
+
+ name->path = fetchname(str, &name->exists, strippath);
+}
+
+/* Determine what kind of diff is in the remaining part of the patch file. */
+
+static int
+intuit_diff_type(void)
+{
+ long this_line = 0, previous_line;
+ long first_command_line = -1;
+ LINENUM fcl_line = -1;
+ bool last_line_was_command = false, this_is_a_command = false;
+ bool stars_last_line = false, stars_this_line = false;
+ char *s, *t;
+ int indent, retval;
+ struct file_name names[MAX_FILE];
+
+ memset(names, 0, sizeof(names));
+ ok_to_create_file = false;
+ fseek(pfp, p_base, SEEK_SET);
+ p_input_line = p_bline - 1;
+ for (;;) {
+ previous_line = this_line;
+ last_line_was_command = this_is_a_command;
+ stars_last_line = stars_this_line;
+ this_line = ftell(pfp);
+ indent = 0;
+ p_input_line++;
+ if (pgets(false) == 0) {
+ if (first_command_line >= 0L) {
+ /* nothing but deletes!? */
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ } else {
+ p_start = this_line;
+ p_sline = p_input_line;
+ retval = 0;
+ goto scan_exit;
+ }
+ }
+ for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
+ if (*s == '\t')
+ indent += 8 - (indent % 8);
+ else
+ indent++;
+ }
+ for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
+ ;
+ this_is_a_command = (isdigit((unsigned char)*s) &&
+ (*t == 'd' || *t == 'c' || *t == 'a'));
+ if (first_command_line < 0L && this_is_a_command) {
+ first_command_line = this_line;
+ fcl_line = p_input_line;
+ p_indent = indent; /* assume this for now */
+ }
+ if (!stars_last_line && strnEQ(s, "*** ", 4))
+ names[OLD_FILE].path = fetchname(s + 4,
+ &names[OLD_FILE].exists, strippath);
+ else if (strnEQ(s, "--- ", 4))
+ names[NEW_FILE].path = fetchname(s + 4,
+ &names[NEW_FILE].exists, strippath);
+ else if (strnEQ(s, "+++ ", 4))
+ /* pretend it is the old name */
+ names[OLD_FILE].path = fetchname(s + 4,
+ &names[OLD_FILE].exists, strippath);
+ else if (strnEQ(s, "Index:", 6))
+ names[INDEX_FILE].path = fetchname(s + 6,
+ &names[INDEX_FILE].exists, strippath);
+ else if (strnEQ(s, "Prereq:", 7)) {
+ for (t = s + 7; isspace((unsigned char)*t); t++)
+ ;
+ revision = savestr(t);
+ for (t = revision; *t && !isspace((unsigned char)*t); t++)
+ ;
+ *t = '\0';
+ if (*revision == '\0') {
+ free(revision);
+ revision = NULL;
+ }
+ } else if (strnEQ(s, "==== ", 5)) {
+ /* Perforce-style diffs. */
+ if ((t = strstr(s + 5, " - ")) != NULL)
+ p4_fetchname(&names[NEW_FILE], t + 3);
+ p4_fetchname(&names[OLD_FILE], s + 5);
+ }
+ if ((!diff_type || diff_type == ED_DIFF) &&
+ first_command_line >= 0L &&
+ strEQ(s, ".\n")) {
+ p_indent = indent;
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ }
+ if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
+ if (strnEQ(s + 4, "0,0", 3))
+ ok_to_create_file = true;
+ p_indent = indent;
+ p_start = this_line;
+ p_sline = p_input_line;
+ retval = UNI_DIFF;
+ goto scan_exit;
+ }
+ stars_this_line = strnEQ(s, "********", 8);
+ if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
+ strnEQ(s, "*** ", 4)) {
+ if (atol(s + 4) == 0)
+ ok_to_create_file = true;
+ /*
+ * If this is a new context diff the character just
+ * before the newline is a '*'.
+ */
+ while (*s != '\n')
+ s++;
+ p_indent = indent;
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
+ goto scan_exit;
+ }
+ if ((!diff_type || diff_type == NORMAL_DIFF) &&
+ last_line_was_command &&
+ (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ p_indent = indent;
+ retval = NORMAL_DIFF;
+ goto scan_exit;
+ }
+ }
+scan_exit:
+ if (retval == UNI_DIFF) {
+ /* unswap old and new */
+ struct file_name tmp = names[OLD_FILE];
+ names[OLD_FILE] = names[NEW_FILE];
+ names[NEW_FILE] = tmp;
+ }
+ if (filearg[0] == NULL) {
+ if (posix)
+ filearg[0] = posix_name(names, ok_to_create_file);
+ else {
+ /* Ignore the Index: name for context diffs, like GNU */
+ if (names[OLD_FILE].path != NULL ||
+ names[NEW_FILE].path != NULL) {
+ free(names[INDEX_FILE].path);
+ names[INDEX_FILE].path = NULL;
+ }
+ filearg[0] = best_name(names, ok_to_create_file);
+ }
+ }
+
+ free(bestguess);
+ bestguess = NULL;
+ if (filearg[0] != NULL)
+ bestguess = savestr(filearg[0]);
+ else if (!ok_to_create_file) {
+ /*
+ * We don't want to create a new file but we need a
+ * filename to set bestguess. Avoid setting filearg[0]
+ * so the file is not created automatically.
+ */
+ if (posix)
+ bestguess = posix_name(names, true);
+ else
+ bestguess = best_name(names, true);
+ }
+ free(names[OLD_FILE].path);
+ free(names[NEW_FILE].path);
+ free(names[INDEX_FILE].path);
+ return retval;
+}
+
+/*
+ * Remember where this patch ends so we know where to start up again.
+ */
+static void
+next_intuit_at(LINENUM file_pos, LINENUM file_line)
+{
+ p_base = file_pos;
+ p_bline = file_line;
+}
+
+/*
+ * Basically a verbose fseek() to the actual diff listing.
+ */
+static void
+skip_to(LINENUM file_pos, LINENUM file_line)
+{
+ size_t len;
+
+ if (p_base > file_pos)
+ fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
+ if (verbose && p_base < file_pos) {
+ fseek(pfp, p_base, SEEK_SET);
+ say("The text leading up to this was:\n--------------------------\n");
+ while (ftell(pfp) < file_pos) {
+ len = pgets(false);
+ if (len == 0)
+ fatal("Unexpected end of file\n");
+ say("|%s", buf);
+ }
+ say("--------------------------\n");
+ } else
+ fseek(pfp, file_pos, SEEK_SET);
+ p_input_line = file_line - 1;
+}
+
+/* Make this a function for better debugging. */
+static void
+malformed(void)
+{
+ fatal("malformed patch at line %ld: %s", p_input_line, buf);
+ /* about as informative as "Syntax error" in C */
+}
+
+/*
+ * True if the line has been discarded (i.e. it is a line saying
+ * "\ No newline at end of file".)
+ */
+static bool
+remove_special_line(void)
+{
+ int c;
+
+ c = fgetc(pfp);
+ if (c == '\\') {
+ do {
+ c = fgetc(pfp);
+ } while (c != EOF && c != '\n');
+
+ return true;
+ }
+ if (c != EOF)
+ fseek(pfp, -1L, SEEK_CUR);
+
+ return false;
+}
+
+/*
+ * True if there is more of the current diff listing to process.
+ */
+bool
+another_hunk(void)
+{
+ long line_beginning; /* file pos of the current line */
+ LINENUM repl_beginning; /* index of --- line */
+ LINENUM fillcnt; /* #lines of missing ptrn or repl */
+ LINENUM fillsrc; /* index of first line to copy */
+ LINENUM filldst; /* index of first missing line */
+ bool ptrn_spaces_eaten; /* ptrn was slightly misformed */
+ bool repl_could_be_missing; /* no + or ! lines in this hunk */
+ bool repl_missing; /* we are now backtracking */
+ long repl_backtrack_position; /* file pos of first repl line */
+ LINENUM repl_patch_line; /* input line number for same */
+ LINENUM ptrn_copiable; /* # of copiable lines in ptrn */
+ char *s;
+ size_t len;
+ int context = 0;
+
+ while (p_end >= 0) {
+ if (p_end == p_efake)
+ p_end = p_bfake; /* don't free twice */
+ else
+ free(p_line[p_end]);
+ p_end--;
+ }
+ p_efake = -1;
+
+ p_max = hunkmax; /* gets reduced when --- found */
+ if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
+ line_beginning = ftell(pfp);
+ repl_beginning = 0;
+ fillcnt = 0;
+ fillsrc = 0;
+ filldst = 0;
+ ptrn_spaces_eaten = false;
+ repl_could_be_missing = true;
+ repl_missing = false;
+ repl_backtrack_position = 0;
+ repl_patch_line = 0;
+ ptrn_copiable = 0;
+
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0 || strnNE(buf, "********", 8)) {
+ next_intuit_at(line_beginning, p_input_line);
+ return false;
+ }
+ p_context = 100;
+ p_hunk_beg = p_input_line + 1;
+ while (p_end < p_max) {
+ line_beginning = ftell(pfp);
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0) {
+ if (p_max - p_end < 4) {
+ /* assume blank lines got chopped */
+ strlcpy(buf, " \n", buf_size);
+ } else {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ fatal("unexpected end of file in patch\n");
+ }
+ }
+ p_end++;
+ if (p_end >= hunkmax)
+ fatal("Internal error: hunk larger than hunk "
+ "buffer size");
+ p_char[p_end] = *buf;
+ p_line[p_end] = NULL;
+ switch (*buf) {
+ case '*':
+ if (strnEQ(buf, "********", 8)) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ } else
+ fatal("unexpected end of hunk "
+ "at line %ld\n",
+ p_input_line);
+ }
+ if (p_end != 0) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ fatal("unexpected *** at line %ld: %s",
+ p_input_line, buf);
+ }
+ context = 0;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return false;
+ }
+ for (s = buf; *s && !isdigit((unsigned char)*s); s++)
+ ;
+ if (!*s)
+ malformed();
+ if (strnEQ(s, "0,0", 3))
+ memmove(s, s + 2, strlen(s + 2) + 1);
+ p_first = (LINENUM) atol(s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ if (*s == ',') {
+ for (; *s && !isdigit((unsigned char)*s); s++)
+ ;
+ if (!*s)
+ malformed();
+ p_ptrn_lines = ((LINENUM) atol(s)) - p_first + 1;
+ } else if (p_first)
+ p_ptrn_lines = 1;
+ else {
+ p_ptrn_lines = 0;
+ p_first = 1;
+ }
+
+ /* we need this much at least */
+ p_max = p_ptrn_lines + 6;
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ p_max = hunkmax;
+ break;
+ case '-':
+ if (buf[1] == '-') {
+ if (repl_beginning ||
+ (p_end != p_ptrn_lines + 1 +
+ (p_char[p_end - 1] == '\n'))) {
+ if (p_end == 1) {
+ /*
+ * `old' lines were omitted;
+ * set up to fill them in
+ * from 'new' context lines.
+ */
+ p_end = p_ptrn_lines + 1;
+ fillsrc = p_end + 1;
+ filldst = 1;
+ fillcnt = p_ptrn_lines;
+ } else {
+ if (repl_beginning) {
+ if (repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
+ p_input_line, p_hunk_beg + repl_beginning);
+ } else {
+ fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
+ (p_end <= p_ptrn_lines
+ ? "Premature"
+ : "Overdue"),
+ p_input_line, p_hunk_beg);
+ }
+ }
+ }
+ repl_beginning = p_end;
+ repl_backtrack_position = ftell(pfp);
+ repl_patch_line = p_input_line;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return false;
+ }
+ p_char[p_end] = '=';
+ for (s = buf; *s && !isdigit((unsigned char)*s); s++)
+ ;
+ if (!*s)
+ malformed();
+ p_newfirst = (LINENUM) atol(s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ if (*s == ',') {
+ for (; *s && !isdigit((unsigned char)*s); s++)
+ ;
+ if (!*s)
+ malformed();
+ p_repl_lines = ((LINENUM) atol(s)) -
+ p_newfirst + 1;
+ } else if (p_newfirst)
+ p_repl_lines = 1;
+ else {
+ p_repl_lines = 0;
+ p_newfirst = 1;
+ }
+ p_max = p_repl_lines + p_end;
+ if (p_max > MAXHUNKSIZE)
+ fatal("hunk too large (%ld lines) at line %ld: %s",
+ p_max, p_input_line, buf);
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ if (p_repl_lines != ptrn_copiable &&
+ (p_context != 0 || p_repl_lines != 1))
+ repl_could_be_missing = false;
+ break;
+ }
+ goto change_line;
+ case '+':
+ case '!':
+ repl_could_be_missing = false;
+ change_line:
+ if (buf[1] == '\n' && canonicalize)
+ strlcpy(buf + 1, " \n", buf_size - 1);
+ if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
+ buf[1] != '<' &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ if (context >= 0) {
+ if (context < p_context)
+ p_context = context;
+ context = -1000;
+ }
+ p_line[p_end] = savestr(buf + 2);
+ if (out_of_mem) {
+ p_end--;
+ return false;
+ }
+ if (p_end == p_ptrn_lines) {
+ if (remove_special_line()) {
+ int l;
+
+ l = strlen(p_line[p_end]) - 1;
+ (p_line[p_end])[l] = 0;
+ }
+ }
+ break;
+ case '\t':
+ case '\n': /* assume the 2 spaces got eaten */
+ if (repl_beginning && repl_could_be_missing &&
+ (!ptrn_spaces_eaten ||
+ diff_type == NEW_CONTEXT_DIFF)) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return false;
+ }
+ if (p_end != p_ptrn_lines + 1) {
+ ptrn_spaces_eaten |= (repl_beginning != 0);
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_char[p_end] = ' ';
+ }
+ break;
+ case ' ':
+ if (!isspace((unsigned char)buf[1]) &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_line[p_end] = savestr(buf + 2);
+ if (out_of_mem) {
+ p_end--;
+ return false;
+ }
+ break;
+ default:
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = true;
+ goto hunk_done;
+ }
+ malformed();
+ }
+ /* set up p_len for strncmp() so we don't have to */
+ /* assume null termination */
+ if (p_line[p_end])
+ p_len[p_end] = strlen(p_line[p_end]);
+ else
+ p_len[p_end] = 0;
+ }
+
+hunk_done:
+ if (p_end >= 0 && !repl_beginning)
+ fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
+
+ if (repl_missing) {
+
+ /* reset state back to just after --- */
+ p_input_line = repl_patch_line;
+ for (p_end--; p_end > repl_beginning; p_end--)
+ free(p_line[p_end]);
+ fseek(pfp, repl_backtrack_position, SEEK_SET);
+
+ /* redundant 'new' context lines were omitted - set */
+ /* up to fill them in from the old file context */
+ if (!p_context && p_repl_lines == 1) {
+ p_repl_lines = 0;
+ p_max--;
+ }
+ fillsrc = 1;
+ filldst = repl_beginning + 1;
+ fillcnt = p_repl_lines;
+ p_end = p_max;
+ } else if (!p_context && fillcnt == 1) {
+ /* the first hunk was a null hunk with no context */
+ /* and we were expecting one line -- fix it up. */
+ while (filldst < p_end) {
+ p_line[filldst] = p_line[filldst + 1];
+ p_char[filldst] = p_char[filldst + 1];
+ p_len[filldst] = p_len[filldst + 1];
+ filldst++;
+ }
+#if 0
+ repl_beginning--; /* this doesn't need to be fixed */
+#endif
+ p_end--;
+ p_first++; /* do append rather than insert */
+ fillcnt = 0;
+ p_ptrn_lines = 0;
+ }
+ if (diff_type == CONTEXT_DIFF &&
+ (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
+ if (verbose)
+ say("%s\n%s\n%s\n",
+ "(Fascinating--this is really a new-style context diff but without",
+ "the telltale extra asterisks on the *** line that usually indicate",
+ "the new style...)");
+ diff_type = NEW_CONTEXT_DIFF;
+ }
+ /* if there were omitted context lines, fill them in now */
+ if (fillcnt) {
+ p_bfake = filldst; /* remember where not to free() */
+ p_efake = filldst + fillcnt - 1;
+ while (fillcnt-- > 0) {
+ while (fillsrc <= p_end && p_char[fillsrc] != ' ')
+ fillsrc++;
+ if (fillsrc > p_end)
+ fatal("replacement text or line numbers mangled in hunk at line %ld\n",
+ p_hunk_beg);
+ p_line[filldst] = p_line[fillsrc];
+ p_char[filldst] = p_char[fillsrc];
+ p_len[filldst] = p_len[fillsrc];
+ fillsrc++;
+ filldst++;
+ }
+ while (fillsrc <= p_end && fillsrc != repl_beginning &&
+ p_char[fillsrc] != ' ')
+ fillsrc++;
+#ifdef DEBUGGING
+ if (debug & 64)
+ printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
+ fillsrc, filldst, repl_beginning, p_end + 1);
+#endif
+ if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
+ malformed();
+ if (filldst != p_end + 1 && filldst != repl_beginning)
+ malformed();
+ }
+ if (p_line[p_end] != NULL) {
+ if (remove_special_line()) {
+ p_len[p_end] -= 1;
+ (p_line[p_end])[p_len[p_end]] = 0;
+ }
+ }
+ } else if (diff_type == UNI_DIFF) {
+ LINENUM fillold; /* index of old lines */
+ LINENUM fillnew; /* index of new lines */
+ char ch;
+
+ line_beginning = ftell(pfp); /* file pos of the current line */
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0 || strnNE(buf, "@@ -", 4)) {
+ next_intuit_at(line_beginning, p_input_line);
+ return false;
+ }
+ s = buf + 4;
+ if (!*s)
+ malformed();
+ p_first = (LINENUM) atol(s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ if (*s == ',') {
+ p_ptrn_lines = (LINENUM) atol(++s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ } else
+ p_ptrn_lines = 1;
+ if (*s == ' ')
+ s++;
+ if (*s != '+' || !*++s)
+ malformed();
+ p_newfirst = (LINENUM) atol(s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ if (*s == ',') {
+ p_repl_lines = (LINENUM) atol(++s);
+ while (isdigit((unsigned char)*s))
+ s++;
+ } else
+ p_repl_lines = 1;
+ if (*s == ' ')
+ s++;
+ if (*s != '@')
+ malformed();
+ if (!p_ptrn_lines)
+ p_first++; /* do append rather than insert */
+ p_max = p_ptrn_lines + p_repl_lines + 1;
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ fillold = 1;
+ fillnew = fillold + p_ptrn_lines;
+ p_end = fillnew + p_repl_lines;
+ snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first,
+ p_first + p_ptrn_lines - 1);
+ p_line[0] = savestr(buf);
+ if (out_of_mem) {
+ p_end = -1;
+ return false;
+ }
+ p_char[0] = '*';
+ snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst,
+ p_newfirst + p_repl_lines - 1);
+ p_line[fillnew] = savestr(buf);
+ if (out_of_mem) {
+ p_end = 0;
+ return false;
+ }
+ p_char[fillnew++] = '=';
+ p_context = 100;
+ context = 0;
+ p_hunk_beg = p_input_line + 1;
+ while (fillold <= p_ptrn_lines || fillnew <= p_end) {
+ line_beginning = ftell(pfp);
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0) {
+ if (p_max - fillnew < 3) {
+ /* assume blank lines got chopped */
+ strlcpy(buf, " \n", buf_size);
+ } else {
+ fatal("unexpected end of file in patch\n");
+ }
+ }
+ if (*buf == '\t' || *buf == '\n') {
+ ch = ' '; /* assume the space got eaten */
+ s = savestr(buf);
+ } else {
+ ch = *buf;
+ s = savestr(buf + 1);
+ }
+ if (out_of_mem) {
+ while (--fillnew > p_ptrn_lines)
+ free(p_line[fillnew]);
+ p_end = fillold - 1;
+ return false;
+ }
+ switch (ch) {
+ case '-':
+ if (fillold > p_ptrn_lines) {
+ free(s);
+ p_end = fillnew - 1;
+ malformed();
+ }
+ p_char[fillold] = ch;
+ p_line[fillold] = s;
+ p_len[fillold++] = strlen(s);
+ if (fillold > p_ptrn_lines) {
+ if (remove_special_line()) {
+ p_len[fillold - 1] -= 1;
+ s[p_len[fillold - 1]] = 0;
+ }
+ }
+ break;
+ case '=':
+ ch = ' ';
+ /* FALL THROUGH */
+ case ' ':
+ if (fillold > p_ptrn_lines) {
+ free(s);
+ while (--fillnew > p_ptrn_lines)
+ free(p_line[fillnew]);
+ p_end = fillold - 1;
+ malformed();
+ }
+ context++;
+ p_char[fillold] = ch;
+ p_line[fillold] = s;
+ p_len[fillold++] = strlen(s);
+ s = savestr(s);
+ if (out_of_mem) {
+ while (--fillnew > p_ptrn_lines)
+ free(p_line[fillnew]);
+ p_end = fillold - 1;
+ return false;
+ }
+ if (fillold > p_ptrn_lines) {
+ if (remove_special_line()) {
+ p_len[fillold - 1] -= 1;
+ s[p_len[fillold - 1]] = 0;
+ }
+ }
+ /* FALL THROUGH */
+ case '+':
+ if (fillnew > p_end) {
+ free(s);
+ while (--fillnew > p_ptrn_lines)
+ free(p_line[fillnew]);
+ p_end = fillold - 1;
+ malformed();
+ }
+ p_char[fillnew] = ch;
+ p_line[fillnew] = s;
+ p_len[fillnew++] = strlen(s);
+ if (fillold > p_ptrn_lines) {
+ if (remove_special_line()) {
+ p_len[fillnew - 1] -= 1;
+ s[p_len[fillnew - 1]] = 0;
+ }
+ }
+ break;
+ default:
+ p_end = fillnew;
+ malformed();
+ }
+ if (ch != ' ' && context > 0) {
+ if (context < p_context)
+ p_context = context;
+ context = -1000;
+ }
+ } /* while */
+ } else { /* normal diff--fake it up */
+ char hunk_type;
+ int i;
+ LINENUM min, max;
+
+ line_beginning = ftell(pfp);
+ p_context = 0;
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0 || !isdigit((unsigned char)*buf)) {
+ next_intuit_at(line_beginning, p_input_line);
+ return false;
+ }
+ p_first = (LINENUM) atol(buf);
+ for (s = buf; isdigit((unsigned char)*s); s++)
+ ;
+ if (*s == ',') {
+ p_ptrn_lines = (LINENUM) atol(++s) - p_first + 1;
+ while (isdigit((unsigned char)*s))
+ s++;
+ } else
+ p_ptrn_lines = (*s != 'a');
+ hunk_type = *s;
+ if (hunk_type == 'a')
+ p_first++; /* do append rather than insert */
+ min = (LINENUM) atol(++s);
+ for (; isdigit((unsigned char)*s); s++)
+ ;
+ if (*s == ',')
+ max = (LINENUM) atol(++s);
+ else
+ max = min;
+ if (hunk_type == 'd')
+ min++;
+ p_end = p_ptrn_lines + 1 + max - min + 1;
+ if (p_end > MAXHUNKSIZE)
+ fatal("hunk too large (%ld lines) at line %ld: %s",
+ p_end, p_input_line, buf);
+ while (p_end >= hunkmax)
+ grow_hunkmax();
+ p_newfirst = min;
+ p_repl_lines = max - min + 1;
+ snprintf(buf, buf_size, "*** %ld,%ld\n", p_first,
+ p_first + p_ptrn_lines - 1);
+ p_line[0] = savestr(buf);
+ if (out_of_mem) {
+ p_end = -1;
+ return false;
+ }
+ p_char[0] = '*';
+ for (i = 1; i <= p_ptrn_lines; i++) {
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0)
+ fatal("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '<')
+ fatal("< expected at line %ld of patch\n",
+ p_input_line);
+ p_line[i] = savestr(buf + 2);
+ if (out_of_mem) {
+ p_end = i - 1;
+ return false;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_char[i] = '-';
+ }
+
+ if (remove_special_line()) {
+ p_len[i - 1] -= 1;
+ (p_line[i - 1])[p_len[i - 1]] = 0;
+ }
+ if (hunk_type == 'c') {
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0)
+ fatal("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '-')
+ fatal("--- expected at line %ld of patch\n",
+ p_input_line);
+ }
+ snprintf(buf, buf_size, "--- %ld,%ld\n", min, max);
+ p_line[i] = savestr(buf);
+ if (out_of_mem) {
+ p_end = i - 1;
+ return false;
+ }
+ p_char[i] = '=';
+ for (i++; i <= p_end; i++) {
+ len = pgets(true);
+ p_input_line++;
+ if (len == 0)
+ fatal("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '>')
+ fatal("> expected at line %ld of patch\n",
+ p_input_line);
+ p_line[i] = savestr(buf + 2);
+ if (out_of_mem) {
+ p_end = i - 1;
+ return false;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_char[i] = '+';
+ }
+
+ if (remove_special_line()) {
+ p_len[i - 1] -= 1;
+ (p_line[i - 1])[p_len[i - 1]] = 0;
+ }
+ }
+ if (reverse) /* backwards patch? */
+ if (!pch_swap())
+ say("Not enough memory to swap next hunk!\n");
+#ifdef DEBUGGING
+ if (debug & 2) {
+ int i;
+ char special;
+
+ for (i = 0; i <= p_end; i++) {
+ if (i == p_ptrn_lines)
+ special = '^';
+ else
+ special = ' ';
+ fprintf(stderr, "%3d %c %c %s", i, p_char[i],
+ special, p_line[i]);
+ fflush(stderr);
+ }
+ }
+#endif
+ if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
+ p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */
+ return true;
+}
+
+/*
+ * Input a line from the patch file.
+ * Worry about indentation if do_indent is true.
+ * The line is read directly into the buf global variable which
+ * is resized if necessary in order to hold the complete line.
+ * Returns the number of characters read including the terminating
+ * '\n', if any.
+ */
+size_t
+pgets(bool do_indent)
+{
+ char *line;
+ size_t len;
+ int indent = 0, skipped = 0;
+
+ line = fgetln(pfp, &len);
+ if (line != NULL) {
+ if (len + 1 > buf_size) {
+ while (len + 1 > buf_size)
+ buf_size *= 2;
+ free(buf);
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ fatal("out of memory\n");
+ }
+ if (do_indent == 1 && p_indent) {
+ for (;
+ indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X');
+ line++, skipped++) {
+ if (*line == '\t')
+ indent += 8 - (indent %7);
+ else
+ indent++;
+ }
+ }
+ strncpy(buf, line, len - skipped);
+ buf[len - skipped] = '\0';
+ }
+ return len;
+}
+
+
+/*
+ * Reverse the old and new portions of the current hunk.
+ */
+bool
+pch_swap(void)
+{
+ char **tp_line; /* the text of the hunk */
+ short *tp_len; /* length of each line */
+ char *tp_char; /* +, -, and ! */
+ LINENUM i;
+ LINENUM n;
+ bool blankline = false;
+ char *s;
+
+ i = p_first;
+ p_first = p_newfirst;
+ p_newfirst = i;
+
+ /* make a scratch copy */
+
+ tp_line = p_line;
+ tp_len = p_len;
+ tp_char = p_char;
+ p_line = NULL; /* force set_hunkmax to allocate again */
+ p_len = NULL;
+ p_char = NULL;
+ set_hunkmax();
+ if (p_line == NULL || p_len == NULL || p_char == NULL) {
+
+ free(p_line);
+ p_line = tp_line;
+ free(p_len);
+ p_len = tp_len;
+ free(p_char);
+ p_char = tp_char;
+ return false; /* not enough memory to swap hunk! */
+ }
+ /* now turn the new into the old */
+
+ i = p_ptrn_lines + 1;
+ if (tp_char[i] == '\n') { /* account for possible blank line */
+ blankline = true;
+ i++;
+ }
+ if (p_efake >= 0) { /* fix non-freeable ptr range */
+ if (p_efake <= i)
+ n = p_end - i + 1;
+ else
+ n = -i;
+ p_efake += n;
+ p_bfake += n;
+ }
+ for (n = 0; i <= p_end; i++, n++) {
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ if (p_char[n] == '+')
+ p_char[n] = '-';
+ p_len[n] = tp_len[i];
+ }
+ if (blankline) {
+ i = p_ptrn_lines + 1;
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ p_len[n] = tp_len[i];
+ n++;
+ }
+ if (p_char[0] != '=')
+ fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
+ p_input_line, p_char[0]);
+ p_char[0] = '*';
+ for (s = p_line[0]; *s; s++)
+ if (*s == '-')
+ *s = '*';
+
+ /* now turn the old into the new */
+
+ if (p_char[0] != '*')
+ fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
+ p_input_line, p_char[0]);
+ tp_char[0] = '=';
+ for (s = tp_line[0]; *s; s++)
+ if (*s == '*')
+ *s = '-';
+ for (i = 0; n <= p_end; i++, n++) {
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ if (p_char[n] == '-')
+ p_char[n] = '+';
+ p_len[n] = tp_len[i];
+ }
+
+ if (i != p_ptrn_lines + 1)
+ fatal("Malformed patch at line %ld: expected %ld lines, "
+ "got %ld\n",
+ p_input_line, p_ptrn_lines + 1, i);
+
+ i = p_ptrn_lines;
+ p_ptrn_lines = p_repl_lines;
+ p_repl_lines = i;
+
+ free(tp_line);
+ free(tp_len);
+ free(tp_char);
+
+ return true;
+}
+
+/*
+ * Return the specified line position in the old file of the old context.
+ */
+LINENUM
+pch_first(void)
+{
+ return p_first;
+}
+
+/*
+ * Return the number of lines of old context.
+ */
+LINENUM
+pch_ptrn_lines(void)
+{
+ return p_ptrn_lines;
+}
+
+/*
+ * Return the probable line position in the new file of the first line.
+ */
+LINENUM
+pch_newfirst(void)
+{
+ return p_newfirst;
+}
+
+/*
+ * Return the number of lines in the replacement text including context.
+ */
+LINENUM
+pch_repl_lines(void)
+{
+ return p_repl_lines;
+}
+
+/*
+ * Return the number of lines in the whole hunk.
+ */
+LINENUM
+pch_end(void)
+{
+ return p_end;
+}
+
+/*
+ * Return the number of context lines before the first changed line.
+ */
+LINENUM
+pch_context(void)
+{
+ return p_context;
+}
+
+/*
+ * Return the length of a particular patch line.
+ */
+short
+pch_line_len(LINENUM line)
+{
+ return p_len[line];
+}
+
+/*
+ * Return the control character (+, -, *, !, etc) for a patch line.
+ */
+char
+pch_char(LINENUM line)
+{
+ return p_char[line];
+}
+
+/*
+ * Return a pointer to a particular patch line.
+ */
+char *
+pfetch(LINENUM line)
+{
+ return p_line[line];
+}
+
+/*
+ * Return where in the patch file this hunk began, for error messages.
+ */
+LINENUM
+pch_hunk_beg(void)
+{
+ return p_hunk_beg;
+}
+
+/*
+ * Apply an ed script by feeding ed itself.
+ */
+void
+do_ed_script(void)
+{
+ char *t;
+ long beginning_of_this_line;
+ FILE *pipefp = NULL;
+
+ if (!skip_rest_of_patch) {
+ if (copy_file(filearg[0], TMPOUTNAME) < 0) {
+ unlink(TMPOUTNAME);
+ fatal("can't create temp file %s", TMPOUTNAME);
+ }
+ snprintf(buf, buf_size, "%s%s%s", _PATH_ED,
+ verbose ? " " : " -s ", TMPOUTNAME);
+ pipefp = popen(buf, "w");
+ }
+ for (;;) {
+ beginning_of_this_line = ftell(pfp);
+ if (pgets(true) == 0) {
+ next_intuit_at(beginning_of_this_line, p_input_line);
+ break;
+ }
+ p_input_line++;
+ for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
+ ;
+ /* POSIX defines allowed commands as {a,c,d,i,s} */
+ if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
+ *t == 'd' || *t == 'i' || *t == 's')) {
+ if (pipefp != NULL)
+ fputs(buf, pipefp);
+ if (*t != 'd') {
+ while (pgets(true)) {
+ p_input_line++;
+ if (pipefp != NULL)
+ fputs(buf, pipefp);
+ if (strEQ(buf, ".\n"))
+ break;
+ }
+ }
+ } else {
+ next_intuit_at(beginning_of_this_line, p_input_line);
+ break;
+ }
+ }
+ if (pipefp == NULL)
+ return;
+ fprintf(pipefp, "w\n");
+ fprintf(pipefp, "q\n");
+ fflush(pipefp);
+ pclose(pipefp);
+ ignore_signals();
+ if (!check_only) {
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = true;
+ chmod(TMPOUTNAME, filemode);
+ } else
+ chmod(outname, filemode);
+ }
+ set_signals(1);
+}
+
+/*
+ * Choose the name of the file to be patched based on POSIX rules.
+ * NOTE: the POSIX rules are amazingly stupid and we only follow them
+ * if the user specified --posix or set POSIXLY_CORRECT.
+ */
+static char *
+posix_name(const struct file_name *names, bool assume_exists)
+{
+ char *path = NULL;
+ int i;
+
+ /*
+ * POSIX states that the filename will be chosen from one
+ * of the old, new and index names (in that order) if
+ * the file exists relative to CWD after -p stripping.
+ */
+ for (i = 0; i < MAX_FILE; i++) {
+ if (names[i].path != NULL && names[i].exists) {
+ path = names[i].path;
+ break;
+ }
+ }
+ if (path == NULL && !assume_exists) {
+ /*
+ * No files found, look for something we can checkout from
+ * RCS/SCCS dirs. Same order as above.
+ */
+ for (i = 0; i < MAX_FILE; i++) {
+ if (names[i].path != NULL &&
+ (path = checked_in(names[i].path)) != NULL)
+ break;
+ }
+ /*
+ * Still no match? Check to see if the diff could be creating
+ * a new file.
+ */
+ if (path == NULL && ok_to_create_file &&
+ names[NEW_FILE].path != NULL)
+ path = names[NEW_FILE].path;
+ }
+
+ return path ? savestr(path) : NULL;
+}
+
+/*
+ * Choose the name of the file to be patched based the "best" one
+ * available.
+ */
+static char *
+best_name(const struct file_name *names, bool assume_exists)
+{
+ size_t min_components, min_baselen, min_len, tmp;
+ char *best = NULL;
+ int i;
+
+ /*
+ * The "best" name is the one with the fewest number of path
+ * components, the shortest basename length, and the shortest
+ * overall length (in that order). We only use the Index: file
+ * if neither of the old or new files could be intuited from
+ * the diff header.
+ */
+ min_components = min_baselen = min_len = SIZE_MAX;
+ for (i = INDEX_FILE; i >= OLD_FILE; i--) {
+ if (names[i].path == NULL ||
+ (!names[i].exists && !assume_exists))
+ continue;
+ if ((tmp = num_components(names[i].path)) > min_components)
+ continue;
+ min_components = tmp;
+ if ((tmp = strlen(basename(names[i].path))) > min_baselen)
+ continue;
+ min_baselen = tmp;
+ if ((tmp = strlen(names[i].path)) > min_len)
+ continue;
+ min_len = tmp;
+ best = names[i].path;
+ }
+ if (best == NULL) {
+ /*
+ * No files found, look for something we can checkout from
+ * RCS/SCCS dirs. Logic is identical to that above...
+ */
+ min_components = min_baselen = min_len = SIZE_MAX;
+ for (i = INDEX_FILE; i >= OLD_FILE; i--) {
+ if (names[i].path == NULL ||
+ checked_in(names[i].path) == NULL)
+ continue;
+ if ((tmp = num_components(names[i].path)) > min_components)
+ continue;
+ min_components = tmp;
+ if ((tmp = strlen(basename(names[i].path))) > min_baselen)
+ continue;
+ min_baselen = tmp;
+ if ((tmp = strlen(names[i].path)) > min_len)
+ continue;
+ min_len = tmp;
+ best = names[i].path;
+ }
+ /*
+ * Still no match? Check to see if the diff could be creating
+ * a new file.
+ */
+ if (best == NULL && ok_to_create_file &&
+ names[NEW_FILE].path != NULL)
+ best = names[NEW_FILE].path;
+ }
+
+ return best ? savestr(best) : NULL;
+}
+
+static size_t
+num_components(const char *path)
+{
+ size_t n;
+ const char *cp;
+
+ for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
+ while (*cp == '/')
+ cp++; /* skip consecutive slashes */
+ }
+ return n;
+}
diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h
new file mode 100644
index 0000000..42910c2
--- /dev/null
+++ b/usr.bin/patch/pch.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
+ * $FreeBSD$
+ */
+
+#define OLD_FILE 0
+#define NEW_FILE 1
+#define INDEX_FILE 2
+#define MAX_FILE 3
+
+struct file_name {
+ char *path;
+ bool exists;
+};
+
+void re_patch(void);
+void open_patch_file(const char *);
+void set_hunkmax(void);
+bool there_is_another_patch(void);
+bool another_hunk(void);
+bool pch_swap(void);
+char *pfetch(LINENUM);
+short pch_line_len(LINENUM);
+LINENUM pch_first(void);
+LINENUM pch_ptrn_lines(void);
+LINENUM pch_newfirst(void);
+LINENUM pch_repl_lines(void);
+LINENUM pch_end(void);
+LINENUM pch_context(void);
+LINENUM pch_hunk_beg(void);
+char pch_char(LINENUM);
+void do_ed_script(void);
diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c
new file mode 100644
index 0000000..3895b2c
--- /dev/null
+++ b/usr.bin/patch/util.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: util.c,v 1.35 2010/07/24 01:10:12 ray Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "backupfile.h"
+#include "pathnames.h"
+
+/* Rename a file, copying it if necessary. */
+
+int
+move_file(const char *from, const char *to)
+{
+ int fromfd;
+ ssize_t i;
+
+ /* to stdout? */
+
+ if (strEQ(to, "-")) {
+#ifdef DEBUGGING
+ if (debug & 4)
+ say("Moving %s to stdout.\n", from);
+#endif
+ fromfd = open(from, O_RDONLY);
+ if (fromfd < 0)
+ pfatal("internal error, can't reopen %s", from);
+ while ((i = read(fromfd, buf, buf_size)) > 0)
+ if (write(STDOUT_FILENO, buf, i) != i)
+ pfatal("write failed");
+ close(fromfd);
+ return 0;
+ }
+ if (backup_file(to) < 0) {
+ say("Can't backup %s, output is in %s: %s\n", to, from,
+ strerror(errno));
+ return -1;
+ }
+#ifdef DEBUGGING
+ if (debug & 4)
+ say("Moving %s to %s.\n", from, to);
+#endif
+ if (rename(from, to) < 0) {
+ if (errno != EXDEV || copy_file(from, to) < 0) {
+ say("Can't create %s, output is in %s: %s\n",
+ to, from, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Backup the original file. */
+
+int
+backup_file(const char *orig)
+{
+ struct stat filestat;
+ char bakname[MAXPATHLEN], *s, *simplename;
+ dev_t orig_device;
+ ino_t orig_inode;
+
+ if (backup_type == none || stat(orig, &filestat) != 0)
+ return 0; /* nothing to do */
+ /*
+ * If the user used zero prefixes or suffixes, then
+ * he doesn't want backups. Yet we have to remove
+ * orig to break possible hardlinks.
+ */
+ if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
+ unlink(orig);
+ return 0;
+ }
+ orig_device = filestat.st_dev;
+ orig_inode = filestat.st_ino;
+
+ if (origprae) {
+ if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
+ strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
+ fatal("filename %s too long for buffer\n", origprae);
+ } else {
+ if ((s = find_backup_file_name(orig)) == NULL)
+ fatal("out of memory\n");
+ if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
+ fatal("filename %s too long for buffer\n", s);
+ free(s);
+ }
+
+ if ((simplename = strrchr(bakname, '/')) != NULL)
+ simplename = simplename + 1;
+ else
+ simplename = bakname;
+
+ /*
+ * Find a backup name that is not the same file. Change the
+ * first lowercase char into uppercase; if that isn't
+ * sufficient, chop off the first char and try again.
+ */
+ while (stat(bakname, &filestat) == 0 &&
+ orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
+ /* Skip initial non-lowercase chars. */
+ for (s = simplename; *s && !islower((unsigned char)*s); s++)
+ ;
+ if (*s)
+ *s = toupper((unsigned char)*s);
+ else
+ memmove(simplename, simplename + 1,
+ strlen(simplename + 1) + 1);
+ }
+#ifdef DEBUGGING
+ if (debug & 4)
+ say("Moving %s to %s.\n", orig, bakname);
+#endif
+ if (rename(orig, bakname) < 0) {
+ if (errno != EXDEV || copy_file(orig, bakname) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Copy a file.
+ */
+int
+copy_file(const char *from, const char *to)
+{
+ int tofd, fromfd;
+ ssize_t i;
+
+ tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ if (tofd < 0)
+ return -1;
+ fromfd = open(from, O_RDONLY, 0);
+ if (fromfd < 0)
+ pfatal("internal error, can't reopen %s", from);
+ while ((i = read(fromfd, buf, buf_size)) > 0)
+ if (write(tofd, buf, i) != i)
+ pfatal("write to %s failed", to);
+ close(fromfd);
+ close(tofd);
+ return 0;
+}
+
+/*
+ * Allocate a unique area for a string.
+ */
+char *
+savestr(const char *s)
+{
+ char *rv;
+
+ if (!s)
+ s = "Oops";
+ rv = strdup(s);
+ if (rv == NULL) {
+ if (using_plan_a)
+ out_of_mem = true;
+ else
+ fatal("out of memory\n");
+ }
+ return rv;
+}
+
+/*
+ * Vanilla terminal output (buffered).
+ */
+void
+say(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ fflush(stdout);
+}
+
+/*
+ * Terminal output, pun intended.
+ */
+void
+fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "patch: **** ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ my_exit(2);
+}
+
+/*
+ * Say something from patch, something from the system, then silence . . .
+ */
+void
+pfatal(const char *fmt, ...)
+{
+ va_list ap;
+ int errnum = errno;
+
+ fprintf(stderr, "patch: **** ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s\n", strerror(errnum));
+ my_exit(2);
+}
+
+/*
+ * Get a response from the user via /dev/tty
+ */
+void
+ask(const char *fmt, ...)
+{
+ va_list ap;
+ ssize_t nr = 0;
+ static int ttyfd = -1;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ fflush(stdout);
+ if (ttyfd < 0)
+ ttyfd = open(_PATH_TTY, O_RDONLY);
+ if (ttyfd >= 0) {
+ if ((nr = read(ttyfd, buf, buf_size)) > 0 &&
+ buf[nr - 1] == '\n')
+ buf[nr - 1] = '\0';
+ }
+ if (ttyfd < 0 || nr <= 0) {
+ /* no tty or error reading, pretend user entered 'return' */
+ putchar('\n');
+ buf[0] = '\0';
+ }
+}
+
+/*
+ * How to handle certain events when not in a critical region.
+ */
+void
+set_signals(int reset)
+{
+ static sig_t hupval, intval;
+
+ if (!reset) {
+ hupval = signal(SIGHUP, SIG_IGN);
+ if (hupval != SIG_IGN)
+ hupval = my_exit;
+ intval = signal(SIGINT, SIG_IGN);
+ if (intval != SIG_IGN)
+ intval = my_exit;
+ }
+ signal(SIGHUP, hupval);
+ signal(SIGINT, intval);
+}
+
+/*
+ * How to handle certain events when in a critical region.
+ */
+void
+ignore_signals(void)
+{
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+}
+
+/*
+ * Make sure we'll have the directories to create a file. If `striplast' is
+ * true, ignore the last element of `filename'.
+ */
+
+void
+makedirs(const char *filename, bool striplast)
+{
+ char *tmpbuf;
+
+ if ((tmpbuf = strdup(filename)) == NULL)
+ fatal("out of memory\n");
+
+ if (striplast) {
+ char *s = strrchr(tmpbuf, '/');
+ if (s == NULL) {
+ free(tmpbuf);
+ return; /* nothing to be done */
+ }
+ *s = '\0';
+ }
+ if (mkpath(tmpbuf) != 0)
+ pfatal("creation of %s failed", tmpbuf);
+ free(tmpbuf);
+}
+
+/*
+ * Make filenames more reasonable.
+ */
+char *
+fetchname(const char *at, bool *exists, int strip_leading)
+{
+ char *fullname, *name, *t;
+ int sleading, tab;
+ struct stat filestat;
+
+ if (at == NULL || *at == '\0')
+ return NULL;
+ while (isspace((unsigned char)*at))
+ at++;
+#ifdef DEBUGGING
+ if (debug & 128)
+ say("fetchname %s %d\n", at, strip_leading);
+#endif
+ /* So files can be created by diffing against /dev/null. */
+ if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
+ return NULL;
+ name = fullname = t = savestr(at);
+
+ tab = strchr(t, '\t') != NULL;
+ /* Strip off up to `strip_leading' path components and NUL terminate. */
+ for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
+ !isspace((unsigned char)*t)); t++) {
+ if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
+ if (--sleading >= 0)
+ name = t + 1;
+ }
+ *t = '\0';
+
+ /*
+ * If no -p option was given (957 is the default value!), we were
+ * given a relative pathname, and the leading directories that we
+ * just stripped off all exist, put them back on.
+ */
+ if (strip_leading == 957 && name != fullname && *fullname != '/') {
+ name[-1] = '\0';
+ if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
+ name[-1] = '/';
+ name = fullname;
+ }
+ }
+ name = savestr(name);
+ free(fullname);
+
+ *exists = stat(name, &filestat) == 0;
+ return name;
+}
+
+/*
+ * Takes the name returned by fetchname and looks in RCS/SCCS directories
+ * for a checked in version.
+ */
+char *
+checked_in(char *file)
+{
+ char *filebase, *filedir, tmpbuf[MAXPATHLEN];
+ struct stat filestat;
+
+ filebase = basename(file);
+ filedir = dirname(file);
+
+#define try(f, a1, a2, a3) \
+(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
+
+ if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
+ try("%s/RCS/%s%s", filedir, filebase, "") ||
+ try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
+ try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
+ try("%s/%s%s", filedir, SCCSPREFIX, filebase))
+ return file;
+
+ return NULL;
+}
+
+void
+version(void)
+{
+ fprintf(stderr, "patch (BSD patch) 2.0-FreeBSD\n");
+ my_exit(EXIT_SUCCESS);
+}
+
+/*
+ * Exit with cleanup.
+ */
+void
+my_exit(int status)
+{
+ unlink(TMPINNAME);
+ if (!toutkeep)
+ unlink(TMPOUTNAME);
+ if (!trejkeep)
+ unlink(TMPREJNAME);
+ unlink(TMPPATNAME);
+ exit(status);
+}
diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h
new file mode 100644
index 0000000..5759d68
--- /dev/null
+++ b/usr.bin/patch/util.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright 1986, Larry Wall
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ *
+ * 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.
+ *
+ * patch - a program to apply diffs to original files
+ *
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ *
+ * $OpenBSD: util.h,v 1.15 2005/06/20 07:14:06 otto Exp $
+ * $FreeBSD$
+ */
+
+char *fetchname(const char *, bool *, int);
+char *checked_in(char *);
+int backup_file(const char *);
+int move_file(const char *, const char *);
+int copy_file(const char *, const char *);
+void say(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+void fatal(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+void pfatal(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+void ask(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+char *savestr(const char *);
+void set_signals(int);
+void ignore_signals(void);
+void makedirs(const char *, bool);
+void version(void);
+void my_exit(int) __attribute__((noreturn));
+
+/* in mkpath.c */
+extern int mkpath(char *);
diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c
index b0cee6b..934e292 100644
--- a/usr.bin/procstat/procstat.c
+++ b/usr.bin/procstat/procstat.c
@@ -216,8 +216,8 @@ main(int argc, char *argv[])
argv += optind;
/* We require that either 0 or 1 mode flags be set. */
- tmp = bflag + cflag + eflag + fflag + (kflag ? 1 : 0) + lflag + sflag +
- tflag + vflag + xflag;
+ tmp = bflag + cflag + eflag + fflag + iflag + jflag + (kflag ? 1 : 0) +
+ lflag + sflag + tflag + vflag + xflag;
if (!(tmp == 0 || tmp == 1))
usage();
diff --git a/usr.bin/sort/sort.c b/usr.bin/sort/sort.c
index 69a5a74..7e0ab69 100644
--- a/usr.bin/sort/sort.c
+++ b/usr.bin/sort/sort.c
@@ -434,8 +434,7 @@ parse_memory_buffer_value(const char *value)
100;
break;
default:
- fprintf(stderr, "%s: %s\n", strerror(EINVAL),
- optarg);
+ warnc(EINVAL, "%s", optarg);
membuf = available_free_memory;
}
}
@@ -658,7 +657,7 @@ parse_pos(const char *s, struct key_specs *ks, bool *mef_flags, bool second)
errno = 0;
ks->f2 = (size_t) strtoul(f, NULL, 10);
if (errno != 0)
- errx(2, "%s: -k", strerror(errno));
+ err(2, "-k");
if (ks->f2 == 0) {
warn("%s",getstr(5));
goto end;
@@ -667,7 +666,7 @@ parse_pos(const char *s, struct key_specs *ks, bool *mef_flags, bool second)
errno = 0;
ks->f1 = (size_t) strtoul(f, NULL, 10);
if (errno != 0)
- errx(2, "%s: -k", strerror(errno));
+ err(2, "-k");
if (ks->f1 == 0) {
warn("%s",getstr(5));
goto end;
@@ -685,12 +684,12 @@ parse_pos(const char *s, struct key_specs *ks, bool *mef_flags, bool second)
errno = 0;
ks->c2 = (size_t) strtoul(c, NULL, 10);
if (errno != 0)
- errx(2, "%s: -k", strerror(errno));
+ err(2, "-k");
} else {
errno = 0;
ks->c1 = (size_t) strtoul(c, NULL, 10);
if (errno != 0)
- errx(2, "%s: -k", strerror(errno));
+ err(2, "-k");
if (ks->c1 == 0) {
warn("%s",getstr(6));
goto end;
@@ -987,21 +986,6 @@ main(int argc, char **argv)
set_tmpdir();
set_sort_opts();
-#if 0
- {
- static int counter = 0;
- char fn[128];
- sprintf(fn, "/var/tmp/debug.sort.%d", counter++);
- FILE* f = fopen(fn, "w");
- fprintf(f, ">>sort>>");
- for (int i = 0; i < argc; i++) {
- fprintf(f, "<%s>", argv[i]);
- }
- fprintf(f, "<<sort<<\n");
- fclose(f);
- }
-#endif
-
fix_obsolete_keys(&argc, argv);
while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL))
@@ -1041,8 +1025,7 @@ main(int argc, char **argv)
if (parse_k(optarg, &(keys[keys_num - 1]))
< 0) {
- errx(2, "%s: -k %s\n",
- strerror(EINVAL), optarg);
+ errc(2, EINVAL, "-k %s", optarg);
}
break;
@@ -1067,8 +1050,7 @@ main(int argc, char **argv)
case 't':
while (strlen(optarg) > 1) {
if (optarg[0] != '\\') {
- errx(2, "%s: %s\n",
- strerror(EINVAL), optarg);
+ errc(2, EINVAL, "%s", optarg);
}
optarg += 1;
if (*optarg == '0') {
@@ -1155,8 +1137,7 @@ main(int argc, char **argv)
errno = 0;
long mof = strtol(optarg, NULL, 10);
if (errno != 0)
- errx(2, "--batch-size: %s",
- strerror(errno));
+ err(2, "--batch-size");
if (mof >= 2)
max_open_files = (size_t) mof + 1;
}
diff --git a/usr.bin/stdbuf/stdbuf.c b/usr.bin/stdbuf/stdbuf.c
index 3831b5e..4346cc8 100644
--- a/usr.bin/stdbuf/stdbuf.c
+++ b/usr.bin/stdbuf/stdbuf.c
@@ -39,7 +39,7 @@ extern char *__progname;
static void
usage(int s)
{
-
+
fprintf(stderr, "Usage: %s [-e 0|L|<sz>] [-i 0|L|<sz>] [-o 0|L|<sz>] "
"<cmd> [args ...]\n", __progname);
exit(s);
@@ -72,8 +72,8 @@ main(int argc, char *argv[])
}
argc -= optind;
argv += optind;
- if (argc < 2)
- usage(0);
+ if (argc == 0)
+ exit(0);
if (ibuf != NULL && setenv("_STDBUF_I", ibuf, 1) == -1)
warn("Failed to set environment variable: %s=%s",
@@ -94,7 +94,7 @@ main(int argc, char *argv[])
if (i < 0 || putenv(preload1) == -1)
warn("Failed to set environment variable: LD_PRELOAD");
-
+
preload0 = getenv("LD_32_PRELOAD");
if (preload0 == NULL)
i = asprintf(&preload1, "LD_32_PRELOAD=" LIBSTDBUF32);
diff --git a/usr.bin/systat/vmstat.c b/usr.bin/systat/vmstat.c
index 459b58d..856a9a6 100644
--- a/usr.bin/systat/vmstat.c
+++ b/usr.bin/systat/vmstat.c
@@ -79,6 +79,7 @@ static struct Info {
* Virtual memory activity.
*/
u_int v_vm_faults; /* number of address memory faults */
+ u_int v_io_faults; /* page faults requiring I/O */
u_int v_cow_faults; /* number of copy-on-writes */
u_int v_zfod; /* pages zero filled on demand */
u_int v_ozfod; /* optimized zero fill pages */
@@ -324,24 +325,25 @@ labelkre(void)
mvprintw(INTSROW, INTSCOL + 1, "Interrupts");
mvprintw(INTSROW + 1, INTSCOL + 6, "total");
- mvprintw(VMSTATROW, VMSTATCOL + 9, "cow");
- mvprintw(VMSTATROW + 1, VMSTATCOL + 9, "zfod");
- mvprintw(VMSTATROW + 2, VMSTATCOL + 9, "ozfod");
- mvprintw(VMSTATROW + 3, VMSTATCOL + 9 - 1, "%%ozfod");
- mvprintw(VMSTATROW + 4, VMSTATCOL + 9, "daefr");
- mvprintw(VMSTATROW + 5, VMSTATCOL + 9, "prcfr");
- mvprintw(VMSTATROW + 6, VMSTATCOL + 9, "totfr");
- mvprintw(VMSTATROW + 7, VMSTATCOL + 9, "react");
- mvprintw(VMSTATROW + 8, VMSTATCOL + 9, "pdwak");
- mvprintw(VMSTATROW + 9, VMSTATCOL + 9, "pdpgs");
- mvprintw(VMSTATROW + 10, VMSTATCOL + 9, "intrn");
- mvprintw(VMSTATROW + 11, VMSTATCOL + 9, "wire");
- mvprintw(VMSTATROW + 12, VMSTATCOL + 9, "act");
- mvprintw(VMSTATROW + 13, VMSTATCOL + 9, "inact");
- mvprintw(VMSTATROW + 14, VMSTATCOL + 9, "cache");
- mvprintw(VMSTATROW + 15, VMSTATCOL + 9, "free");
- if (LINES - 1 > VMSTATROW + 16)
- mvprintw(VMSTATROW + 16, VMSTATCOL + 9, "buf");
+ mvprintw(VMSTATROW, VMSTATCOL + 9, "ioflt");
+ mvprintw(VMSTATROW + 1, VMSTATCOL + 9, "cow");
+ mvprintw(VMSTATROW + 2, VMSTATCOL + 9, "zfod");
+ mvprintw(VMSTATROW + 3, VMSTATCOL + 9, "ozfod");
+ mvprintw(VMSTATROW + 4, VMSTATCOL + 9 - 1, "%%ozfod");
+ mvprintw(VMSTATROW + 5, VMSTATCOL + 9, "daefr");
+ mvprintw(VMSTATROW + 6, VMSTATCOL + 9, "prcfr");
+ mvprintw(VMSTATROW + 7, VMSTATCOL + 9, "totfr");
+ mvprintw(VMSTATROW + 8, VMSTATCOL + 9, "react");
+ mvprintw(VMSTATROW + 9, VMSTATCOL + 9, "pdwak");
+ mvprintw(VMSTATROW + 10, VMSTATCOL + 9, "pdpgs");
+ mvprintw(VMSTATROW + 11, VMSTATCOL + 9, "intrn");
+ mvprintw(VMSTATROW + 12, VMSTATCOL + 9, "wire");
+ mvprintw(VMSTATROW + 13, VMSTATCOL + 9, "act");
+ mvprintw(VMSTATROW + 14, VMSTATCOL + 9, "inact");
+ mvprintw(VMSTATROW + 15, VMSTATCOL + 9, "cache");
+ mvprintw(VMSTATROW + 16, VMSTATCOL + 9, "free");
+ if (LINES - 1 > VMSTATROW + 17)
+ mvprintw(VMSTATROW + 17, VMSTATCOL + 9, "buf");
mvprintw(GENSTATROW, GENSTATCOL, " Csw Trp Sys Int Sof Flt");
@@ -493,25 +495,26 @@ showkre(void)
putint(total.t_dw, PROCSROW + 2, PROCSCOL + 8, 3);
putint(total.t_sl, PROCSROW + 2, PROCSCOL + 12, 3);
putint(total.t_sw, PROCSROW + 2, PROCSCOL + 16, 3);
- PUTRATE(v_cow_faults, VMSTATROW, VMSTATCOL + 2, 8 - 2);
- PUTRATE(v_zfod, VMSTATROW + 1, VMSTATCOL + 2, 8 - 2);
- PUTRATE(v_ozfod, VMSTATROW + 2, VMSTATCOL, 8);
+ PUTRATE(v_io_faults, VMSTATROW, VMSTATCOL + 2, 8 - 2);
+ PUTRATE(v_cow_faults, VMSTATROW + 1, VMSTATCOL + 2, 8 - 2);
+ PUTRATE(v_zfod, VMSTATROW + 2, VMSTATCOL + 2, 8 - 2);
+ PUTRATE(v_ozfod, VMSTATROW + 3, VMSTATCOL, 8);
putint(s.v_zfod != 0 ? (int)(s.v_ozfod * 100.0 / s.v_zfod) : 0,
- VMSTATROW + 3, VMSTATCOL + 1, 8 - 1);
- PUTRATE(v_dfree, VMSTATROW + 4, VMSTATCOL + 2, 8 - 2);
- PUTRATE(v_pfree, VMSTATROW + 5, VMSTATCOL + 2, 8 - 2);
- PUTRATE(v_tfree, VMSTATROW + 6, VMSTATCOL, 8);
- PUTRATE(v_reactivated, VMSTATROW + 7, VMSTATCOL, 8);
- PUTRATE(v_pdwakeups, VMSTATROW + 8, VMSTATCOL, 8);
- PUTRATE(v_pdpages, VMSTATROW + 9, VMSTATCOL, 8);
- PUTRATE(v_intrans, VMSTATROW + 10, VMSTATCOL, 8);
- putint(pgtokb(s.v_wire_count), VMSTATROW + 11, VMSTATCOL, 8);
- putint(pgtokb(s.v_active_count), VMSTATROW + 12, VMSTATCOL, 8);
- putint(pgtokb(s.v_inactive_count), VMSTATROW + 13, VMSTATCOL, 8);
- putint(pgtokb(s.v_cache_count), VMSTATROW + 14, VMSTATCOL, 8);
- putint(pgtokb(s.v_free_count), VMSTATROW + 15, VMSTATCOL, 8);
- if (LINES - 1 > VMSTATROW + 16)
- putint(s.bufspace / 1024, VMSTATROW + 16, VMSTATCOL, 8);
+ VMSTATROW + 4, VMSTATCOL + 1, 8 - 1);
+ PUTRATE(v_dfree, VMSTATROW + 5, VMSTATCOL + 2, 8 - 2);
+ PUTRATE(v_pfree, VMSTATROW + 6, VMSTATCOL + 2, 8 - 2);
+ PUTRATE(v_tfree, VMSTATROW + 7, VMSTATCOL, 8);
+ PUTRATE(v_reactivated, VMSTATROW + 8, VMSTATCOL, 8);
+ PUTRATE(v_pdwakeups, VMSTATROW + 9, VMSTATCOL, 8);
+ PUTRATE(v_pdpages, VMSTATROW + 10, VMSTATCOL, 8);
+ PUTRATE(v_intrans, VMSTATROW + 11, VMSTATCOL, 8);
+ putint(pgtokb(s.v_wire_count), VMSTATROW + 12, VMSTATCOL, 8);
+ putint(pgtokb(s.v_active_count), VMSTATROW + 13, VMSTATCOL, 8);
+ putint(pgtokb(s.v_inactive_count), VMSTATROW + 14, VMSTATCOL, 8);
+ putint(pgtokb(s.v_cache_count), VMSTATROW + 15, VMSTATCOL, 8);
+ putint(pgtokb(s.v_free_count), VMSTATROW + 16, VMSTATCOL, 8);
+ if (LINES - 1 > VMSTATROW + 17)
+ putint(s.bufspace / 1024, VMSTATROW + 17, VMSTATCOL, 8);
PUTRATE(v_vnodein, PAGEROW + 2, PAGECOL + 6, 5);
PUTRATE(v_vnodeout, PAGEROW + 2, PAGECOL + 12, 5);
PUTRATE(v_swapin, PAGEROW + 2, PAGECOL + 19, 5);
@@ -751,6 +754,7 @@ getinfo(struct Info *ls)
GETSYSCTL("vm.stats.sys.v_intr", ls->v_intr);
GETSYSCTL("vm.stats.sys.v_soft", ls->v_soft);
GETSYSCTL("vm.stats.vm.v_vm_faults", ls->v_vm_faults);
+ GETSYSCTL("vm.stats.vm.v_io_faults", ls->v_io_faults);
GETSYSCTL("vm.stats.vm.v_cow_faults", ls->v_cow_faults);
GETSYSCTL("vm.stats.vm.v_zfod", ls->v_zfod);
GETSYSCTL("vm.stats.vm.v_ozfod", ls->v_ozfod);
diff --git a/usr.bin/tr/tr.c b/usr.bin/tr/tr.c
index c2b6bf8..6eea2cb 100644
--- a/usr.bin/tr/tr.c
+++ b/usr.bin/tr/tr.c
@@ -47,6 +47,7 @@ static const char sccsid[] = "@(#)tr.c 8.2 (Berkeley) 5/4/95";
#include <err.h>
#include <limits.h>
#include <locale.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -267,7 +268,7 @@ endloop:
*/
s2.str = argv[1];
s2.state = NORMAL;
- for (cnt = 0; cnt < WCHAR_MAX; cnt++) {
+ for (cnt = 0; cnt < WINT_MAX; cnt++) {
if (Cflag && !iswrune(cnt))
continue;
if (cmap_lookup(map, cnt) == OOBCH) {
diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c
index 02c6930..4e9f74b 100644
--- a/usr.bin/truss/main.c
+++ b/usr.bin/truss/main.c
@@ -234,15 +234,12 @@ main(int ac, char **av)
usage();
if (fname != NULL) { /* Use output file */
- if ((trussinfo->outfile = fopen(fname, "w")) == NULL)
- errx(1, "cannot open %s", fname);
/*
- * Set FD_CLOEXEC, so that the output file is not shared with
- * the traced process.
+ * Set close-on-exec ('e'), so that the output file is not
+ * shared with the traced process.
*/
- if (fcntl(fileno(trussinfo->outfile), F_SETFD, FD_CLOEXEC) ==
- -1)
- warn("fcntl()");
+ if ((trussinfo->outfile = fopen(fname, "we")) == NULL)
+ err(1, "cannot open %s", fname);
}
/*
diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c
index fe3a29c..c089dbf 100644
--- a/usr.bin/vmstat/vmstat.c
+++ b/usr.bin/vmstat/vmstat.c
@@ -469,6 +469,7 @@ fill_vmmeter(struct vmmeter *vmmp)
ADD_FROM_PCPU(i, v_intr);
ADD_FROM_PCPU(i, v_soft);
ADD_FROM_PCPU(i, v_vm_faults);
+ ADD_FROM_PCPU(i, v_io_faults);
ADD_FROM_PCPU(i, v_cow_faults);
ADD_FROM_PCPU(i, v_cow_optim);
ADD_FROM_PCPU(i, v_zfod);
@@ -507,6 +508,7 @@ fill_vmmeter(struct vmmeter *vmmp)
/* vm */
GET_VM_STATS(vm, v_vm_faults);
+ GET_VM_STATS(vm, v_io_faults);
GET_VM_STATS(vm, v_cow_faults);
GET_VM_STATS(vm, v_cow_optim);
GET_VM_STATS(vm, v_zfod);
@@ -968,6 +970,7 @@ dosum(void)
(void)printf("%9u zero fill pages prezeroed\n", sum.v_ozfod);
(void)printf("%9u intransit blocking page faults\n", sum.v_intrans);
(void)printf("%9u total VM faults taken\n", sum.v_vm_faults);
+ (void)printf("%9u page faults requiring I/O\n", sum.v_io_faults);
(void)printf("%9u pages affected by kernel thread creation\n", sum.v_kthreadpages);
(void)printf("%9u pages affected by fork()\n", sum.v_forkpages);
(void)printf("%9u pages affected by vfork()\n", sum.v_vforkpages);
diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c
index 2e19bb1..1b9af5f 100644
--- a/usr.bin/w/w.c
+++ b/usr.bin/w/w.c
@@ -123,7 +123,7 @@ static struct stat *ttystat(char *);
static void usage(int);
static int this_is_uptime(const char *s);
-char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
+char *fmt_argv(char **, char *, char *, size_t); /* ../../bin/ps/fmt.c */
int
main(int argc, char *argv[])
@@ -320,7 +320,7 @@ main(int argc, char *argv[])
continue;
}
ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
- ep->kp->ki_comm, MAXCOMLEN);
+ ep->kp->ki_comm, NULL, MAXCOMLEN);
if (ep->args == NULL)
err(1, NULL);
}
@@ -404,7 +404,7 @@ main(int argc, char *argv[])
const char *ptr;
ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
- dkp->ki_comm, MAXCOMLEN);
+ dkp->ki_comm, NULL, MAXCOMLEN);
if (ptr == NULL)
ptr = "-";
(void)printf("\t\t%-9d %s\n",
diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile
index e6ff88e..d9d5873 100644
--- a/usr.bin/xinstall/Makefile
+++ b/usr.bin/xinstall/Makefile
@@ -3,6 +3,14 @@
PROG= xinstall
PROGNAME= install
+SRCS= xinstall.c getid.c
MAN= install.1
+.PATH: ${.CURDIR}/../../contrib/mtree
+CFLAGS+= -I${.CURDIR}/../../contrib/mtree
+CFLAGS+= -I${.CURDIR}/../../lib/libnetbsd
+
+DPADD+= ${LIBUTIL} ${LIBMD}
+LDADD+= -lutil -lmd
+
.include <bsd.prog.mk>
diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1
index 1c7c415..6fc038e 100644
--- a/usr.bin/xinstall/install.1
+++ b/usr.bin/xinstall/install.1
@@ -28,7 +28,7 @@
.\" From: @(#)install.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
-.Dd March 6, 2006
+.Dd January 18, 2013
.Dt INSTALL 1
.Os
.Sh NAME
@@ -36,31 +36,50 @@
.Nd install binaries
.Sh SYNOPSIS
.Nm
-.Op Fl bCcMpSsv
+.Op Fl bCcpSsUv
.Op Fl B Ar suffix
+.Op Fl D Ar destdir
.Op Fl f Ar flags
.Op Fl g Ar group
+.Op Fl h Ar hash
+.Op Fl l Ar linkflags
+.Op Fl M Ar metalog
.Op Fl m Ar mode
+.Op Fl N Ar dbdir
.Op Fl o Ar owner
+.Op Fl T Ar tags
.Ar file1 file2
.Nm
-.Op Fl bCcMpSsv
+.Op Fl bCcpSsUv
.Op Fl B Ar suffix
+.Op Fl D Ar destdir
.Op Fl f Ar flags
.Op Fl g Ar group
+.Op Fl h Ar hash
+.Op Fl l Ar linkflags
+.Op Fl M Ar metalog
.Op Fl m Ar mode
+.Op Fl N Ar dbdir
.Op Fl o Ar owner
+.Op Fl T Ar tags
.Ar file1 ... fileN directory
.Nm
.Fl d
-.Op Fl v
+.Op Fl Uv
+.Op Fl D Ar destdir
.Op Fl g Ar group
+.Op Fl h Ar hash
+.Op Fl M Ar metalog
.Op Fl m Ar mode
+.Op Fl N Ar dbdir
.Op Fl o Ar owner
+.Op Fl T Ar tags
.Ar directory ...
.Sh DESCRIPTION
The file(s) are copied
-to the target file or directory.
+(or linked if the
+.Fl l
+option is specified) to the target file or directory.
If the destination is a directory, then the
.Ar file
is copied into
@@ -105,6 +124,17 @@ This is actually the default.
The
.Fl c
option is only included for backwards compatibility.
+.It Fl D Ar destdir
+Specify the
+.Ev DESTDIR
+(top of the file hierarchy) that the items are installed in to.
+If
+.Fl M Ar metalog
+is in use, a leading string of
+.Dq Ar destdir
+will be removed from the file names logged to the
+.Ar metalog .
+This option does not affect where the actual files are installed.
.It Fl d
Create directories.
Missing parent directories are created as required.
@@ -115,15 +145,82 @@ for a list of possible flags and their meanings.
.It Fl g
Specify a group.
A numeric GID is allowed.
-.It Fl M
-Disable all use of
-.Xr mmap 2 .
+.It Fl h Ar hash
+When copying, calculate the digest of the files with
+.Ar hash
+to store in the
+.Fl M Ar metalog .
+When
+.Fl d
+is given no hash is emitted.
+Supported digests:
+.Bl -tag -width rmd160 -offset indent
+.It Sy none
+No hash.
+This is the default.
+.It Sy md5
+The MD5 cryptographic message digest.
+.It Sy rmd160
+The RMD-160 cryptographic message digest.
+.It Sy sha1
+The SHA-1 cryptographic message digest.
+.It Sy sha256
+The 256-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha512
+The 512-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.El
+.It Fl l Ar linkflags
+Instead of copying the file make a link to the source.
+The type of the link is determined by the
+.Ar linkflags
+argument.
+Valid
+.Ar linkflags
+are:
+.Ar a
+(absolute),
+.Ar r
+(relative),
+.Ar h
+(hard),
+.Ar s
+(symbolic),
+.Ar m
+(mixed).
+Absolute and relative have effect only for symbolic links.
+Mixed links
+are hard links for files on the same filesystem, symbolic otherwise.
+.It Fl M Ar metalog
+Write the metadata associated with each item installed to
+.Ar metalog
+in an
+.Xr mtree 8
+.Dq full path
+specification line.
+The metadata includes: the file name and file type, and depending upon
+other options, the owner, group, file flags, modification time, and tags.
.It Fl m
Specify an alternate mode.
The default mode is set to rwxr-xr-x (0755).
The specified mode may be either an octal or symbolic value; see
.Xr chmod 1
for a description of possible mode values.
+.It Fl N
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar dbdir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
.It Fl o
Specify an owner.
A numeric UID is allowed.
@@ -156,6 +253,17 @@ number of systems and binary types.
See below for how
.Nm
can be instructed to use another program to strip binaries.
+.It Fl T Ar tags
+Specify the
+.Xr mtree 8
+tags to write out for the file when using
+.Fl M Ar metalog .
+.It Fl U
+Indicate that install is running unprivileged, and that it should not
+try to change the owner, the group, or the file flags of the destination.
+The information that would have been updated can be stored in a log
+file with
+.Fl M Ar metalog .
.It Fl v
Cause
.Nm
@@ -231,6 +339,8 @@ The default was changed to copy in
.Xr mv 1 ,
.Xr strip 1 ,
.Xr mmap 2 ,
+.Xr getgrnam 3 ,
+.Xr getpwnam 3 ,
.Xr chown 8
.Sh HISTORY
The
@@ -238,6 +348,16 @@ The
utility appeared in
.Bx 4.2 .
.Sh BUGS
+The meaning of the
+.Fl M
+option has changed as of
+.Fx 10
+and it now takes an argument.
+Command lines that used the old
+.Fl M
+will get an error or in rare cases will append logs to the first of
+multiple source files rather than installing it.
+.Pp
Temporary files may be left in the target directory if
.Nm
exits abnormally.
diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c
index 583348a..15b115a 100644
--- a/usr.bin/xinstall/xinstall.c
+++ b/usr.bin/xinstall/xinstall.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2012, 2013 SRI International
* Copyright (c) 1987, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -53,14 +54,23 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
+#include <libgen.h>
+#include <md5.h>
#include <paths.h>
#include <pwd.h>
+#include <ripemd.h>
+#include <sha.h>
+#include <sha256.h>
+#include <sha512.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
+#include <vis.h>
+
+#include "mtree.h"
/* Bootstrap aid - this doesn't exist in most older releases */
#ifndef MAP_FAILED
@@ -69,27 +79,63 @@ __FBSDID("$FreeBSD$");
#define MAX_CMP_SIZE (16 * 1024 * 1024)
+#define LN_ABSOLUTE 0x01
+#define LN_RELATIVE 0x02
+#define LN_HARD 0x04
+#define LN_SYMBOLIC 0x08
+#define LN_MIXED 0x10
+
#define DIRECTORY 0x01 /* Tell install it's a directory. */
#define SETFLAGS 0x02 /* Tell install to set flags. */
#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
#define BACKUP_SUFFIX ".old"
-static struct passwd *pp;
-static struct group *gp;
+typedef union {
+ MD5_CTX MD5;
+ RIPEMD160_CTX RIPEMD160;
+ SHA1_CTX SHA1;
+ SHA256_CTX SHA256;
+ SHA512_CTX SHA512;
+} DIGEST_CTX;
+
+static enum {
+ DIGEST_NONE = 0,
+ DIGEST_MD5,
+ DIGEST_RIPEMD160,
+ DIGEST_SHA1,
+ DIGEST_SHA256,
+ DIGEST_SHA512,
+} digesttype = DIGEST_NONE;
+
static gid_t gid;
static uid_t uid;
-static int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy,
- verbose;
+static int dobackup, docompare, dodir, dolink, dopreserve, dostrip, dounpriv,
+ safecopy, verbose;
+static int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+static FILE *metafp;
+static const char *group, *owner;
static const char *suffix = BACKUP_SUFFIX;
+static char *destdir, *digest, *fflags, *metafile, *tags;
-static int compare(int, const char *, size_t, int, const char *, size_t);
-static void copy(int, const char *, int, const char *, off_t);
+static int compare(int, const char *, size_t, int, const char *, size_t,
+ char **);
+static char *copy(int, const char *, int, const char *, off_t);
static int create_newfile(const char *, int, struct stat *);
static int create_tempfile(const char *, char *, size_t);
+static char *quiet_mktemp(char *template);
+static char *digest_file(const char *);
+static void digest_init(DIGEST_CTX *);
+static void digest_update(DIGEST_CTX *, const unsigned char *, size_t);
+static char *digest_end(DIGEST_CTX *, char *);
+static int do_link(const char *, const char *, const struct stat *);
+static void do_symlink(const char *, const char *, const struct stat *);
+static void makelink(const char *, const char *, const struct stat *);
static void install(const char *, const char *, u_long, u_int);
static void install_dir(char *);
-static u_long numeric_id(const char *, const char *);
+static void metadata_log(const char *, const char *, struct timeval *,
+ const char *, const char *, off_t);
+static int parseid(const char *, id_t *);
static void strip(const char *);
static int trymmap(int);
static void usage(void);
@@ -102,12 +148,13 @@ main(int argc, char *argv[])
u_long fset;
int ch, no_target;
u_int iflags;
- char *flags;
- const char *group, *owner, *to_name;
+ char *p;
+ const char *to_name;
iflags = 0;
group = owner = NULL;
- while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1)
+ while ((ch = getopt(argc, argv, "B:bCcD:df:g:h:l:M:m:N:o:pSsT:Uv")) !=
+ -1)
switch((char)ch) {
case 'B':
suffix = optarg;
@@ -121,29 +168,69 @@ main(int argc, char *argv[])
case 'c':
/* For backwards compatibility. */
break;
+ case 'D':
+ destdir = optarg;
+ break;
case 'd':
dodir = 1;
break;
case 'f':
- flags = optarg;
- if (strtofflags(&flags, &fset, NULL))
- errx(EX_USAGE, "%s: invalid flag", flags);
- iflags |= SETFLAGS;
+ haveopt_f = 1;
+ fflags = optarg;
break;
case 'g':
+ haveopt_g = 1;
group = optarg;
break;
+ case 'h':
+ digest = optarg;
+ break;
+ case 'l':
+ for (p = optarg; *p != '\0'; p++)
+ switch (*p) {
+ case 's':
+ dolink &= ~(LN_HARD|LN_MIXED);
+ dolink |= LN_SYMBOLIC;
+ break;
+ case 'h':
+ dolink &= ~(LN_SYMBOLIC|LN_MIXED);
+ dolink |= LN_HARD;
+ break;
+ case 'm':
+ dolink &= ~(LN_SYMBOLIC|LN_HARD);
+ dolink |= LN_MIXED;
+ break;
+ case 'a':
+ dolink &= ~LN_RELATIVE;
+ dolink |= LN_ABSOLUTE;
+ break;
+ case 'r':
+ dolink &= ~LN_ABSOLUTE;
+ dolink |= LN_RELATIVE;
+ break;
+ default:
+ errx(1, "%c: invalid link type", *p);
+ /* NOTREACHED */
+ }
+ break;
case 'M':
- nommap = 1;
+ metafile = optarg;
break;
case 'm':
+ haveopt_m = 1;
if (!(set = setmode(optarg)))
errx(EX_USAGE, "invalid file mode: %s",
optarg);
mode = getmode(set, 0);
free(set);
break;
+ case 'N':
+ if (!setup_getid(optarg))
+ err(EX_OSERR, "Unable to use user and group "
+ "databases in `%s'", optarg);
+ break;
case 'o':
+ haveopt_o = 1;
owner = optarg;
break;
case 'p':
@@ -155,6 +242,12 @@ main(int argc, char *argv[])
case 's':
dostrip = 1;
break;
+ case 'T':
+ tags = optarg;
+ break;
+ case 'U':
+ dounpriv = 1;
+ break;
case 'v':
verbose = 1;
break;
@@ -180,27 +273,62 @@ main(int argc, char *argv[])
if (argc == 0 || (argc == 1 && !dodir))
usage();
+ if (digest != NULL) {
+ if (strcmp(digest, "none") == 0) {
+ digesttype = DIGEST_NONE;
+ } else if (strcmp(digest, "md5") == 0) {
+ digesttype = DIGEST_MD5;
+ } else if (strcmp(digest, "rmd160") == 0) {
+ digesttype = DIGEST_RIPEMD160;
+ } else if (strcmp(digest, "sha1") == 0) {
+ digesttype = DIGEST_SHA1;
+ } else if (strcmp(digest, "sha256") == 0) {
+ digesttype = DIGEST_SHA256;
+ } else if (strcmp(digest, "sha512") == 0) {
+ digesttype = DIGEST_SHA512;
+ } else {
+ warnx("unknown digest `%s'", digest);
+ usage();
+ }
+ }
+
/* need to make a temp copy so we can compare stripped version */
if (docompare && dostrip)
safecopy = 1;
/* get group and owner id's */
- if (group != NULL) {
- if ((gp = getgrnam(group)) != NULL)
- gid = gp->gr_gid;
- else
- gid = (gid_t)numeric_id(group, "group");
+ if (group != NULL && !dounpriv) {
+ if (gid_from_group(group, &gid) == -1) {
+ id_t id;
+ if (!parseid(group, &id))
+ errx(1, "unknown group %s", group);
+ gid = id;
+ }
} else
gid = (gid_t)-1;
- if (owner != NULL) {
- if ((pp = getpwnam(owner)) != NULL)
- uid = pp->pw_uid;
- else
- uid = (uid_t)numeric_id(owner, "user");
+ if (owner != NULL && !dounpriv) {
+ if (uid_from_user(owner, &uid) == -1) {
+ id_t id;
+ if (!parseid(owner, &id))
+ errx(1, "unknown user %s", owner);
+ uid = id;
+ }
} else
uid = (uid_t)-1;
+ if (fflags != NULL && !dounpriv) {
+ if (strtofflags(&fflags, &fset, NULL))
+ errx(EX_USAGE, "%s: invalid flag", fflags);
+ iflags |= SETFLAGS;
+ }
+
+ if (metafile != NULL) {
+ if ((metafp = fopen(metafile, "a")) == NULL)
+ warn("open %s", metafile);
+ } else
+ digesttype = DIGEST_NONE;
+
if (dodir) {
for (; *argv != NULL; ++argv)
install_dir(*argv);
@@ -208,8 +336,21 @@ main(int argc, char *argv[])
/* NOTREACHED */
}
- no_target = stat(to_name = argv[argc - 1], &to_sb);
+ to_name = argv[argc - 1];
+ no_target = stat(to_name, &to_sb);
if (!no_target && S_ISDIR(to_sb.st_mode)) {
+ if (dolink & LN_SYMBOLIC) {
+ if (lstat(to_name, &to_sb) != 0)
+ err(EX_OSERR, "%s vanished", to_name);
+ if (S_ISLNK(to_sb.st_mode)) {
+ if (argc != 2) {
+ errno = ENOTDIR;
+ err(EX_USAGE, "%s", to_name);
+ }
+ install(*argv, to_name, fset, iflags);
+ exit(EX_OK);
+ }
+ }
for (; *argv != to_name; ++argv)
install(*argv, to_name, fset, iflags | DIRECTORY);
exit(EX_OK);
@@ -227,7 +368,7 @@ main(int argc, char *argv[])
usage();
}
- if (!no_target) {
+ if (!no_target && !dolink) {
if (stat(*argv, &from_sb))
err(EX_OSERR, "%s", *argv);
if (!S_ISREG(to_sb.st_mode)) {
@@ -244,23 +385,327 @@ main(int argc, char *argv[])
/* NOTREACHED */
}
-static u_long
-numeric_id(const char *name, const char *type)
+static char *
+digest_file(const char *name)
+{
+
+ switch (digesttype) {
+ case DIGEST_MD5:
+ return (MD5File(name, NULL));
+ case DIGEST_RIPEMD160:
+ return (RIPEMD160_File(name, NULL));
+ case DIGEST_SHA1:
+ return (SHA1_File(name, NULL));
+ case DIGEST_SHA256:
+ return (SHA256_File(name, NULL));
+ case DIGEST_SHA512:
+ return (SHA512_File(name, NULL));
+ default:
+ return (NULL);
+ }
+}
+
+static void
+digest_init(DIGEST_CTX *c)
+{
+
+ switch (digesttype) {
+ case DIGEST_NONE:
+ break;
+ case DIGEST_MD5:
+ MD5Init(&(c->MD5));
+ break;
+ case DIGEST_RIPEMD160:
+ RIPEMD160_Init(&(c->RIPEMD160));
+ break;
+ case DIGEST_SHA1:
+ SHA1_Init(&(c->SHA1));
+ break;
+ case DIGEST_SHA256:
+ SHA256_Init(&(c->SHA256));
+ break;
+ case DIGEST_SHA512:
+ SHA512_Init(&(c->SHA512));
+ break;
+ }
+}
+
+static void
+digest_update(DIGEST_CTX *c, const unsigned char *data, size_t len)
+{
+
+ switch (digesttype) {
+ case DIGEST_NONE:
+ break;
+ case DIGEST_MD5:
+ MD5Update(&(c->MD5), data, len);
+ break;
+ case DIGEST_RIPEMD160:
+ RIPEMD160_Update(&(c->RIPEMD160), data, len);
+ break;
+ case DIGEST_SHA1:
+ SHA1_Update(&(c->SHA1), data, len);
+ break;
+ case DIGEST_SHA256:
+ SHA256_Update(&(c->SHA256), data, len);
+ break;
+ case DIGEST_SHA512:
+ SHA512_Update(&(c->SHA512), data, len);
+ break;
+ }
+}
+
+static char *
+digest_end(DIGEST_CTX *c, char *buf)
+{
+
+ switch (digesttype) {
+ case DIGEST_MD5:
+ return (MD5End(&(c->MD5), buf));
+ case DIGEST_RIPEMD160:
+ return (RIPEMD160_End(&(c->RIPEMD160), buf));
+ case DIGEST_SHA1:
+ return (SHA1_End(&(c->SHA1), buf));
+ case DIGEST_SHA256:
+ return (SHA256_End(&(c->SHA256), buf));
+ case DIGEST_SHA512:
+ return (SHA512_End(&(c->SHA512), buf));
+ default:
+ return (NULL);
+ }
+}
+
+/*
+ * parseid --
+ * parse uid or gid from arg into id, returning non-zero if successful
+ */
+static int
+parseid(const char *name, id_t *id)
+{
+ char *ep;
+ errno = 0;
+ *id = (id_t)strtoul(name, &ep, 10);
+ if (errno || *ep != '\0')
+ return (0);
+ return (1);
+}
+
+/*
+ * quiet_mktemp --
+ * mktemp implementation used mkstemp to avoid mktemp warnings. We
+ * really do need mktemp semantics here as we will be creating a link.
+ */
+static char *
+quiet_mktemp(char *template)
+{
+ int fd;
+
+ if ((fd = mkstemp(template)) == -1)
+ return (NULL);
+ close (fd);
+ if (unlink(template) == -1)
+ err(EX_OSERR, "unlink %s", template);
+ return (template);
+}
+
+/*
+ * do_link --
+ * make a hard link, obeying dorename if set
+ * return -1 on failure
+ */
+static int
+do_link(const char *from_name, const char *to_name,
+ const struct stat *target_sb)
+{
+ char tmpl[MAXPATHLEN];
+ int ret;
+
+ if (safecopy && target_sb != NULL) {
+ (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+ /* This usage is safe. */
+ if (quiet_mktemp(tmpl) == NULL)
+ err(EX_OSERR, "%s: mktemp", tmpl);
+ ret = link(from_name, tmpl);
+ if (ret == 0) {
+ if (target_sb->st_mode & S_IFDIR && rmdir(to_name) ==
+ -1) {
+ unlink(tmpl);
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (target_sb->st_flags & NOCHANGEBITS)
+ (void)chflags(to_name, target_sb->st_flags &
+ ~NOCHANGEBITS);
+ unlink(to_name);
+ ret = rename(tmpl, to_name);
+ /*
+ * If rename has posix semantics, then the temporary
+ * file may still exist when from_name and to_name point
+ * to the same file, so unlink it unconditionally.
+ */
+ (void)unlink(tmpl);
+ }
+ return (ret);
+ } else
+ return (link(from_name, to_name));
+}
+
+/*
+ * do_symlink --
+ * Make a symbolic link, obeying dorename if set. Exit on failure.
+ */
+static void
+do_symlink(const char *from_name, const char *to_name,
+ const struct stat *target_sb)
{
- u_long val;
- char *ep;
+ char tmpl[MAXPATHLEN];
+
+ if (safecopy && target_sb != NULL) {
+ (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+ /* This usage is safe. */
+ if (quiet_mktemp(tmpl) == NULL)
+ err(EX_OSERR, "%s: mktemp", tmpl);
+
+ if (symlink(from_name, tmpl) == -1)
+ err(EX_OSERR, "symlink %s -> %s", from_name, tmpl);
+
+ if (target_sb->st_mode & S_IFDIR && rmdir(to_name) == -1) {
+ (void)unlink(tmpl);
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (target_sb->st_flags & NOCHANGEBITS)
+ (void)chflags(to_name, target_sb->st_flags &
+ ~NOCHANGEBITS);
+ unlink(to_name);
+
+ if (rename(tmpl, to_name) == -1) {
+ /* Remove temporary link before exiting. */
+ (void)unlink(tmpl);
+ err(EX_OSERR, "%s: rename", to_name);
+ }
+ } else {
+ if (symlink(from_name, to_name) == -1)
+ err(EX_OSERR, "symlink %s -> %s", from_name, to_name);
+ }
+}
+
+/*
+ * makelink --
+ * make a link from source to destination
+ */
+static void
+makelink(const char *from_name, const char *to_name,
+ const struct stat *target_sb)
+{
+ char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
+ struct stat to_sb;
+
+ /* Try hard links first. */
+ if (dolink & (LN_HARD|LN_MIXED)) {
+ if (do_link(from_name, to_name, target_sb) == -1) {
+ if ((dolink & LN_HARD) || errno != EXDEV)
+ err(EX_OSERR, "link %s -> %s", from_name, to_name);
+ } else {
+ if (stat(to_name, &to_sb))
+ err(EX_OSERR, "%s: stat", to_name);
+ if (S_ISREG(to_sb.st_mode)) {
+ /*
+ * XXX: hard links to anything other than
+ * plain files are not metalogged
+ */
+ int omode;
+ const char *oowner, *ogroup;
+ char *offlags;
+ char *dres;
+
+ /*
+ * XXX: use underlying perms, unless
+ * overridden on command line.
+ */
+ omode = mode;
+ if (!haveopt_m)
+ mode = (to_sb.st_mode & 0777);
+ oowner = owner;
+ if (!haveopt_o)
+ owner = NULL;
+ ogroup = group;
+ if (!haveopt_g)
+ group = NULL;
+ offlags = fflags;
+ if (!haveopt_f)
+ fflags = NULL;
+ dres = digest_file(from_name);
+ metadata_log(to_name, "file", NULL, NULL,
+ dres, to_sb.st_size);
+ free(dres);
+ mode = omode;
+ owner = oowner;
+ group = ogroup;
+ fflags = offlags;
+ }
+ return;
+ }
+ }
+
+ /* Symbolic links. */
+ if (dolink & LN_ABSOLUTE) {
+ /* Convert source path to absolute. */
+ if (realpath(from_name, src) == NULL)
+ err(EX_OSERR, "%s: realpath", from_name);
+ do_symlink(src, to_name, target_sb);
+ /* XXX: src may point outside of destdir */
+ metadata_log(to_name, "link", NULL, src, NULL, 0);
+ return;
+ }
+
+ if (dolink & LN_RELATIVE) {
+ char *cp, *d, *s;
+
+ /* Resolve pathnames. */
+ if (realpath(from_name, src) == NULL)
+ err(EX_OSERR, "%s: realpath", from_name);
+
+ /*
+ * The last component of to_name may be a symlink,
+ * so use realpath to resolve only the directory.
+ */
+ cp = dirname(to_name);
+ if (realpath(cp, dst) == NULL)
+ err(EX_OSERR, "%s: realpath", cp);
+ /* .. and add the last component. */
+ if (strcmp(dst, "/") != 0) {
+ if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
+ errx(1, "resolved pathname too long");
+ }
+ cp = basename(to_name);
+ if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
+ errx(1, "resolved pathname too long");
+
+ /* Trim common path components. */
+ for (s = src, d = dst; *s == *d; s++, d++)
+ continue;
+ while (*s != '/')
+ s--, d--;
+
+ /* Count the number of directories we need to backtrack. */
+ for (++d, lnk[0] = '\0'; *d; d++)
+ if (*d == '/')
+ (void)strlcat(lnk, "../", sizeof(lnk));
+
+ (void)strlcat(lnk, ++s, sizeof(lnk));
+
+ do_symlink(lnk, to_name, target_sb);
+ /* XXX: Link may point outside of destdir. */
+ metadata_log(to_name, "link", NULL, lnk, NULL, 0);
+ return;
+ }
/*
- * XXX
- * We know that uid_t's and gid_t's are unsigned longs.
+ * If absolute or relative was not specified, try the names the
+ * user provided.
*/
- errno = 0;
- val = strtoul(name, &ep, 10);
- if (errno)
- err(EX_NOUSER, "%s", name);
- if (*ep != '\0')
- errx(EX_NOUSER, "unknown %s %s", type, name);
- return (val);
+ do_symlink(from_name, to_name, target_sb);
+ /* XXX: from_name may point outside of destdir. */
+ metadata_log(to_name, "link", NULL, from_name, NULL, 0);
}
/*
@@ -275,6 +720,7 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
int devnull, files_match, from_fd, serrno, target;
int tempcopy, temp_fd, to_fd;
char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
+ char *digestresult;
files_match = 0;
from_fd = -1;
@@ -282,11 +728,13 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
/* If try to install NULL file to a directory, fails. */
if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
- if (stat(from_name, &from_sb))
- err(EX_OSERR, "%s", from_name);
- if (!S_ISREG(from_sb.st_mode)) {
- errno = EFTYPE;
- err(EX_OSERR, "%s", from_name);
+ if (!dolink) {
+ if (stat(from_name, &from_sb))
+ err(EX_OSERR, "%s", from_name);
+ if (!S_ISREG(from_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", from_name);
+ }
}
/* Build the target path. */
if (flags & DIRECTORY) {
@@ -300,7 +748,23 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
devnull = 1;
}
- target = stat(to_name, &to_sb) == 0;
+ if (!dolink)
+ target = (stat(to_name, &to_sb) == 0);
+ else
+ target = (lstat(to_name, &to_sb) == 0);
+
+ if (dolink) {
+ if (target && !safecopy) {
+ if (to_sb.st_mode & S_IFDIR && rmdir(to_name) == -1)
+ err(EX_OSERR, "%s", to_name);
+ if (to_sb.st_flags & NOCHANGEBITS)
+ (void)chflags(to_name,
+ to_sb.st_flags & ~NOCHANGEBITS);
+ unlink(to_name);
+ }
+ makelink(from_name, to_name, target ? &to_sb : NULL);
+ return;
+ }
/* Only install to regular files. */
if (target && !S_ISREG(to_sb.st_mode)) {
@@ -324,7 +788,7 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
else
files_match = !(compare(from_fd, from_name,
(size_t)from_sb.st_size, to_fd,
- to_name, (size_t)to_sb.st_size));
+ to_name, (size_t)to_sb.st_size, &digestresult));
/* Close "to" file unless we match. */
if (!files_match)
@@ -346,8 +810,10 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
from_name, to_name);
}
if (!devnull)
- copy(from_fd, from_name, to_fd,
+ digestresult = copy(from_fd, from_name, to_fd,
tempcopy ? tempfile : to_name, from_sb.st_size);
+ else
+ digestresult = NULL;
}
if (dostrip) {
@@ -381,7 +847,8 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
}
if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
- to_name, (size_t)to_sb.st_size) == 0) {
+ to_name, (size_t)to_sb.st_size, &digestresult)
+ == 0) {
/*
* If target has more than one link we need to
* replace it in order to snap the extra links.
@@ -401,6 +868,9 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
}
}
+ if (dostrip && (!docompare || !target))
+ digestresult = digest_file(tempfile);
+
/*
* Move the new file into place if doing a safe copy
* and the files are different (or just not compared).
@@ -464,15 +934,16 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
* Set owner, group, mode for target; do the chown first,
* chown may lose the setuid bits.
*/
- if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
+ if (!dounpriv && ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
(uid != (uid_t)-1 && uid != to_sb.st_uid) ||
- (mode != (to_sb.st_mode & ALLPERMS))) {
+ (mode != (to_sb.st_mode & ALLPERMS)))) {
/* Try to turn off the immutable bits. */
if (to_sb.st_flags & NOCHANGEBITS)
(void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
}
- if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
+ if (!dounpriv &
+ (gid != (gid_t)-1 && gid != to_sb.st_gid) ||
(uid != (uid_t)-1 && uid != to_sb.st_uid))
if (fchown(to_fd, uid, gid) == -1) {
serrno = errno;
@@ -481,13 +952,15 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
err(EX_OSERR,"%s: chown/chgrp", to_name);
}
- if (mode != (to_sb.st_mode & ALLPERMS))
- if (fchmod(to_fd, mode)) {
+ if (mode != (to_sb.st_mode & ALLPERMS)) {
+ if (fchmod(to_fd,
+ dounpriv ? mode & (S_IRWXU|S_IRWXG|S_IRWXO) : mode)) {
serrno = errno;
(void)unlink(to_name);
errno = serrno;
err(EX_OSERR, "%s: chmod", to_name);
}
+ }
/*
* If provided a set of flags, set them, otherwise, preserve the
@@ -496,7 +969,7 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
* trying to turn off UF_NODUMP. If we're trying to set real flags,
* then warn if the fs doesn't support it, otherwise fail.
*/
- if (!devnull && (flags & SETFLAGS ||
+ if (!dounpriv & !devnull && (flags & SETFLAGS ||
(from_sb.st_flags & ~UF_NODUMP) != to_sb.st_flags) &&
fchflags(to_fd,
flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
@@ -515,6 +988,9 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
(void)close(to_fd);
if (!devnull)
(void)close(from_fd);
+
+ metadata_log(to_name, "file", tvb, NULL, digestresult, to_sb.st_size);
+ free(digestresult);
}
/*
@@ -523,29 +999,37 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags)
*/
static int
compare(int from_fd, const char *from_name __unused, size_t from_len,
- int to_fd, const char *to_name __unused, size_t to_len)
+ int to_fd, const char *to_name __unused, size_t to_len,
+ char **dresp)
{
char *p, *q;
int rv;
int done_compare;
+ DIGEST_CTX ctx;
rv = 0;
if (from_len != to_len)
return 1;
if (from_len <= MAX_CMP_SIZE) {
+ if (dresp != NULL)
+ digest_init(&ctx);
done_compare = 0;
if (trymmap(from_fd) && trymmap(to_fd)) {
- p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+ p = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
+ from_fd, (off_t)0);
if (p == (char *)MAP_FAILED)
goto out;
- q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+ q = mmap(NULL, from_len, PROT_READ, MAP_SHARED,
+ to_fd, (off_t)0);
if (q == (char *)MAP_FAILED) {
munmap(p, from_len);
goto out;
}
rv = memcmp(p, q, from_len);
+ if (dresp != NULL)
+ digest_update(&ctx, p, from_len);
munmap(p, from_len);
munmap(q, from_len);
done_compare = 1;
@@ -571,6 +1055,7 @@ compare(int from_fd, const char *from_name __unused, size_t from_len,
rv = 1; /* out of sync */
} else
rv = 1; /* read failure */
+ digest_update(&ctx, buf1, n1);
}
lseek(from_fd, 0, SEEK_SET);
lseek(to_fd, 0, SEEK_SET);
@@ -578,6 +1063,13 @@ compare(int from_fd, const char *from_name __unused, size_t from_len,
} else
rv = 1; /* don't bother in this case */
+ if (dresp != NULL) {
+ if (rv == 0)
+ *dresp = digest_end(&ctx, NULL);
+ else
+ (void)digest_end(&ctx, NULL);
+ }
+
return rv;
}
@@ -648,7 +1140,7 @@ create_newfile(const char *path, int target, struct stat *sbp)
* copy --
* copy from one file to another
*/
-static void
+static char *
copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
off_t size)
{
@@ -656,6 +1148,7 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
int serrno;
char *p, buf[MAXBSIZE];
int done_copy;
+ DIGEST_CTX ctx;
/* Rewind file descriptors. */
if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
@@ -663,6 +1156,8 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
err(EX_OSERR, "lseek: %s", to_name);
+ digest_init(&ctx);
+
/*
* Mmap and write if less than 8M (the limit is so we don't totally
* trash memory on big files. This is really a minor hack, but it
@@ -685,10 +1180,12 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
err(EX_OSERR, "%s", to_name);
}
}
+ digest_update(&ctx, p, size);
+ (void)munmap(p, size);
done_copy = 1;
}
if (!done_copy) {
- while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
+ while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
if ((nw = write(to_fd, buf, nr)) != nr) {
serrno = errno;
(void)unlink(to_name);
@@ -702,6 +1199,8 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
err(EX_OSERR, "%s", to_name);
}
}
+ digest_update(&ctx, buf, nr);
+ }
if (nr != 0) {
serrno = errno;
(void)unlink(to_name);
@@ -709,6 +1208,7 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name,
err(EX_OSERR, "%s", from_name);
}
}
+ return (digest_end(&ctx, NULL));
}
/*
@@ -771,10 +1271,96 @@ install_dir(char *path)
break;
}
- if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
- warn("chown %u:%u %s", uid, gid, path);
- if (chmod(path, mode))
- warn("chmod %o %s", mode, path);
+ if (!dounpriv) {
+ if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&
+ chown(path, uid, gid))
+ warn("chown %u:%u %s", uid, gid, path);
+ /* XXXBED: should we do the chmod in the dounpriv case? */
+ if (chmod(path, mode))
+ warn("chmod %o %s", mode, path);
+ }
+ metadata_log(path, "dir", NULL, NULL, NULL, 0);
+}
+
+/*
+ * metadata_log --
+ * if metafp is not NULL, output mtree(8) full path name and settings to
+ * metafp, to allow permissions to be set correctly by other tools,
+ * or to allow integrity checks to be performed.
+ */
+static void
+metadata_log(const char *path, const char *type, struct timeval *tv,
+ const char *slink, const char *digestresult, off_t size)
+{
+ static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+ const char *p;
+ char *buf;
+ size_t destlen;
+ struct flock metalog_lock;
+
+ if (!metafp)
+ return;
+ /* Buffer for strsvis(3). */
+ buf = (char *)malloc(4 * strlen(path) + 1);
+ if (buf == NULL) {
+ warnx("%s", strerror(ENOMEM));
+ return;
+ }
+
+ /* Lock log file. */
+ metalog_lock.l_start = 0;
+ metalog_lock.l_len = 0;
+ metalog_lock.l_whence = SEEK_SET;
+ metalog_lock.l_type = F_WRLCK;
+ if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
+ warn("can't lock %s", metafile);
+ free(buf);
+ return;
+ }
+
+ /* Remove destdir. */
+ p = path;
+ if (destdir) {
+ destlen = strlen(destdir);
+ if (strncmp(p, destdir, destlen) == 0 &&
+ (p[destlen] == '/' || p[destlen] == '\0'))
+ p += destlen;
+ }
+ while (*p && *p == '/')
+ p++;
+ strsvis(buf, p, VIS_OCTAL, extra);
+ p = buf;
+ /* Print details. */
+ fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type);
+ if (owner)
+ fprintf(metafp, " uname=%s", owner);
+ if (group)
+ fprintf(metafp, " gname=%s", group);
+ fprintf(metafp, " mode=%#o", mode);
+ if (slink) {
+ strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */
+ fprintf(metafp, " link=%s", buf);
+ }
+ if (*type == 'f') /* type=file */
+ fprintf(metafp, " size=%lld", (long long)size);
+ if (tv != NULL && dopreserve)
+ fprintf(metafp, " time=%lld.%ld",
+ (long long)tv[1].tv_sec, (long)tv[1].tv_usec);
+ if (digestresult && digest)
+ fprintf(metafp, " %s=%s", digest, digestresult);
+ if (fflags)
+ fprintf(metafp, " flags=%s", fflags);
+ if (tags)
+ fprintf(metafp, " tags=%s", tags);
+ fputc('\n', metafp);
+ /* Flush line. */
+ fflush(metafp);
+
+ /* Unlock log file. */
+ metalog_lock.l_type = F_UNLCK;
+ if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1)
+ warn("can't unlock %s", metafile);
+ free(buf);
}
/*
@@ -785,11 +1371,17 @@ static void
usage(void)
{
(void)fprintf(stderr,
-"usage: install [-bCcMpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
-" [-o owner] file1 file2\n"
-" install [-bCcMpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n"
-" [-o owner] file1 ... fileN directory\n"
-" install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
+"usage: install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n"
+" [-M log] [-D dest] [-h hash] [-T tags]\n"
+" [-B suffix] [-l linkflags] [-N dbdir]\n"
+" file1 file2\n"
+" install [-bCcpSsUv] [-f flags] [-g group] [-m mode] [-o owner]\n"
+" [-M log] [-D dest] [-h hash] [-T tags]\n"
+" [-B suffix] [-l linkflags] [-N dbdir]\n"
+" file1 ... fileN directory\n"
+" install -dU [-vU] [-g group] [-m mode] [-N dbdir] [-o owner]\n"
+" [-M log] [-D dest] [-h hash] [-T tags]\n"
+" directory ...\n");
exit(EX_USAGE);
/* NOTREACHED */
}
@@ -808,7 +1400,7 @@ trymmap(int fd)
#ifdef MFSNAMELEN
struct statfs stfs;
- if (nommap || fstatfs(fd, &stfs) != 0)
+ if (fstatfs(fd, &stfs) != 0)
return (0);
if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
strcmp(stfs.f_fstypename, "cd9660") == 0)
OpenPOWER on IntegriCloud