summaryrefslogtreecommitdiffstats
path: root/text_cmds
diff options
context:
space:
mode:
Diffstat (limited to 'text_cmds')
-rw-r--r--text_cmds/.upstream_base_commits10
-rw-r--r--text_cmds/banner/banner.682
-rw-r--r--text_cmds/banner/banner.c1186
-rw-r--r--text_cmds/cat/cat.1201
-rw-r--r--text_cmds/cat/cat.c314
-rw-r--r--text_cmds/col/README48
-rw-r--r--text_cmds/col/col.1156
-rw-r--r--text_cmds/col/col.c552
-rw-r--r--text_cmds/colrm/colrm.191
-rw-r--r--text_cmds/colrm/colrm.c146
-rw-r--r--text_cmds/column/column.1101
-rw-r--r--text_cmds/column/column.c334
-rw-r--r--text_cmds/comm/comm.1124
-rw-r--r--text_cmds/comm/comm.c220
-rw-r--r--text_cmds/csplit/csplit.1157
-rw-r--r--text_cmds/csplit/csplit.c467
-rw-r--r--text_cmds/cut/cut.1166
-rw-r--r--text_cmds/cut/cut.c468
-rw-r--r--text_cmds/ed/POSIX101
-rw-r--r--text_cmds/ed/README24
-rw-r--r--text_cmds/ed/buf.c284
-rw-r--r--text_cmds/ed/cbc.c402
-rw-r--r--text_cmds/ed/ed.11004
-rw-r--r--text_cmds/ed/ed.h282
-rw-r--r--text_cmds/ed/glbl.c218
-rw-r--r--text_cmds/ed/io.c341
-rw-r--r--text_cmds/ed/main.c1455
-rw-r--r--text_cmds/ed/re.c132
-rw-r--r--text_cmds/ed/red.11
-rw-r--r--text_cmds/ed/sub.c256
-rw-r--r--text_cmds/ed/test/=.err1
-rw-r--r--text_cmds/ed/test/README32
-rw-r--r--text_cmds/ed/test/TODO15
-rw-r--r--text_cmds/ed/test/a.d5
-rw-r--r--text_cmds/ed/test/a.r8
-rw-r--r--text_cmds/ed/test/a.t9
-rw-r--r--text_cmds/ed/test/a1.err3
-rw-r--r--text_cmds/ed/test/a2.err3
-rw-r--r--text_cmds/ed/test/addr.d9
-rw-r--r--text_cmds/ed/test/addr.r2
-rw-r--r--text_cmds/ed/test/addr.t5
-rw-r--r--text_cmds/ed/test/addr1.err1
-rw-r--r--text_cmds/ed/test/addr2.err1
-rw-r--r--text_cmds/ed/test/ascii.d.uu9
-rw-r--r--text_cmds/ed/test/ascii.r.uu9
-rw-r--r--text_cmds/ed/test/ascii.t0
-rw-r--r--text_cmds/ed/test/bang1.d0
-rw-r--r--text_cmds/ed/test/bang1.err1
-rw-r--r--text_cmds/ed/test/bang1.r1
-rw-r--r--text_cmds/ed/test/bang1.t5
-rw-r--r--text_cmds/ed/test/bang2.err1
-rw-r--r--text_cmds/ed/test/c.d5
-rw-r--r--text_cmds/ed/test/c.r4
-rw-r--r--text_cmds/ed/test/c.t12
-rw-r--r--text_cmds/ed/test/c1.err3
-rw-r--r--text_cmds/ed/test/c2.err3
-rwxr-xr-xtext_cmds/ed/test/ckscripts.sh37
-rw-r--r--text_cmds/ed/test/d.d5
-rw-r--r--text_cmds/ed/test/d.err1
-rw-r--r--text_cmds/ed/test/d.r1
-rw-r--r--text_cmds/ed/test/d.t3
-rw-r--r--text_cmds/ed/test/e1.d1
-rw-r--r--text_cmds/ed/test/e1.err1
-rw-r--r--text_cmds/ed/test/e1.r1
-rw-r--r--text_cmds/ed/test/e1.t1
-rw-r--r--text_cmds/ed/test/e2.d1
-rw-r--r--text_cmds/ed/test/e2.err1
-rw-r--r--text_cmds/ed/test/e2.r1
-rw-r--r--text_cmds/ed/test/e2.t1
-rw-r--r--text_cmds/ed/test/e3.d1
-rw-r--r--text_cmds/ed/test/e3.err1
-rw-r--r--text_cmds/ed/test/e3.r1
-rw-r--r--text_cmds/ed/test/e3.t1
-rw-r--r--text_cmds/ed/test/e4.d1
-rw-r--r--text_cmds/ed/test/e4.r1
-rw-r--r--text_cmds/ed/test/e4.t1
-rw-r--r--text_cmds/ed/test/f1.err1
-rw-r--r--text_cmds/ed/test/f2.err1
-rw-r--r--text_cmds/ed/test/g1.d5
-rw-r--r--text_cmds/ed/test/g1.err1
-rw-r--r--text_cmds/ed/test/g1.r15
-rw-r--r--text_cmds/ed/test/g1.t6
-rw-r--r--text_cmds/ed/test/g2.d5
-rw-r--r--text_cmds/ed/test/g2.err1
-rw-r--r--text_cmds/ed/test/g2.r1
-rw-r--r--text_cmds/ed/test/g2.t2
-rw-r--r--text_cmds/ed/test/g3.d5
-rw-r--r--text_cmds/ed/test/g3.err1
-rw-r--r--text_cmds/ed/test/g3.r5
-rw-r--r--text_cmds/ed/test/g3.t4
-rw-r--r--text_cmds/ed/test/g4.d5
-rw-r--r--text_cmds/ed/test/g4.r7
-rw-r--r--text_cmds/ed/test/g4.t13
-rw-r--r--text_cmds/ed/test/g5.d3
-rw-r--r--text_cmds/ed/test/g5.r9
-rw-r--r--text_cmds/ed/test/g5.t2
-rw-r--r--text_cmds/ed/test/h.err1
-rw-r--r--text_cmds/ed/test/i.d5
-rw-r--r--text_cmds/ed/test/i.r8
-rw-r--r--text_cmds/ed/test/i.t9
-rw-r--r--text_cmds/ed/test/i1.err3
-rw-r--r--text_cmds/ed/test/i2.err3
-rw-r--r--text_cmds/ed/test/i3.err3
-rw-r--r--text_cmds/ed/test/j.d5
-rw-r--r--text_cmds/ed/test/j.r4
-rw-r--r--text_cmds/ed/test/j.t2
-rw-r--r--text_cmds/ed/test/k.d5
-rw-r--r--text_cmds/ed/test/k.r5
-rw-r--r--text_cmds/ed/test/k.t10
-rw-r--r--text_cmds/ed/test/k1.err1
-rw-r--r--text_cmds/ed/test/k2.err1
-rw-r--r--text_cmds/ed/test/k3.err1
-rw-r--r--text_cmds/ed/test/k4.err6
-rw-r--r--text_cmds/ed/test/l.d0
-rw-r--r--text_cmds/ed/test/l.r0
-rw-r--r--text_cmds/ed/test/l.t0
-rw-r--r--text_cmds/ed/test/m.d5
-rw-r--r--text_cmds/ed/test/m.err4
-rw-r--r--text_cmds/ed/test/m.r5
-rw-r--r--text_cmds/ed/test/m.t7
-rwxr-xr-xtext_cmds/ed/test/mkscripts.sh75
-rw-r--r--text_cmds/ed/test/n.d0
-rw-r--r--text_cmds/ed/test/n.r0
-rw-r--r--text_cmds/ed/test/n.t0
-rw-r--r--text_cmds/ed/test/nl.err1
-rw-r--r--text_cmds/ed/test/nl1.d5
-rw-r--r--text_cmds/ed/test/nl1.r8
-rw-r--r--text_cmds/ed/test/nl1.t8
-rw-r--r--text_cmds/ed/test/nl2.d5
-rw-r--r--text_cmds/ed/test/nl2.r6
-rw-r--r--text_cmds/ed/test/nl2.t4
-rw-r--r--text_cmds/ed/test/p.d0
-rw-r--r--text_cmds/ed/test/p.r0
-rw-r--r--text_cmds/ed/test/p.t0
-rw-r--r--text_cmds/ed/test/q.d0
-rw-r--r--text_cmds/ed/test/q.r0
-rw-r--r--text_cmds/ed/test/q.t5
-rw-r--r--text_cmds/ed/test/q1.err1
-rw-r--r--text_cmds/ed/test/r1.d5
-rw-r--r--text_cmds/ed/test/r1.err1
-rw-r--r--text_cmds/ed/test/r1.r7
-rw-r--r--text_cmds/ed/test/r1.t3
-rw-r--r--text_cmds/ed/test/r2.d5
-rw-r--r--text_cmds/ed/test/r2.err1
-rw-r--r--text_cmds/ed/test/r2.r10
-rw-r--r--text_cmds/ed/test/r2.t1
-rw-r--r--text_cmds/ed/test/r3.d1
-rw-r--r--text_cmds/ed/test/r3.r2
-rw-r--r--text_cmds/ed/test/r3.t1
-rw-r--r--text_cmds/ed/test/s1.d5
-rw-r--r--text_cmds/ed/test/s1.err1
-rw-r--r--text_cmds/ed/test/s1.r5
-rw-r--r--text_cmds/ed/test/s1.t6
-rw-r--r--text_cmds/ed/test/s10.err4
-rw-r--r--text_cmds/ed/test/s2.d5
-rw-r--r--text_cmds/ed/test/s2.err4
-rw-r--r--text_cmds/ed/test/s2.r5
-rw-r--r--text_cmds/ed/test/s2.t4
-rw-r--r--text_cmds/ed/test/s3.d0
-rw-r--r--text_cmds/ed/test/s3.err1
-rw-r--r--text_cmds/ed/test/s3.r1
-rw-r--r--text_cmds/ed/test/s3.t6
-rw-r--r--text_cmds/ed/test/s4.err1
-rw-r--r--text_cmds/ed/test/s5.err1
-rw-r--r--text_cmds/ed/test/s6.err1
-rw-r--r--text_cmds/ed/test/s7.err5
-rw-r--r--text_cmds/ed/test/s8.err4
-rw-r--r--text_cmds/ed/test/s9.err4
-rw-r--r--text_cmds/ed/test/t.d5
-rw-r--r--text_cmds/ed/test/t.r16
-rw-r--r--text_cmds/ed/test/t1.d5
-rw-r--r--text_cmds/ed/test/t1.err1
-rw-r--r--text_cmds/ed/test/t1.r16
-rw-r--r--text_cmds/ed/test/t1.t3
-rw-r--r--text_cmds/ed/test/t2.d5
-rw-r--r--text_cmds/ed/test/t2.err1
-rw-r--r--text_cmds/ed/test/t2.r6
-rw-r--r--text_cmds/ed/test/t2.t1
-rw-r--r--text_cmds/ed/test/u.d5
-rw-r--r--text_cmds/ed/test/u.err1
-rw-r--r--text_cmds/ed/test/u.r9
-rw-r--r--text_cmds/ed/test/u.t31
-rw-r--r--text_cmds/ed/test/v.d5
-rw-r--r--text_cmds/ed/test/v.r11
-rw-r--r--text_cmds/ed/test/v.t6
-rw-r--r--text_cmds/ed/test/w.d5
-rw-r--r--text_cmds/ed/test/w.r10
-rw-r--r--text_cmds/ed/test/w.t2
-rw-r--r--text_cmds/ed/test/w1.err1
-rw-r--r--text_cmds/ed/test/w2.err1
-rw-r--r--text_cmds/ed/test/w3.err1
-rw-r--r--text_cmds/ed/test/x.err1
-rw-r--r--text_cmds/ed/test/z.err2
-rw-r--r--text_cmds/ed/undo.c150
-rw-r--r--text_cmds/ee/Changes40
-rw-r--r--text_cmds/ee/Makefile29
-rw-r--r--text_cmds/ee/README.ee119
-rwxr-xr-xtext_cmds/ee/create.make292
-rw-r--r--text_cmds/ee/ee.1543
-rw-r--r--text_cmds/ee/ee.c5348
-rw-r--r--text_cmds/ee/ee.i18n.guide158
-rw-r--r--text_cmds/ee/ee.msg186
-rw-r--r--text_cmds/ee/ee_version.h6
-rwxr-xr-xtext_cmds/ee/genstr32
-rw-r--r--text_cmds/ee/make.default57
-rw-r--r--text_cmds/ee/new_curse.c3819
-rw-r--r--text_cmds/ee/new_curse.h260
-rw-r--r--text_cmds/expand/expand.1118
-rw-r--r--text_cmds/expand/expand.c200
-rw-r--r--text_cmds/expand/xcodescripts/link-man-pages.sh3
-rw-r--r--text_cmds/fmt/fmt.1196
-rw-r--r--text_cmds/fmt/fmt.c668
-rw-r--r--text_cmds/fold/fold.190
-rw-r--r--text_cmds/fold/fold.c228
-rw-r--r--text_cmds/grep/file.c347
-rw-r--r--text_cmds/grep/grep.1500
-rw-r--r--text_cmds/grep/grep.c783
-rw-r--r--text_cmds/grep/grep.h161
-rw-r--r--text_cmds/grep/queue.c107
-rw-r--r--text_cmds/grep/util.c599
-rw-r--r--text_cmds/head/head.169
-rw-r--r--text_cmds/head/head.c199
-rw-r--r--text_cmds/join/join.1235
-rw-r--r--text_cmds/join/join.c685
-rw-r--r--text_cmds/lam/lam.1141
-rw-r--r--text_cmds/lam/lam.c234
-rw-r--r--text_cmds/look/look.1124
-rw-r--r--text_cmds/look/look.c357
-rw-r--r--text_cmds/look/pathnames.h36
-rw-r--r--text_cmds/md5/commoncrypto.c108
-rw-r--r--text_cmds/md5/commoncrypto.h8
-rw-r--r--text_cmds/md5/md5.195
-rw-r--r--text_cmds/md5/md5.c419
-rw-r--r--text_cmds/nl/nl.1253
-rw-r--r--text_cmds/nl/nl.c439
-rw-r--r--text_cmds/paste/paste.1150
-rw-r--r--text_cmds/paste/paste.c280
-rw-r--r--text_cmds/pr/egetopt.c218
-rw-r--r--text_cmds/pr/extern.h61
-rw-r--r--text_cmds/pr/pr.1402
-rw-r--r--text_cmds/pr/pr.c1856
-rw-r--r--text_cmds/pr/pr.h74
-rw-r--r--text_cmds/rev/rev.149
-rw-r--r--text_cmds/rev/rev.c118
-rw-r--r--text_cmds/rs/rs.1239
-rw-r--r--text_cmds/rs/rs.c553
-rw-r--r--text_cmds/sed/POSIX204
-rw-r--r--text_cmds/sed/TEST/hanoi.sed103
-rw-r--r--text_cmds/sed/TEST/math.sed439
-rw-r--r--text_cmds/sed/TEST/sed.test556
-rw-r--r--text_cmds/sed/compile.c987
-rw-r--r--text_cmds/sed/defs.h151
-rw-r--r--text_cmds/sed/extern.h60
-rw-r--r--text_cmds/sed/main.c547
-rw-r--r--text_cmds/sed/misc.c73
-rw-r--r--text_cmds/sed/process.c791
-rw-r--r--text_cmds/sed/sed.1668
-rw-r--r--text_cmds/sort/bwstring.c1142
-rw-r--r--text_cmds/sort/bwstring.h142
-rw-r--r--text_cmds/sort/coll.c1324
-rw-r--r--text_cmds/sort/coll.h168
-rw-r--r--text_cmds/sort/commoncrypto.c98
-rw-r--r--text_cmds/sort/commoncrypto.h8
-rw-r--r--text_cmds/sort/file.c1684
-rw-r--r--text_cmds/sort/file.h126
-rw-r--r--text_cmds/sort/mem.c81
-rw-r--r--text_cmds/sort/mem.h45
-rw-r--r--text_cmds/sort/nls/C.msg16
-rw-r--r--text_cmds/sort/nls/hu_HU.ISO8859-2.msg16
-rw-r--r--text_cmds/sort/radixsort.c746
-rw-r--r--text_cmds/sort/radixsort.h38
-rw-r--r--text_cmds/sort/sort.1.in639
-rw-r--r--text_cmds/sort/sort.c1325
-rw-r--r--text_cmds/sort/sort.h139
-rw-r--r--text_cmds/sort/testsuite/README.txt19
-rw-r--r--text_cmds/sort/testsuite/bigsample.txt.xzbin0 -> 50904 bytes
-rwxr-xr-xtext_cmds/sort/testsuite/run.sh436
-rw-r--r--text_cmds/sort/testsuite/sample.txtbin0 -> 1672 bytes
-rw-r--r--text_cmds/sort/vsort.c265
-rw-r--r--text_cmds/sort/vsort.h37
-rw-r--r--text_cmds/split/split.1142
-rw-r--r--text_cmds/split/split.c342
-rw-r--r--text_cmds/tail/extern.h76
-rw-r--r--text_cmds/tail/forward.c520
-rw-r--r--text_cmds/tail/misc.c119
-rw-r--r--text_cmds/tail/read.c214
-rw-r--r--text_cmds/tail/reverse.c287
-rw-r--r--text_cmds/tail/tail.1188
-rw-r--r--text_cmds/tail/tail.c349
-rw-r--r--text_cmds/tests/Makefile6
-rw-r--r--text_cmds/tests/sort_vers.c23
-rw-r--r--text_cmds/text_cmds.plist607
-rw-r--r--text_cmds/text_cmds.xcodeproj/project.pbxproj3890
-rw-r--r--text_cmds/text_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--text_cmds/tr/cmap.c212
-rw-r--r--text_cmds/tr/cmap.h83
-rw-r--r--text_cmds/tr/cset.c290
-rw-r--r--text_cmds/tr/cset.h74
-rw-r--r--text_cmds/tr/extern.h55
-rw-r--r--text_cmds/tr/str.c450
-rw-r--r--text_cmds/tr/tr.1420
-rw-r--r--text_cmds/tr/tr.c378
-rw-r--r--text_cmds/ul/ul.1104
-rw-r--r--text_cmds/ul/ul.c569
-rw-r--r--text_cmds/unexpand/unexpand.c231
-rw-r--r--text_cmds/uniq/uniq.1151
-rw-r--r--text_cmds/uniq/uniq.c367
-rw-r--r--text_cmds/unvis/unvis.159
-rw-r--r--text_cmds/unvis/unvis.c126
-rw-r--r--text_cmds/vis/extern.h36
-rw-r--r--text_cmds/vis/foldit.c82
-rw-r--r--text_cmds/vis/vis.1140
-rw-r--r--text_cmds/vis/vis.c194
-rw-r--r--text_cmds/wc/wc.1163
-rw-r--r--text_cmds/wc/wc.c294
-rw-r--r--text_cmds/xcconfigs/base.xcconfig59
-rw-r--r--text_cmds/xcconfigs/ee.xcconfig27
-rw-r--r--text_cmds/xcconfigs/grep.xcconfig19
-rw-r--r--text_cmds/xcconfigs/sort.xcconfig15
-rw-r--r--text_cmds/xcodescripts/grep_variant_links.sh13
-rw-r--r--text_cmds/xcodescripts/install-opensource.sh9
-rw-r--r--text_cmds/xcodescripts/install-sort-man.sh3
322 files changed, 56992 insertions, 0 deletions
diff --git a/text_cmds/.upstream_base_commits b/text_cmds/.upstream_base_commits
new file mode 100644
index 0000000..a9f96cf
--- /dev/null
+++ b/text_cmds/.upstream_base_commits
@@ -0,0 +1,10 @@
+#freebsd = https://github.com/freebsd/freebsd.git
+
+sed/POSIX freebsd usr.bin/sed/POSIX 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/compile.c freebsd usr.bin/sed/compile.c 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/defs.h freebsd usr.bin/sed/defs.h 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/extern.h freebsd usr.bin/sed/extern.h 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/main.c freebsd usr.bin/sed/main.c 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/misc.c freebsd usr.bin/sed/misc.c 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/process.c freebsd usr.bin/sed/process.c 3a58a22a0b52d622721ce09df3930ff87174022f
+sed/sed.1 freebsd usr.bin/sed/sed.1 3a58a22a0b52d622721ce09df3930ff87174022f
diff --git a/text_cmds/banner/banner.6 b/text_cmds/banner/banner.6
new file mode 100644
index 0000000..b5e8cf5
--- /dev/null
+++ b/text_cmds/banner/banner.6
@@ -0,0 +1,82 @@
+.\" Copyright (c) 1980, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)banner.6 8.2 (Berkeley) 4/29/95
+.\" $FreeBSD: src/usr.bin/banner/banner.6,v 1.9 2005/01/25 22:24:04 tjr Exp $
+.\"
+.Dd January 26, 2005
+.Dt BANNER 6
+.Os
+.Sh NAME
+.Nm banner
+.Nd print large banner on printer
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl t
+.Op Fl w Ar width
+.Ar message ...
+.Sh DESCRIPTION
+.Nm Banner
+prints a large, high quality banner on the standard output.
+If the message is omitted, it prompts for and reads one line of its
+standard input.
+.Pp
+The output should be printed on paper of the appropriate width,
+with no breaks between the pages.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debug.
+.It Fl t
+Enable trace.
+.It Fl w Ar width
+Change the output from a width of 132 to
+.Ar width ,
+suitable for a narrow terminal.
+.El
+.Sh AUTHORS
+.An Mark Horton
+.Sh BUGS
+Several
+.Tn ASCII
+characters are not defined, notably <, >, [, ], \\,
+^, _, {, }, |, and ~.
+Also, the characters ", ', and & are funny looking (but in a useful way.)
+.Pp
+The
+.Fl w
+option is implemented by skipping some rows and columns.
+The smaller it gets, the grainier the output.
+Sometimes it runs letters together.
+.Pp
+Messages are limited to 1024 characters in length.
diff --git a/text_cmds/banner/banner.c b/text_cmds/banner/banner.c
new file mode 100644
index 0000000..8a0286c
--- /dev/null
+++ b/text_cmds/banner/banner.c
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (c) 1980, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)banner.c 8.4 (Berkeley) 4/29/95";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/banner/banner.c,v 1.15 2002/03/22 01:19:22 imp Exp $");
+
+/*
+ * banner - prints large signs
+ * banner [-w#] [-d] [-t] message ...
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXMSG 1024
+#define DWIDTH 132
+#define NCHARS 128
+#define NBYTES 9271
+
+/* Pointers into data_table for each ASCII char */
+const int asc_ptr[NCHARS] = {
+/* ^@ */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^H */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^P */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^X */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* */ 1, 3, 50, 81, 104, 281, 483, 590,
+/* ( */ 621, 685, 749, 851, 862, 893, 898, 921,
+/* 0 */1019, 1150, 1200, 1419, 1599, 1744, 1934, 2111,
+/* 8 */2235, 2445, 2622, 2659, 0, 2708, 0, 2715,
+/* @ */2857, 3072, 3273, 3403, 3560, 3662, 3730, 3785,
+/* H */3965, 4000, 4015, 4115, 4281, 4314, 4432, 4548,
+/* P */4709, 4790, 4999, 5188, 5397, 5448, 5576, 5710,
+/* X */5892, 6106, 6257, 0, 0, 0, 0, 0,
+/* ` */ 50, 6503, 6642, 6733, 6837, 6930, 7073, 7157,
+/* h */7380, 7452, 7499, 7584, 7689, 7702, 7797, 7869,
+/* p */7978, 8069, 8160, 8222, 8381, 8442, 8508, 8605,
+/* x */8732, 8888, 9016, 0, 0, 0, 0, 0
+};
+
+/*
+ * Table of stuff to print. Format:
+ * 128+n -> print current line n times.
+ * 64+n -> this is last byte of char.
+ * else, put m chars at position n (where m
+ * is the next elt in array) and goto second
+ * next element in array.
+ */
+const unsigned char data_table[NBYTES] = {
+/* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 129, 227, 130, 34, 6, 90, 19, 129, 32, 10,
+/* 10 */ 74, 40, 129, 31, 12, 64, 53, 129, 30, 14,
+/* 20 */ 54, 65, 129, 30, 14, 53, 67, 129, 30, 14,
+/* 30 */ 54, 65, 129, 31, 12, 64, 53, 129, 32, 10,
+/* 40 */ 74, 40, 129, 34, 6, 90, 19, 129, 194, 130,
+/* 50 */ 99, 9, 129, 97, 14, 129, 96, 18, 129, 95,
+/* 60 */ 22, 129, 95, 16, 117, 2, 129, 95, 14, 129,
+/* 70 */ 96, 11, 129, 97, 9, 129, 99, 6, 129, 194,
+/* 80 */ 129, 87, 4, 101, 4, 131, 82, 28, 131, 87,
+/* 90 */ 4, 101, 4, 133, 82, 28, 131, 87, 4, 101,
+/* 100 */ 4, 131, 193, 129, 39, 1, 84, 27, 129, 38,
+/* 110 */ 3, 81, 32, 129, 37, 5, 79, 35, 129, 36,
+/* 120 */ 5, 77, 38, 129, 35, 5, 76, 40, 129, 34,
+/* 130 */ 5, 75, 21, 103, 14, 129, 33, 5, 74, 19,
+/* 140 */ 107, 11, 129, 32, 5, 73, 17, 110, 9, 129,
+/* 150 */ 32, 4, 73, 16, 112, 7, 129, 31, 4, 72,
+/* 160 */ 15, 114, 6, 129, 31, 4, 72, 14, 115, 5,
+/* 170 */ 129, 30, 4, 71, 15, 116, 5, 129, 27, 97,
+/* 180 */ 131, 30, 4, 69, 14, 117, 4, 129, 30, 4,
+/* 190 */ 68, 15, 117, 4, 132, 30, 4, 68, 14, 117,
+/* 200 */ 4, 129, 27, 97, 131, 30, 5, 65, 15, 116,
+/* 210 */ 5, 129, 31, 4, 65, 14, 116, 4, 129, 31,
+/* 220 */ 6, 64, 15, 116, 4, 129, 32, 7, 62, 16,
+/* 230 */ 115, 4, 129, 32, 9, 61, 17, 114, 5, 129,
+/* 240 */ 33, 11, 58, 19, 113, 5, 129, 34, 14, 55,
+/* 250 */ 21, 112, 5, 129, 35, 40, 111, 5, 129, 36,
+/* 260 */ 38, 110, 5, 129, 37, 35, 109, 5, 129, 38,
+/* 270 */ 32, 110, 3, 129, 40, 27, 111, 1, 129, 193,
+/* 280 */ 129, 30, 4, 103, 9, 129, 30, 7, 100, 15,
+/* 290 */ 129, 30, 10, 99, 17, 129, 33, 10, 97, 6,
+/* 300 */ 112, 6, 129, 36, 10, 96, 5, 114, 5, 129,
+/* 310 */ 39, 10, 96, 4, 115, 4, 129, 42, 10, 95,
+/* 320 */ 4, 116, 4, 129, 45, 10, 95, 3, 117, 3,
+/* 330 */ 129, 48, 10, 95, 3, 117, 3, 129, 51, 10,
+/* 340 */ 95, 4, 116, 4, 129, 54, 10, 96, 4, 115,
+/* 350 */ 4, 129, 57, 10, 96, 5, 114, 5, 129, 60,
+/* 360 */ 10, 97, 6, 112, 6, 129, 63, 10, 99, 17,
+/* 370 */ 129, 66, 10, 100, 15, 129, 69, 10, 103, 9,
+/* 380 */ 129, 39, 9, 72, 10, 129, 36, 15, 75, 10,
+/* 390 */ 129, 35, 17, 78, 10, 129, 33, 6, 48, 6,
+/* 400 */ 81, 10, 129, 32, 5, 50, 5, 84, 10, 129,
+/* 410 */ 32, 4, 51, 4, 87, 10, 129, 31, 4, 52,
+/* 420 */ 4, 90, 10, 129, 31, 3, 53, 3, 93, 10,
+/* 430 */ 129, 31, 3, 53, 3, 96, 10, 129, 31, 4,
+/* 440 */ 52, 4, 99, 10, 129, 32, 4, 51, 4, 102,
+/* 450 */ 10, 129, 32, 5, 50, 5, 105, 10, 129, 33,
+/* 460 */ 6, 48, 6, 108, 10, 129, 35, 17, 111, 10,
+/* 470 */ 129, 36, 15, 114, 7, 129, 40, 9, 118, 4,
+/* 480 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41,
+/* 490 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44,
+/* 500 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32,
+/* 510 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31,
+/* 520 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30,
+/* 530 */ 5, 79, 5, 129, 20, 74, 132, 30, 4, 80,
+/* 540 */ 4, 129, 31, 3, 79, 4, 129, 31, 4, 79,
+/* 550 */ 4, 129, 32, 3, 78, 4, 129, 32, 4, 76,
+/* 560 */ 6, 129, 33, 4, 74, 7, 129, 34, 4, 72,
+/* 570 */ 8, 129, 35, 5, 72, 7, 129, 37, 5, 73,
+/* 580 */ 4, 129, 39, 4, 74, 1, 129, 129, 193, 130,
+/* 590 */ 111, 6, 129, 109, 10, 129, 108, 12, 129, 107,
+/* 600 */ 14, 129, 97, 2, 105, 16, 129, 99, 22, 129,
+/* 610 */ 102, 18, 129, 105, 14, 129, 108, 9, 129, 194,
+/* 620 */ 130, 63, 25, 129, 57, 37, 129, 52, 47, 129,
+/* 630 */ 48, 55, 129, 44, 63, 129, 41, 69, 129, 38,
+/* 640 */ 75, 129, 36, 79, 129, 34, 83, 129, 33, 28,
+/* 650 */ 90, 28, 129, 32, 23, 96, 23, 129, 32, 17,
+/* 660 */ 102, 17, 129, 31, 13, 107, 13, 129, 30, 9,
+/* 670 */ 112, 9, 129, 30, 5, 116, 5, 129, 30, 1,
+/* 680 */ 120, 1, 129, 194, 130, 30, 1, 120, 1, 129,
+/* 690 */ 30, 5, 116, 5, 129, 30, 9, 112, 9, 129,
+/* 700 */ 31, 13, 107, 13, 129, 32, 17, 102, 17, 129,
+/* 710 */ 32, 23, 96, 23, 129, 33, 28, 90, 28, 129,
+/* 720 */ 34, 83, 129, 36, 79, 129, 38, 75, 129, 41,
+/* 730 */ 69, 129, 44, 63, 129, 48, 55, 129, 52, 47,
+/* 740 */ 129, 57, 37, 129, 63, 25, 129, 194, 129, 80,
+/* 750 */ 4, 130, 80, 4, 129, 68, 2, 80, 4, 94,
+/* 760 */ 2, 129, 66, 6, 80, 4, 92, 6, 129, 67,
+/* 770 */ 7, 80, 4, 90, 7, 129, 69, 7, 80, 4,
+/* 780 */ 88, 7, 129, 71, 6, 80, 4, 87, 6, 129,
+/* 790 */ 72, 20, 129, 74, 16, 129, 76, 12, 129, 62,
+/* 800 */ 40, 131, 76, 12, 129, 74, 16, 129, 72, 20,
+/* 810 */ 129, 71, 6, 80, 4, 87, 6, 129, 69, 7,
+/* 820 */ 80, 4, 88, 7, 129, 67, 7, 80, 4, 90,
+/* 830 */ 7, 129, 66, 6, 80, 4, 92, 6, 129, 68,
+/* 840 */ 2, 80, 4, 94, 2, 129, 80, 4, 130, 193,
+/* 850 */ 129, 60, 4, 139, 41, 42, 131, 60, 4, 139,
+/* 860 */ 193, 130, 34, 6, 129, 32, 10, 129, 31, 12,
+/* 870 */ 129, 30, 14, 129, 20, 2, 28, 16, 129, 22,
+/* 880 */ 22, 129, 24, 19, 129, 27, 15, 129, 31, 9,
+/* 890 */ 129, 194, 129, 60, 4, 152, 193, 130, 34, 6,
+/* 900 */ 129, 32, 10, 129, 31, 12, 129, 30, 14, 131,
+/* 910 */ 31, 12, 129, 32, 10, 129, 34, 6, 129, 194,
+/* 920 */ 129, 30, 4, 129, 30, 7, 129, 30, 10, 129,
+/* 930 */ 33, 10, 129, 36, 10, 129, 39, 10, 129, 42,
+/* 940 */ 10, 129, 45, 10, 129, 48, 10, 129, 51, 10,
+/* 950 */ 129, 54, 10, 129, 57, 10, 129, 60, 10, 129,
+/* 960 */ 63, 10, 129, 66, 10, 129, 69, 10, 129, 72,
+/* 970 */ 10, 129, 75, 10, 129, 78, 10, 129, 81, 10,
+/* 980 */ 129, 84, 10, 129, 87, 10, 129, 90, 10, 129,
+/* 990 */ 93, 10, 129, 96, 10, 129, 99, 10, 129, 102,
+/* 1000 */ 10, 129, 105, 10, 129, 108, 10, 129, 111, 10,
+/* 1010 */ 129, 114, 7, 129, 117, 4, 129, 193, 129, 60,
+/* 1020 */ 31, 129, 53, 45, 129, 49, 53, 129, 46, 59,
+/* 1030 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129,
+/* 1040 */ 37, 77, 129, 36, 79, 129, 35, 15, 101, 15,
+/* 1050 */ 129, 34, 11, 106, 11, 129, 33, 9, 109, 9,
+/* 1060 */ 129, 32, 7, 112, 7, 129, 31, 6, 114, 6,
+/* 1070 */ 129, 31, 5, 115, 5, 129, 30, 5, 116, 5,
+/* 1080 */ 129, 30, 4, 117, 4, 132, 30, 5, 116, 5,
+/* 1090 */ 129, 31, 5, 115, 5, 129, 31, 6, 114, 6,
+/* 1100 */ 129, 32, 7, 112, 7, 129, 33, 9, 109, 9,
+/* 1110 */ 129, 34, 11, 106, 11, 129, 35, 15, 101, 15,
+/* 1120 */ 129, 36, 79, 129, 37, 77, 129, 39, 73, 129,
+/* 1130 */ 41, 69, 129, 43, 65, 129, 46, 59, 129, 49,
+/* 1140 */ 53, 129, 53, 45, 129, 60, 31, 129, 193, 129,
+/* 1150 */ 30, 4, 129, 30, 4, 100, 1, 129, 30, 4,
+/* 1160 */ 100, 3, 129, 30, 4, 100, 5, 129, 30, 76,
+/* 1170 */ 129, 30, 78, 129, 30, 80, 129, 30, 82, 129,
+/* 1180 */ 30, 83, 129, 30, 85, 129, 30, 87, 129, 30,
+/* 1190 */ 89, 129, 30, 91, 129, 30, 4, 132, 193, 129,
+/* 1200 */ 30, 3, 129, 30, 7, 129, 30, 10, 112, 1,
+/* 1210 */ 129, 30, 13, 112, 2, 129, 30, 16, 112, 3,
+/* 1220 */ 129, 30, 18, 111, 5, 129, 30, 21, 111, 6,
+/* 1230 */ 129, 30, 23, 112, 6, 129, 30, 14, 47, 8,
+/* 1240 */ 113, 6, 129, 30, 14, 49, 8, 114, 5, 129,
+/* 1250 */ 30, 14, 51, 8, 115, 5, 129, 30, 14, 53,
+/* 1260 */ 8, 116, 4, 129, 30, 14, 55, 8, 116, 5,
+/* 1270 */ 129, 30, 14, 56, 9, 117, 4, 129, 30, 14,
+/* 1280 */ 57, 9, 117, 4, 129, 30, 14, 58, 10, 117,
+/* 1290 */ 4, 129, 30, 14, 59, 10, 117, 4, 129, 30,
+/* 1300 */ 14, 60, 11, 117, 4, 129, 30, 14, 61, 11,
+/* 1310 */ 116, 5, 129, 30, 14, 62, 11, 116, 5, 129,
+/* 1320 */ 30, 14, 63, 12, 115, 6, 129, 30, 14, 64,
+/* 1330 */ 13, 114, 7, 129, 30, 14, 65, 13, 113, 8,
+/* 1340 */ 129, 30, 14, 65, 15, 111, 9, 129, 30, 14,
+/* 1350 */ 66, 16, 109, 11, 129, 30, 14, 67, 17, 107,
+/* 1360 */ 12, 129, 30, 14, 68, 20, 103, 16, 129, 30,
+/* 1370 */ 14, 69, 49, 129, 30, 14, 70, 47, 129, 30,
+/* 1380 */ 14, 71, 45, 129, 30, 14, 73, 42, 129, 30,
+/* 1390 */ 15, 75, 38, 129, 33, 12, 77, 34, 129, 36,
+/* 1400 */ 10, 79, 30, 129, 40, 6, 82, 23, 129, 44,
+/* 1410 */ 3, 86, 15, 129, 47, 1, 129, 193, 129, 129,
+/* 1420 */ 38, 3, 129, 37, 5, 111, 1, 129, 36, 7,
+/* 1430 */ 111, 2, 129, 35, 9, 110, 5, 129, 34, 8,
+/* 1440 */ 110, 6, 129, 33, 7, 109, 8, 129, 32, 7,
+/* 1450 */ 110, 8, 129, 32, 6, 112, 7, 129, 31, 6,
+/* 1460 */ 113, 6, 129, 31, 5, 114, 6, 129, 30, 5,
+/* 1470 */ 115, 5, 129, 30, 5, 116, 4, 129, 30, 4,
+/* 1480 */ 117, 4, 131, 30, 4, 117, 4, 129, 30, 4,
+/* 1490 */ 79, 2, 117, 4, 129, 30, 5, 78, 4, 117,
+/* 1500 */ 4, 129, 30, 5, 77, 6, 116, 5, 129, 30,
+/* 1510 */ 6, 76, 8, 115, 6, 129, 30, 7, 75, 11,
+/* 1520 */ 114, 6, 129, 30, 8, 73, 15, 112, 8, 129,
+/* 1530 */ 31, 9, 71, 19, 110, 9, 129, 31, 11, 68,
+/* 1540 */ 26, 107, 12, 129, 32, 13, 65, 14, 82, 36,
+/* 1550 */ 129, 32, 16, 61, 17, 83, 34, 129, 33, 44,
+/* 1560 */ 84, 32, 129, 34, 42, 85, 30, 129, 35, 40,
+/* 1570 */ 87, 27, 129, 36, 38, 89, 23, 129, 38, 34,
+/* 1580 */ 92, 17, 129, 40, 30, 95, 11, 129, 42, 26,
+/* 1590 */ 129, 45, 20, 129, 49, 11, 129, 193, 129, 49,
+/* 1600 */ 1, 129, 49, 4, 129, 49, 6, 129, 49, 8,
+/* 1610 */ 129, 49, 10, 129, 49, 12, 129, 49, 14, 129,
+/* 1620 */ 49, 17, 129, 49, 19, 129, 49, 21, 129, 49,
+/* 1630 */ 23, 129, 49, 14, 65, 9, 129, 49, 14, 67,
+/* 1640 */ 9, 129, 49, 14, 69, 9, 129, 49, 14, 71,
+/* 1650 */ 10, 129, 49, 14, 74, 9, 129, 49, 14, 76,
+/* 1660 */ 9, 129, 49, 14, 78, 9, 129, 49, 14, 80,
+/* 1670 */ 9, 129, 49, 14, 82, 9, 129, 49, 14, 84,
+/* 1680 */ 9, 129, 30, 4, 49, 14, 86, 10, 129, 30,
+/* 1690 */ 4, 49, 14, 89, 9, 129, 30, 4, 49, 14,
+/* 1700 */ 91, 9, 129, 30, 4, 49, 14, 93, 9, 129,
+/* 1710 */ 30, 74, 129, 30, 76, 129, 30, 78, 129, 30,
+/* 1720 */ 81, 129, 30, 83, 129, 30, 85, 129, 30, 87,
+/* 1730 */ 129, 30, 89, 129, 30, 91, 129, 30, 4, 49,
+/* 1740 */ 14, 132, 193, 129, 37, 1, 129, 36, 3, 77,
+/* 1750 */ 3, 129, 35, 5, 78, 11, 129, 34, 7, 78,
+/* 1760 */ 21, 129, 33, 7, 79, 29, 129, 32, 7, 79,
+/* 1770 */ 38, 129, 32, 6, 80, 4, 92, 29, 129, 31,
+/* 1780 */ 6, 80, 5, 102, 19, 129, 31, 5, 80, 6,
+/* 1790 */ 107, 14, 129, 31, 4, 81, 5, 107, 14, 129,
+/* 1800 */ 30, 5, 81, 6, 107, 14, 129, 30, 4, 81,
+/* 1810 */ 6, 107, 14, 130, 30, 4, 81, 7, 107, 14,
+/* 1820 */ 129, 30, 4, 80, 8, 107, 14, 130, 30, 5,
+/* 1830 */ 80, 8, 107, 14, 129, 30, 5, 79, 9, 107,
+/* 1840 */ 14, 129, 31, 5, 79, 9, 107, 14, 129, 31,
+/* 1850 */ 6, 78, 10, 107, 14, 129, 32, 6, 76, 11,
+/* 1860 */ 107, 14, 129, 32, 8, 74, 13, 107, 14, 129,
+/* 1870 */ 33, 10, 71, 16, 107, 14, 129, 33, 15, 67,
+/* 1880 */ 19, 107, 14, 129, 34, 51, 107, 14, 129, 35,
+/* 1890 */ 49, 107, 14, 129, 36, 47, 107, 14, 129, 37,
+/* 1900 */ 45, 107, 14, 129, 39, 41, 107, 14, 129, 41,
+/* 1910 */ 37, 107, 14, 129, 44, 32, 107, 14, 129, 47,
+/* 1920 */ 25, 111, 10, 129, 51, 16, 115, 6, 129, 119,
+/* 1930 */ 2, 129, 193, 129, 56, 39, 129, 51, 49, 129,
+/* 1940 */ 47, 57, 129, 44, 63, 129, 42, 67, 129, 40,
+/* 1950 */ 71, 129, 38, 75, 129, 37, 77, 129, 35, 81,
+/* 1960 */ 129, 34, 16, 74, 5, 101, 16, 129, 33, 11,
+/* 1970 */ 76, 5, 107, 11, 129, 32, 9, 77, 5, 110,
+/* 1980 */ 9, 129, 32, 7, 79, 4, 112, 7, 129, 31,
+/* 1990 */ 6, 80, 4, 114, 6, 129, 31, 5, 81, 4,
+/* 2000 */ 115, 5, 129, 30, 5, 82, 4, 116, 5, 129,
+/* 2010 */ 30, 4, 82, 4, 116, 5, 129, 30, 4, 82,
+/* 2020 */ 5, 117, 4, 131, 30, 5, 82, 5, 117, 4,
+/* 2030 */ 129, 31, 5, 81, 6, 117, 4, 129, 31, 6,
+/* 2040 */ 80, 7, 117, 4, 129, 32, 7, 79, 8, 117,
+/* 2050 */ 4, 129, 32, 9, 77, 9, 116, 5, 129, 33,
+/* 2060 */ 11, 75, 11, 116, 4, 129, 34, 16, 69, 16,
+/* 2070 */ 115, 5, 129, 35, 49, 114, 5, 129, 37, 46,
+/* 2080 */ 113, 5, 129, 38, 44, 112, 6, 129, 40, 41,
+/* 2090 */ 112, 5, 129, 42, 37, 113, 3, 129, 44, 33,
+/* 2100 */ 114, 1, 129, 47, 27, 129, 51, 17, 129, 193,
+/* 2110 */ 129, 103, 2, 129, 103, 6, 129, 104, 9, 129,
+/* 2120 */ 105, 12, 129, 106, 15, 129, 107, 14, 135, 30,
+/* 2130 */ 10, 107, 14, 129, 30, 17, 107, 14, 129, 30,
+/* 2140 */ 25, 107, 14, 129, 30, 31, 107, 14, 129, 30,
+/* 2150 */ 37, 107, 14, 129, 30, 42, 107, 14, 129, 30,
+/* 2160 */ 46, 107, 14, 129, 30, 50, 107, 14, 129, 30,
+/* 2170 */ 54, 107, 14, 129, 30, 58, 107, 14, 129, 59,
+/* 2180 */ 32, 107, 14, 129, 64, 30, 107, 14, 129, 74,
+/* 2190 */ 23, 107, 14, 129, 81, 18, 107, 14, 129, 86,
+/* 2200 */ 16, 107, 14, 129, 91, 14, 107, 14, 129, 96,
+/* 2210 */ 25, 129, 100, 21, 129, 104, 17, 129, 107, 14,
+/* 2220 */ 129, 111, 10, 129, 114, 7, 129, 117, 4, 129,
+/* 2230 */ 120, 1, 129, 193, 129, 48, 13, 129, 44, 21,
+/* 2240 */ 129, 42, 26, 129, 40, 30, 92, 12, 129, 38,
+/* 2250 */ 34, 88, 20, 129, 36, 37, 86, 25, 129, 35,
+/* 2260 */ 39, 84, 29, 129, 34, 13, 63, 12, 82, 33,
+/* 2270 */ 129, 33, 11, 67, 9, 80, 36, 129, 32, 9,
+/* 2280 */ 70, 7, 79, 38, 129, 31, 8, 72, 46, 129,
+/* 2290 */ 30, 7, 74, 22, 108, 11, 129, 30, 6, 75,
+/* 2300 */ 19, 111, 9, 129, 30, 5, 75, 17, 113, 7,
+/* 2310 */ 129, 30, 5, 74, 16, 114, 6, 129, 30, 4,
+/* 2320 */ 73, 16, 115, 6, 129, 30, 4, 72, 16, 116,
+/* 2330 */ 5, 129, 30, 4, 72, 15, 117, 4, 129, 30,
+/* 2340 */ 4, 71, 16, 117, 4, 129, 30, 5, 70, 16,
+/* 2350 */ 117, 4, 129, 30, 5, 70, 15, 117, 4, 129,
+/* 2360 */ 30, 6, 69, 15, 116, 5, 129, 30, 7, 68,
+/* 2370 */ 17, 115, 5, 129, 30, 9, 67, 19, 114, 6,
+/* 2380 */ 129, 30, 10, 65, 22, 113, 6, 129, 31, 12,
+/* 2390 */ 63, 27, 110, 9, 129, 32, 14, 60, 21, 84,
+/* 2400 */ 9, 106, 12, 129, 33, 47, 85, 32, 129, 34,
+/* 2410 */ 45, 86, 30, 129, 35, 43, 88, 26, 129, 36,
+/* 2420 */ 40, 90, 22, 129, 38, 36, 93, 17, 129, 40,
+/* 2430 */ 32, 96, 10, 129, 42, 28, 129, 44, 23, 129,
+/* 2440 */ 48, 15, 129, 193, 129, 83, 17, 129, 77, 27,
+/* 2450 */ 129, 36, 1, 74, 33, 129, 35, 3, 72, 37,
+/* 2460 */ 129, 34, 5, 70, 41, 129, 33, 6, 69, 44,
+/* 2470 */ 129, 33, 5, 68, 46, 129, 32, 5, 67, 49,
+/* 2480 */ 129, 31, 5, 66, 17, 101, 16, 129, 31, 5,
+/* 2490 */ 66, 11, 108, 10, 129, 30, 4, 65, 9, 110,
+/* 2500 */ 9, 129, 30, 4, 64, 8, 112, 7, 129, 30,
+/* 2510 */ 4, 64, 7, 114, 6, 129, 30, 4, 64, 6,
+/* 2520 */ 115, 5, 129, 30, 4, 64, 5, 116, 5, 129,
+/* 2530 */ 30, 4, 64, 5, 117, 4, 131, 30, 4, 65,
+/* 2540 */ 4, 117, 4, 129, 30, 5, 65, 4, 116, 5,
+/* 2550 */ 129, 31, 5, 66, 4, 115, 5, 129, 31, 6,
+/* 2560 */ 67, 4, 114, 6, 129, 32, 7, 68, 4, 112,
+/* 2570 */ 7, 129, 32, 9, 69, 5, 110, 9, 129, 33,
+/* 2580 */ 11, 70, 5, 107, 11, 129, 34, 16, 72, 5,
+/* 2590 */ 101, 16, 129, 35, 81, 129, 37, 77, 129, 38,
+/* 2600 */ 75, 129, 40, 71, 129, 42, 67, 129, 44, 63,
+/* 2610 */ 129, 47, 57, 129, 51, 49, 129, 56, 39, 129,
+/* 2620 */ 193, 130, 34, 6, 74, 6, 129, 32, 10, 72,
+/* 2630 */ 10, 129, 31, 12, 71, 12, 129, 30, 14, 70,
+/* 2640 */ 14, 131, 31, 12, 71, 12, 129, 32, 10, 72,
+/* 2650 */ 10, 129, 34, 6, 74, 6, 129, 194, 130, 34,
+/* 2660 */ 6, 74, 6, 129, 32, 10, 72, 10, 129, 31,
+/* 2670 */ 12, 71, 12, 129, 30, 14, 70, 14, 129, 20,
+/* 2680 */ 2, 28, 16, 70, 14, 129, 22, 22, 70, 14,
+/* 2690 */ 129, 24, 19, 71, 12, 129, 27, 15, 72, 10,
+/* 2700 */ 129, 31, 9, 74, 6, 129, 194, 129, 53, 4,
+/* 2710 */ 63, 4, 152, 193, 130, 99, 7, 129, 97, 13,
+/* 2720 */ 129, 96, 16, 129, 96, 18, 129, 96, 19, 129,
+/* 2730 */ 97, 19, 129, 99, 6, 110, 7, 129, 112, 6,
+/* 2740 */ 129, 114, 5, 129, 34, 6, 57, 5, 115, 4,
+/* 2750 */ 129, 32, 10, 54, 12, 116, 4, 129, 31, 12,
+/* 2760 */ 53, 16, 117, 3, 129, 30, 14, 52, 20, 117,
+/* 2770 */ 4, 129, 30, 14, 52, 23, 117, 4, 129, 30,
+/* 2780 */ 14, 52, 25, 117, 4, 129, 31, 12, 52, 27,
+/* 2790 */ 117, 4, 129, 32, 10, 53, 10, 70, 11, 116,
+/* 2800 */ 5, 129, 34, 6, 55, 5, 73, 10, 115, 6,
+/* 2810 */ 129, 74, 11, 114, 7, 129, 75, 12, 112, 9,
+/* 2820 */ 129, 76, 13, 110, 10, 129, 77, 16, 106, 14,
+/* 2830 */ 129, 78, 41, 129, 80, 38, 129, 81, 36, 129,
+/* 2840 */ 82, 34, 129, 84, 30, 129, 86, 26, 129, 88,
+/* 2850 */ 22, 129, 92, 14, 129, 194, 129, 55, 15, 129,
+/* 2860 */ 50, 25, 129, 47, 32, 129, 45, 13, 70, 12,
+/* 2870 */ 129, 43, 9, 76, 10, 129, 42, 6, 79, 8,
+/* 2880 */ 129, 41, 5, 81, 7, 129, 40, 4, 84, 6,
+/* 2890 */ 129, 39, 4, 59, 12, 85, 6, 129, 38, 4,
+/* 2900 */ 55, 19, 87, 5, 129, 37, 4, 53, 23, 88,
+/* 2910 */ 4, 129, 36, 4, 51, 8, 71, 6, 89, 4,
+/* 2920 */ 129, 36, 4, 51, 6, 73, 4, 89, 4, 129,
+/* 2930 */ 36, 4, 50, 6, 74, 4, 90, 3, 129, 35,
+/* 2940 */ 4, 50, 5, 75, 3, 90, 4, 129, 35, 4,
+/* 2950 */ 50, 4, 75, 4, 90, 4, 131, 35, 4, 50,
+/* 2960 */ 5, 75, 4, 90, 4, 129, 36, 4, 51, 5,
+/* 2970 */ 75, 4, 90, 4, 129, 36, 4, 51, 6, 75,
+/* 2980 */ 4, 90, 4, 129, 36, 4, 53, 26, 90, 4,
+/* 2990 */ 129, 37, 4, 54, 25, 90, 4, 129, 37, 4,
+/* 3000 */ 52, 27, 90, 3, 129, 38, 4, 52, 4, 89,
+/* 3010 */ 4, 129, 39, 4, 51, 4, 88, 4, 129, 40,
+/* 3020 */ 4, 50, 4, 87, 5, 129, 41, 4, 50, 4,
+/* 3030 */ 86, 5, 129, 42, 4, 50, 4, 85, 5, 129,
+/* 3040 */ 43, 3, 50, 4, 83, 6, 129, 44, 2, 51,
+/* 3050 */ 5, 80, 7, 129, 46, 1, 52, 6, 76, 9,
+/* 3060 */ 129, 54, 28, 129, 56, 23, 129, 60, 16, 129,
+/* 3070 */ 193, 129, 30, 4, 132, 30, 5, 129, 30, 8,
+/* 3080 */ 129, 30, 12, 129, 30, 16, 129, 30, 4, 37,
+/* 3090 */ 12, 129, 30, 4, 41, 12, 129, 30, 4, 44,
+/* 3100 */ 13, 129, 30, 4, 48, 13, 129, 52, 13, 129,
+/* 3110 */ 56, 12, 129, 58, 14, 129, 58, 4, 64, 12,
+/* 3120 */ 129, 58, 4, 68, 12, 129, 58, 4, 72, 12,
+/* 3130 */ 129, 58, 4, 75, 13, 129, 58, 4, 79, 13,
+/* 3140 */ 129, 58, 4, 83, 13, 129, 58, 4, 87, 13,
+/* 3150 */ 129, 58, 4, 91, 12, 129, 58, 4, 95, 12,
+/* 3160 */ 129, 58, 4, 96, 15, 129, 58, 4, 93, 22,
+/* 3170 */ 129, 58, 4, 89, 30, 129, 58, 4, 85, 36,
+/* 3180 */ 129, 58, 4, 81, 38, 129, 58, 4, 77, 38,
+/* 3190 */ 129, 58, 4, 73, 38, 129, 58, 4, 70, 37,
+/* 3200 */ 129, 58, 4, 66, 37, 129, 58, 41, 129, 58,
+/* 3210 */ 37, 129, 54, 38, 129, 30, 4, 50, 38, 129,
+/* 3220 */ 30, 4, 46, 38, 129, 30, 4, 42, 38, 129,
+/* 3230 */ 30, 4, 38, 39, 129, 30, 43, 129, 30, 39,
+/* 3240 */ 129, 30, 35, 129, 30, 31, 129, 30, 27, 129,
+/* 3250 */ 30, 24, 129, 30, 20, 129, 30, 16, 129, 30,
+/* 3260 */ 12, 129, 30, 8, 129, 30, 5, 129, 30, 4,
+/* 3270 */ 132, 193, 129, 30, 4, 117, 4, 132, 30, 91,
+/* 3280 */ 137, 30, 4, 80, 4, 117, 4, 138, 30, 4,
+/* 3290 */ 80, 5, 116, 5, 129, 30, 5, 79, 6, 116,
+/* 3300 */ 5, 130, 30, 6, 78, 8, 115, 6, 129, 31,
+/* 3310 */ 6, 77, 9, 115, 6, 129, 31, 7, 76, 11,
+/* 3320 */ 114, 6, 129, 31, 8, 75, 14, 112, 8, 129,
+/* 3330 */ 32, 8, 74, 16, 111, 9, 129, 32, 9, 73,
+/* 3340 */ 19, 109, 10, 129, 33, 10, 71, 24, 106, 13,
+/* 3350 */ 129, 33, 13, 68, 12, 83, 35, 129, 34, 16,
+/* 3360 */ 64, 15, 84, 33, 129, 35, 43, 85, 31, 129,
+/* 3370 */ 36, 41, 86, 29, 129, 37, 39, 88, 25, 129,
+/* 3380 */ 38, 37, 90, 21, 129, 40, 33, 93, 15, 129,
+/* 3390 */ 42, 29, 96, 9, 129, 45, 24, 129, 49, 16,
+/* 3400 */ 129, 193, 129, 63, 25, 129, 57, 37, 129, 53,
+/* 3410 */ 45, 129, 50, 51, 129, 47, 57, 129, 45, 61,
+/* 3420 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129,
+/* 3430 */ 38, 25, 92, 21, 129, 36, 21, 97, 18, 129,
+/* 3440 */ 35, 18, 102, 14, 129, 34, 16, 106, 11, 129,
+/* 3450 */ 33, 14, 108, 10, 129, 32, 12, 111, 8, 129,
+/* 3460 */ 32, 10, 113, 6, 129, 31, 10, 114, 6, 129,
+/* 3470 */ 31, 8, 115, 5, 129, 30, 8, 116, 5, 129,
+/* 3480 */ 30, 7, 116, 5, 129, 30, 6, 117, 4, 130,
+/* 3490 */ 30, 5, 117, 4, 131, 31, 4, 116, 5, 129,
+/* 3500 */ 32, 4, 116, 4, 129, 32, 5, 115, 5, 129,
+/* 3510 */ 33, 4, 114, 5, 129, 34, 4, 112, 6, 129,
+/* 3520 */ 35, 4, 110, 7, 129, 37, 4, 107, 9, 129,
+/* 3530 */ 39, 4, 103, 12, 129, 41, 4, 103, 18, 129,
+/* 3540 */ 43, 4, 103, 18, 129, 45, 5, 103, 18, 129,
+/* 3550 */ 48, 5, 103, 18, 129, 51, 1, 129, 193, 129,
+/* 3560 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 3570 */ 117, 4, 135, 30, 5, 116, 5, 130, 30, 6,
+/* 3580 */ 115, 6, 130, 31, 6, 114, 6, 129, 31, 7,
+/* 3590 */ 113, 7, 129, 32, 7, 112, 7, 129, 32, 8,
+/* 3600 */ 111, 8, 129, 33, 9, 109, 9, 129, 33, 12,
+/* 3610 */ 106, 12, 129, 34, 13, 104, 13, 129, 35, 15,
+/* 3620 */ 101, 15, 129, 36, 19, 96, 19, 129, 37, 24,
+/* 3630 */ 90, 24, 129, 39, 73, 129, 40, 71, 129, 42,
+/* 3640 */ 67, 129, 44, 63, 129, 46, 59, 129, 49, 53,
+/* 3650 */ 129, 52, 47, 129, 56, 39, 129, 61, 29, 129,
+/* 3660 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 137,
+/* 3670 */ 30, 4, 80, 4, 117, 4, 140, 30, 4, 79,
+/* 3680 */ 6, 117, 4, 129, 30, 4, 77, 10, 117, 4,
+/* 3690 */ 129, 30, 4, 73, 18, 117, 4, 132, 30, 4,
+/* 3700 */ 117, 4, 130, 30, 5, 116, 5, 130, 30, 7,
+/* 3710 */ 114, 7, 129, 30, 8, 113, 8, 129, 30, 11,
+/* 3720 */ 110, 11, 129, 30, 18, 103, 18, 132, 193, 129,
+/* 3730 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 3740 */ 80, 4, 117, 4, 132, 80, 4, 117, 4, 136,
+/* 3750 */ 79, 6, 117, 4, 129, 77, 10, 117, 4, 129,
+/* 3760 */ 73, 18, 117, 4, 132, 117, 4, 130, 116, 5,
+/* 3770 */ 130, 114, 7, 129, 113, 8, 129, 110, 11, 129,
+/* 3780 */ 103, 18, 132, 193, 129, 63, 25, 129, 57, 37,
+/* 3790 */ 129, 53, 45, 129, 50, 51, 129, 47, 57, 129,
+/* 3800 */ 45, 61, 129, 43, 65, 129, 41, 69, 129, 39,
+/* 3810 */ 73, 129, 38, 25, 92, 21, 129, 36, 21, 97,
+/* 3820 */ 18, 129, 35, 18, 102, 14, 129, 34, 16, 106,
+/* 3830 */ 11, 129, 33, 14, 108, 10, 129, 32, 12, 111,
+/* 3840 */ 8, 129, 32, 10, 113, 6, 129, 31, 10, 114,
+/* 3850 */ 6, 129, 31, 8, 115, 5, 129, 30, 8, 116,
+/* 3860 */ 5, 129, 30, 7, 116, 5, 129, 30, 6, 117,
+/* 3870 */ 4, 130, 30, 5, 117, 4, 131, 30, 5, 75,
+/* 3880 */ 4, 116, 5, 129, 31, 5, 75, 4, 116, 4,
+/* 3890 */ 129, 31, 6, 75, 4, 115, 5, 129, 32, 7,
+/* 3900 */ 75, 4, 114, 5, 129, 32, 9, 75, 4, 112,
+/* 3910 */ 6, 129, 33, 11, 75, 4, 110, 7, 129, 34,
+/* 3920 */ 15, 75, 4, 107, 9, 129, 35, 44, 103, 12,
+/* 3930 */ 129, 36, 43, 103, 18, 129, 38, 41, 103, 18,
+/* 3940 */ 129, 39, 40, 103, 18, 129, 41, 38, 103, 18,
+/* 3950 */ 129, 44, 35, 129, 48, 31, 129, 52, 27, 129,
+/* 3960 */ 61, 18, 129, 193, 129, 30, 4, 117, 4, 132,
+/* 3970 */ 30, 91, 137, 30, 4, 80, 4, 117, 4, 132,
+/* 3980 */ 80, 4, 140, 30, 4, 80, 4, 117, 4, 132,
+/* 3990 */ 30, 91, 137, 30, 4, 117, 4, 132, 193, 129,
+/* 4000 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 4010 */ 117, 4, 132, 193, 129, 44, 7, 129, 40, 13,
+/* 4020 */ 129, 37, 17, 129, 35, 20, 129, 34, 22, 129,
+/* 4030 */ 33, 23, 129, 32, 24, 129, 32, 23, 129, 31,
+/* 4040 */ 6, 41, 13, 129, 31, 5, 42, 11, 129, 30,
+/* 4050 */ 5, 44, 7, 129, 30, 4, 132, 30, 5, 130,
+/* 4060 */ 31, 5, 129, 31, 6, 117, 4, 129, 31, 8,
+/* 4070 */ 117, 4, 129, 32, 9, 117, 4, 129, 33, 11,
+/* 4080 */ 117, 4, 129, 34, 87, 129, 35, 86, 129, 36,
+/* 4090 */ 85, 129, 37, 84, 129, 38, 83, 129, 40, 81,
+/* 4100 */ 129, 42, 79, 129, 45, 76, 129, 50, 71, 129,
+/* 4110 */ 117, 4, 132, 193, 129, 30, 4, 117, 4, 132,
+/* 4120 */ 30, 91, 137, 30, 4, 76, 8, 117, 4, 129,
+/* 4130 */ 30, 4, 73, 13, 117, 4, 129, 30, 4, 70,
+/* 4140 */ 18, 117, 4, 129, 30, 4, 67, 23, 117, 4,
+/* 4150 */ 129, 65, 26, 129, 62, 31, 129, 59, 35, 129,
+/* 4160 */ 56, 29, 89, 7, 129, 53, 29, 91, 7, 129,
+/* 4170 */ 50, 29, 93, 7, 129, 47, 29, 95, 6, 129,
+/* 4180 */ 30, 4, 45, 29, 96, 7, 129, 30, 4, 42,
+/* 4190 */ 29, 98, 7, 129, 30, 4, 39, 30, 100, 6,
+/* 4200 */ 129, 30, 4, 36, 30, 101, 7, 129, 30, 33,
+/* 4210 */ 103, 7, 117, 4, 129, 30, 30, 105, 6, 117,
+/* 4220 */ 4, 129, 30, 27, 106, 7, 117, 4, 129, 30,
+/* 4230 */ 25, 108, 7, 117, 4, 129, 30, 22, 110, 11,
+/* 4240 */ 129, 30, 19, 111, 10, 129, 30, 16, 113, 8,
+/* 4250 */ 129, 30, 13, 115, 6, 129, 30, 11, 116, 5,
+/* 4260 */ 129, 30, 8, 117, 4, 129, 30, 5, 117, 4,
+/* 4270 */ 129, 30, 4, 117, 4, 130, 30, 4, 130, 193,
+/* 4280 */ 129, 30, 4, 117, 4, 132, 30, 91, 137, 30,
+/* 4290 */ 4, 117, 4, 132, 30, 4, 144, 30, 5, 130,
+/* 4300 */ 30, 7, 129, 30, 8, 129, 30, 11, 129, 30,
+/* 4310 */ 18, 132, 193, 129, 30, 4, 117, 4, 132, 30,
+/* 4320 */ 91, 132, 30, 4, 103, 18, 129, 30, 4, 97,
+/* 4330 */ 24, 129, 30, 4, 92, 29, 129, 30, 4, 87,
+/* 4340 */ 34, 129, 81, 40, 129, 76, 45, 129, 70, 49,
+/* 4350 */ 129, 65, 49, 129, 60, 49, 129, 55, 49, 129,
+/* 4360 */ 50, 48, 129, 44, 49, 129, 39, 48, 129, 33,
+/* 4370 */ 49, 129, 30, 47, 129, 34, 37, 129, 40, 26,
+/* 4380 */ 129, 46, 19, 129, 52, 19, 129, 58, 19, 129,
+/* 4390 */ 64, 19, 129, 70, 19, 129, 76, 19, 129, 82,
+/* 4400 */ 19, 129, 30, 4, 88, 18, 129, 30, 4, 94,
+/* 4410 */ 18, 129, 30, 4, 100, 18, 129, 30, 4, 106,
+/* 4420 */ 15, 129, 30, 91, 137, 30, 4, 117, 4, 132,
+/* 4430 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 132,
+/* 4440 */ 30, 4, 107, 14, 129, 30, 4, 104, 17, 129,
+/* 4450 */ 30, 4, 101, 20, 129, 30, 4, 99, 22, 129,
+/* 4460 */ 96, 25, 129, 93, 28, 129, 91, 28, 129, 88,
+/* 4470 */ 29, 129, 85, 29, 129, 82, 29, 129, 79, 29,
+/* 4480 */ 129, 76, 29, 129, 74, 29, 129, 71, 29, 129,
+/* 4490 */ 68, 29, 129, 65, 29, 129, 62, 29, 129, 60,
+/* 4500 */ 29, 129, 57, 29, 129, 54, 29, 129, 51, 29,
+/* 4510 */ 129, 49, 28, 129, 46, 29, 129, 43, 29, 129,
+/* 4520 */ 40, 29, 117, 4, 129, 37, 29, 117, 4, 129,
+/* 4530 */ 35, 29, 117, 4, 129, 32, 29, 117, 4, 129,
+/* 4540 */ 30, 91, 132, 117, 4, 132, 193, 129, 63, 25,
+/* 4550 */ 129, 57, 37, 129, 53, 45, 129, 50, 51, 129,
+/* 4560 */ 47, 57, 129, 45, 61, 129, 43, 65, 129, 41,
+/* 4570 */ 69, 129, 39, 73, 129, 38, 21, 92, 21, 129,
+/* 4580 */ 36, 18, 97, 18, 129, 35, 14, 102, 14, 129,
+/* 4590 */ 34, 11, 106, 11, 129, 33, 10, 108, 10, 129,
+/* 4600 */ 32, 8, 111, 8, 129, 32, 6, 113, 6, 129,
+/* 4610 */ 31, 6, 114, 6, 129, 31, 5, 115, 5, 129,
+/* 4620 */ 30, 5, 116, 5, 130, 30, 4, 117, 4, 132,
+/* 4630 */ 30, 5, 116, 5, 130, 31, 5, 115, 5, 129,
+/* 4640 */ 31, 6, 114, 6, 129, 32, 6, 113, 6, 129,
+/* 4650 */ 32, 8, 111, 8, 129, 33, 10, 108, 10, 129,
+/* 4660 */ 34, 11, 106, 11, 129, 35, 14, 102, 14, 129,
+/* 4670 */ 36, 18, 97, 18, 129, 38, 21, 92, 21, 129,
+/* 4680 */ 39, 73, 129, 41, 69, 129, 43, 65, 129, 45,
+/* 4690 */ 61, 129, 47, 57, 129, 50, 51, 129, 53, 45,
+/* 4700 */ 129, 57, 37, 129, 63, 25, 129, 193, 129, 30,
+/* 4710 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 80,
+/* 4720 */ 4, 117, 4, 132, 80, 4, 117, 4, 134, 80,
+/* 4730 */ 5, 116, 5, 131, 80, 6, 115, 6, 130, 81,
+/* 4740 */ 6, 114, 6, 129, 81, 8, 112, 8, 129, 81,
+/* 4750 */ 9, 111, 9, 129, 82, 10, 109, 10, 129, 82,
+/* 4760 */ 13, 106, 13, 129, 83, 35, 129, 84, 33, 129,
+/* 4770 */ 85, 31, 129, 86, 29, 129, 88, 25, 129, 90,
+/* 4780 */ 21, 129, 93, 15, 129, 96, 9, 129, 193, 129,
+/* 4790 */ 63, 25, 129, 57, 37, 129, 53, 45, 129, 50,
+/* 4800 */ 51, 129, 47, 57, 129, 45, 61, 129, 43, 65,
+/* 4810 */ 129, 41, 69, 129, 39, 73, 129, 38, 21, 92,
+/* 4820 */ 21, 129, 36, 18, 97, 18, 129, 35, 14, 102,
+/* 4830 */ 14, 129, 34, 11, 106, 11, 129, 33, 10, 108,
+/* 4840 */ 10, 129, 32, 8, 111, 8, 129, 32, 6, 113,
+/* 4850 */ 6, 129, 31, 6, 114, 6, 129, 31, 5, 115,
+/* 4860 */ 5, 129, 30, 5, 116, 5, 130, 30, 4, 39,
+/* 4870 */ 2, 117, 4, 129, 30, 4, 40, 4, 117, 4,
+/* 4880 */ 129, 30, 4, 41, 5, 117, 4, 129, 30, 4,
+/* 4890 */ 41, 6, 117, 4, 129, 30, 5, 40, 8, 116,
+/* 4900 */ 5, 129, 30, 5, 39, 10, 116, 5, 129, 31,
+/* 4910 */ 5, 38, 11, 115, 5, 129, 31, 18, 114, 6,
+/* 4920 */ 129, 32, 17, 113, 6, 129, 32, 16, 111, 8,
+/* 4930 */ 129, 33, 15, 108, 10, 129, 33, 14, 106, 11,
+/* 4940 */ 129, 32, 17, 102, 14, 129, 31, 23, 97, 18,
+/* 4950 */ 129, 31, 28, 92, 21, 129, 30, 82, 129, 30,
+/* 4960 */ 80, 129, 30, 11, 43, 65, 129, 30, 10, 45,
+/* 4970 */ 61, 129, 31, 8, 47, 57, 129, 32, 6, 50,
+/* 4980 */ 51, 129, 33, 5, 53, 45, 129, 35, 4, 57,
+/* 4990 */ 37, 129, 38, 2, 63, 25, 129, 193, 129, 30,
+/* 5000 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 76,
+/* 5010 */ 8, 117, 4, 129, 30, 4, 73, 11, 117, 4,
+/* 5020 */ 129, 30, 4, 70, 14, 117, 4, 129, 30, 4,
+/* 5030 */ 67, 17, 117, 4, 129, 65, 19, 117, 4, 129,
+/* 5040 */ 62, 22, 117, 4, 129, 59, 25, 117, 4, 129,
+/* 5050 */ 56, 28, 117, 4, 129, 53, 31, 117, 4, 129,
+/* 5060 */ 50, 34, 117, 4, 129, 47, 29, 80, 5, 116,
+/* 5070 */ 5, 129, 30, 4, 45, 29, 80, 5, 116, 5,
+/* 5080 */ 129, 30, 4, 42, 29, 80, 5, 116, 5, 129,
+/* 5090 */ 30, 4, 39, 30, 80, 6, 115, 6, 129, 30,
+/* 5100 */ 4, 36, 30, 80, 6, 115, 6, 129, 30, 33,
+/* 5110 */ 81, 6, 114, 6, 129, 30, 30, 81, 8, 112,
+/* 5120 */ 8, 129, 30, 27, 81, 9, 111, 9, 129, 30,
+/* 5130 */ 25, 82, 10, 109, 10, 129, 30, 22, 82, 13,
+/* 5140 */ 106, 13, 129, 30, 19, 83, 35, 129, 30, 16,
+/* 5150 */ 84, 33, 129, 30, 13, 85, 31, 129, 30, 11,
+/* 5160 */ 86, 29, 129, 30, 8, 88, 25, 129, 30, 5,
+/* 5170 */ 90, 21, 129, 30, 4, 93, 15, 129, 30, 4,
+/* 5180 */ 96, 9, 129, 30, 4, 130, 193, 129, 30, 18,
+/* 5190 */ 130, 30, 18, 89, 15, 129, 30, 18, 85, 23,
+/* 5200 */ 129, 34, 11, 83, 27, 129, 34, 9, 81, 31,
+/* 5210 */ 129, 33, 8, 79, 35, 129, 33, 6, 78, 16,
+/* 5220 */ 106, 9, 129, 32, 6, 77, 15, 109, 7, 129,
+/* 5230 */ 32, 5, 76, 14, 111, 6, 129, 31, 5, 75,
+/* 5240 */ 14, 113, 5, 129, 31, 4, 74, 15, 114, 5,
+/* 5250 */ 129, 31, 4, 74, 14, 115, 4, 129, 30, 4,
+/* 5260 */ 73, 15, 116, 4, 129, 30, 4, 73, 14, 116,
+/* 5270 */ 4, 129, 30, 4, 73, 14, 117, 4, 129, 30,
+/* 5280 */ 4, 72, 15, 117, 4, 130, 30, 4, 71, 15,
+/* 5290 */ 117, 4, 130, 30, 4, 70, 15, 117, 4, 129,
+/* 5300 */ 30, 5, 70, 15, 117, 4, 129, 30, 5, 69,
+/* 5310 */ 15, 116, 5, 129, 30, 6, 68, 16, 115, 5,
+/* 5320 */ 129, 31, 6, 67, 16, 114, 6, 129, 31, 7,
+/* 5330 */ 66, 17, 113, 6, 129, 32, 7, 64, 18, 111,
+/* 5340 */ 8, 129, 32, 8, 62, 19, 109, 9, 129, 33,
+/* 5350 */ 9, 60, 20, 107, 10, 129, 34, 11, 57, 22,
+/* 5360 */ 103, 13, 129, 35, 43, 103, 18, 129, 36, 41,
+/* 5370 */ 103, 18, 129, 38, 38, 103, 18, 129, 39, 35,
+/* 5380 */ 103, 18, 129, 41, 31, 129, 43, 27, 129, 46,
+/* 5390 */ 22, 129, 49, 14, 129, 193, 129, 103, 18, 132,
+/* 5400 */ 110, 11, 129, 113, 8, 129, 114, 7, 129, 116,
+/* 5410 */ 5, 130, 117, 4, 132, 30, 4, 117, 4, 132,
+/* 5420 */ 30, 91, 137, 30, 4, 117, 4, 132, 117, 4,
+/* 5430 */ 132, 116, 5, 130, 114, 7, 129, 113, 8, 129,
+/* 5440 */ 110, 11, 129, 103, 18, 132, 193, 129, 117, 4,
+/* 5450 */ 132, 56, 65, 129, 50, 71, 129, 46, 75, 129,
+/* 5460 */ 44, 77, 129, 42, 79, 129, 40, 81, 129, 38,
+/* 5470 */ 83, 129, 36, 85, 129, 35, 86, 129, 34, 20,
+/* 5480 */ 117, 4, 129, 33, 17, 117, 4, 129, 32, 15,
+/* 5490 */ 117, 4, 129, 32, 13, 117, 4, 129, 31, 12,
+/* 5500 */ 129, 31, 10, 129, 31, 9, 129, 30, 9, 129,
+/* 5510 */ 30, 8, 130, 30, 7, 132, 31, 6, 130, 31,
+/* 5520 */ 7, 129, 32, 6, 129, 32, 7, 129, 33, 7,
+/* 5530 */ 129, 34, 7, 129, 35, 8, 129, 36, 9, 117,
+/* 5540 */ 4, 129, 38, 9, 117, 4, 129, 40, 10, 117,
+/* 5550 */ 4, 129, 42, 12, 117, 4, 129, 44, 77, 129,
+/* 5560 */ 46, 75, 129, 50, 71, 129, 56, 43, 100, 21,
+/* 5570 */ 129, 117, 4, 132, 193, 129, 117, 4, 132, 115,
+/* 5580 */ 6, 129, 110, 11, 129, 105, 16, 129, 101, 20,
+/* 5590 */ 129, 96, 25, 129, 92, 29, 129, 87, 34, 129,
+/* 5600 */ 83, 38, 129, 78, 43, 129, 74, 47, 129, 70,
+/* 5610 */ 42, 117, 4, 129, 65, 42, 117, 4, 129, 60,
+/* 5620 */ 43, 117, 4, 129, 56, 42, 129, 51, 42, 129,
+/* 5630 */ 46, 43, 129, 42, 43, 129, 37, 44, 129, 33,
+/* 5640 */ 43, 129, 30, 42, 129, 33, 34, 129, 38, 25,
+/* 5650 */ 129, 42, 16, 129, 47, 15, 129, 52, 15, 129,
+/* 5660 */ 57, 15, 129, 61, 16, 129, 66, 16, 129, 71,
+/* 5670 */ 16, 129, 76, 16, 129, 80, 16, 129, 85, 16,
+/* 5680 */ 117, 4, 129, 90, 16, 117, 4, 129, 95, 16,
+/* 5690 */ 117, 4, 129, 100, 21, 129, 105, 16, 129, 110,
+/* 5700 */ 11, 129, 114, 7, 129, 117, 4, 132, 193, 129,
+/* 5710 */ 117, 4, 132, 115, 6, 129, 110, 11, 129, 105,
+/* 5720 */ 16, 129, 101, 20, 129, 96, 25, 129, 92, 29,
+/* 5730 */ 129, 87, 34, 129, 83, 38, 129, 78, 43, 129,
+/* 5740 */ 74, 47, 129, 70, 42, 117, 4, 129, 65, 42,
+/* 5750 */ 117, 4, 129, 60, 43, 117, 4, 129, 56, 42,
+/* 5760 */ 129, 51, 42, 129, 46, 43, 129, 42, 43, 129,
+/* 5770 */ 37, 44, 129, 33, 43, 129, 30, 42, 129, 33,
+/* 5780 */ 34, 129, 38, 25, 129, 42, 16, 129, 47, 15,
+/* 5790 */ 129, 52, 15, 129, 57, 15, 129, 61, 16, 129,
+/* 5800 */ 65, 17, 129, 60, 27, 129, 56, 36, 129, 51,
+/* 5810 */ 42, 129, 46, 43, 129, 42, 43, 129, 37, 44,
+/* 5820 */ 129, 33, 43, 129, 30, 42, 129, 33, 34, 129,
+/* 5830 */ 38, 25, 129, 42, 16, 129, 47, 15, 129, 52,
+/* 5840 */ 15, 129, 57, 15, 129, 61, 16, 129, 66, 16,
+/* 5850 */ 129, 71, 16, 129, 76, 16, 129, 80, 16, 129,
+/* 5860 */ 85, 16, 117, 4, 129, 90, 16, 117, 4, 129,
+/* 5870 */ 95, 16, 117, 4, 129, 100, 21, 129, 105, 16,
+/* 5880 */ 129, 110, 11, 129, 114, 7, 129, 117, 4, 132,
+/* 5890 */ 193, 129, 30, 4, 117, 4, 132, 30, 4, 115,
+/* 5900 */ 6, 129, 30, 4, 112, 9, 129, 30, 6, 109,
+/* 5910 */ 12, 129, 30, 9, 106, 15, 129, 30, 11, 103,
+/* 5920 */ 18, 129, 30, 14, 100, 21, 129, 30, 4, 38,
+/* 5930 */ 9, 98, 23, 129, 30, 4, 40, 10, 95, 26,
+/* 5940 */ 129, 30, 4, 43, 9, 92, 29, 129, 46, 9,
+/* 5950 */ 89, 32, 129, 49, 8, 86, 28, 117, 4, 129,
+/* 5960 */ 51, 9, 83, 28, 117, 4, 129, 54, 9, 80,
+/* 5970 */ 28, 117, 4, 129, 57, 8, 77, 28, 117, 4,
+/* 5980 */ 129, 59, 9, 74, 28, 129, 62, 37, 129, 64,
+/* 5990 */ 33, 129, 66, 28, 129, 63, 28, 129, 60, 28,
+/* 6000 */ 129, 57, 28, 129, 54, 33, 129, 51, 39, 129,
+/* 6010 */ 48, 29, 83, 9, 129, 30, 4, 45, 29, 86,
+/* 6020 */ 9, 129, 30, 4, 42, 29, 89, 9, 129, 30,
+/* 6030 */ 4, 39, 29, 92, 8, 129, 30, 4, 36, 29,
+/* 6040 */ 94, 9, 129, 30, 32, 97, 9, 129, 30, 29,
+/* 6050 */ 100, 8, 117, 4, 129, 30, 26, 103, 8, 117,
+/* 6060 */ 4, 129, 30, 23, 105, 9, 117, 4, 129, 30,
+/* 6070 */ 20, 108, 13, 129, 30, 18, 111, 10, 129, 30,
+/* 6080 */ 15, 113, 8, 129, 30, 12, 116, 5, 129, 30,
+/* 6090 */ 9, 117, 4, 129, 30, 6, 117, 4, 129, 30,
+/* 6100 */ 4, 117, 4, 132, 193, 129, 117, 4, 132, 114,
+/* 6110 */ 7, 129, 111, 10, 129, 108, 13, 129, 105, 16,
+/* 6120 */ 129, 102, 19, 129, 100, 21, 129, 96, 25, 129,
+/* 6130 */ 93, 28, 129, 90, 31, 129, 87, 34, 129, 84,
+/* 6140 */ 30, 117, 4, 129, 30, 4, 81, 30, 117, 4,
+/* 6150 */ 129, 30, 4, 78, 30, 117, 4, 129, 30, 4,
+/* 6160 */ 75, 30, 117, 4, 129, 30, 4, 72, 30, 129,
+/* 6170 */ 30, 69, 129, 30, 66, 129, 30, 63, 129, 30,
+/* 6180 */ 60, 129, 30, 57, 129, 30, 54, 129, 30, 51,
+/* 6190 */ 129, 30, 48, 129, 30, 51, 129, 30, 4, 73,
+/* 6200 */ 12, 129, 30, 4, 76, 12, 129, 30, 4, 80,
+/* 6210 */ 12, 129, 30, 4, 83, 12, 129, 87, 12, 129,
+/* 6220 */ 90, 12, 117, 4, 129, 94, 11, 117, 4, 129,
+/* 6230 */ 97, 12, 117, 4, 129, 101, 12, 117, 4, 129,
+/* 6240 */ 104, 17, 129, 108, 13, 129, 111, 10, 129, 115,
+/* 6250 */ 6, 129, 117, 4, 134, 193, 129, 30, 1, 103,
+/* 6260 */ 18, 129, 30, 4, 103, 18, 129, 30, 7, 103,
+/* 6270 */ 18, 129, 30, 9, 103, 18, 129, 30, 12, 110,
+/* 6280 */ 11, 129, 30, 15, 113, 8, 129, 30, 18, 114,
+/* 6290 */ 7, 129, 30, 21, 116, 5, 129, 30, 24, 116,
+/* 6300 */ 5, 129, 30, 27, 117, 4, 129, 30, 30, 117,
+/* 6310 */ 4, 129, 30, 33, 117, 4, 129, 30, 4, 37,
+/* 6320 */ 28, 117, 4, 129, 30, 4, 40, 28, 117, 4,
+/* 6330 */ 129, 30, 4, 42, 29, 117, 4, 129, 30, 4,
+/* 6340 */ 45, 29, 117, 4, 129, 30, 4, 48, 29, 117,
+/* 6350 */ 4, 129, 30, 4, 51, 29, 117, 4, 129, 30,
+/* 6360 */ 4, 54, 29, 117, 4, 129, 30, 4, 57, 29,
+/* 6370 */ 117, 4, 129, 30, 4, 59, 30, 117, 4, 129,
+/* 6380 */ 30, 4, 62, 30, 117, 4, 129, 30, 4, 65,
+/* 6390 */ 30, 117, 4, 129, 30, 4, 68, 30, 117, 4,
+/* 6400 */ 129, 30, 4, 71, 30, 117, 4, 129, 30, 4,
+/* 6410 */ 74, 30, 117, 4, 129, 30, 4, 77, 30, 117,
+/* 6420 */ 4, 129, 30, 4, 80, 30, 117, 4, 129, 30,
+/* 6430 */ 4, 83, 30, 117, 4, 129, 30, 4, 86, 35,
+/* 6440 */ 129, 30, 4, 89, 32, 129, 30, 4, 91, 30,
+/* 6450 */ 129, 30, 4, 94, 27, 129, 30, 5, 97, 24,
+/* 6460 */ 129, 30, 5, 100, 21, 129, 30, 7, 103, 18,
+/* 6470 */ 129, 30, 8, 106, 15, 129, 30, 11, 109, 12,
+/* 6480 */ 129, 30, 18, 112, 9, 129, 30, 18, 115, 6,
+/* 6490 */ 129, 30, 18, 117, 4, 129, 30, 18, 120, 1,
+/* 6500 */ 129, 193, 129, 42, 8, 129, 38, 16, 129, 36,
+/* 6510 */ 20, 129, 34, 24, 71, 5, 129, 33, 26, 69,
+/* 6520 */ 10, 129, 32, 28, 68, 13, 129, 31, 30, 68,
+/* 6530 */ 14, 129, 31, 9, 52, 9, 68, 15, 129, 30,
+/* 6540 */ 8, 54, 8, 69, 14, 129, 30, 7, 55, 7,
+/* 6550 */ 71, 4, 78, 6, 129, 30, 6, 56, 6, 79,
+/* 6560 */ 5, 129, 30, 6, 56, 6, 80, 4, 130, 31,
+/* 6570 */ 5, 56, 5, 80, 4, 129, 31, 5, 56, 5,
+/* 6580 */ 79, 5, 129, 32, 5, 55, 5, 78, 6, 129,
+/* 6590 */ 33, 5, 54, 5, 77, 7, 129, 34, 6, 52,
+/* 6600 */ 6, 74, 9, 129, 35, 48, 129, 33, 49, 129,
+/* 6610 */ 32, 49, 129, 31, 49, 129, 30, 49, 129, 30,
+/* 6620 */ 47, 129, 30, 45, 129, 30, 41, 129, 30, 6,
+/* 6630 */ 129, 30, 4, 129, 30, 3, 129, 30, 2, 129,
+/* 6640 */ 193, 129, 30, 4, 117, 4, 130, 31, 90, 136,
+/* 6650 */ 37, 5, 72, 5, 129, 35, 5, 74, 5, 129,
+/* 6660 */ 33, 5, 76, 5, 129, 32, 5, 77, 5, 129,
+/* 6670 */ 31, 5, 78, 5, 129, 31, 4, 79, 4, 129,
+/* 6680 */ 30, 5, 79, 5, 131, 30, 6, 78, 6, 129,
+/* 6690 */ 30, 7, 77, 7, 129, 31, 8, 75, 8, 129,
+/* 6700 */ 31, 11, 72, 11, 129, 32, 15, 67, 15, 129,
+/* 6710 */ 33, 48, 129, 34, 46, 129, 35, 44, 129, 37,
+/* 6720 */ 40, 129, 39, 36, 129, 42, 30, 129, 46, 22,
+/* 6730 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41,
+/* 6740 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44,
+/* 6750 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32,
+/* 6760 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31,
+/* 6770 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30,
+/* 6780 */ 5, 79, 5, 129, 30, 4, 80, 4, 133, 31,
+/* 6790 */ 3, 79, 4, 129, 31, 4, 79, 4, 129, 32,
+/* 6800 */ 3, 78, 4, 129, 32, 4, 76, 6, 129, 33,
+/* 6810 */ 4, 74, 7, 129, 34, 4, 72, 8, 129, 35,
+/* 6820 */ 5, 72, 7, 129, 37, 5, 73, 4, 129, 39,
+/* 6830 */ 4, 74, 1, 129, 129, 193, 129, 46, 22, 129,
+/* 6840 */ 42, 30, 129, 39, 36, 129, 37, 40, 129, 35,
+/* 6850 */ 44, 129, 34, 46, 129, 33, 48, 129, 32, 15,
+/* 6860 */ 67, 15, 129, 31, 11, 72, 11, 129, 31, 8,
+/* 6870 */ 75, 8, 129, 30, 7, 77, 7, 129, 30, 6,
+/* 6880 */ 78, 6, 129, 30, 5, 79, 5, 131, 31, 4,
+/* 6890 */ 79, 4, 129, 31, 5, 78, 5, 129, 32, 5,
+/* 6900 */ 77, 5, 129, 33, 5, 76, 5, 129, 35, 5,
+/* 6910 */ 74, 5, 117, 4, 129, 37, 5, 72, 5, 117,
+/* 6920 */ 4, 129, 30, 91, 136, 30, 4, 130, 193, 129,
+/* 6930 */ 48, 18, 129, 43, 28, 129, 41, 32, 129, 39,
+/* 6940 */ 36, 129, 37, 40, 129, 35, 44, 129, 34, 46,
+/* 6950 */ 129, 33, 13, 55, 4, 68, 13, 129, 32, 9,
+/* 6960 */ 55, 4, 73, 9, 129, 32, 7, 55, 4, 75,
+/* 6970 */ 7, 129, 31, 6, 55, 4, 77, 6, 129, 31,
+/* 6980 */ 5, 55, 4, 78, 5, 129, 30, 5, 55, 4,
+/* 6990 */ 79, 5, 129, 30, 4, 55, 4, 80, 4, 132,
+/* 7000 */ 30, 4, 55, 4, 79, 5, 129, 31, 3, 55,
+/* 7010 */ 4, 78, 5, 129, 31, 4, 55, 4, 77, 6,
+/* 7020 */ 129, 32, 3, 55, 4, 75, 7, 129, 32, 4,
+/* 7030 */ 55, 4, 73, 9, 129, 33, 4, 55, 4, 68,
+/* 7040 */ 13, 129, 34, 4, 55, 25, 129, 35, 5, 55,
+/* 7050 */ 24, 129, 37, 5, 55, 22, 129, 39, 4, 55,
+/* 7060 */ 20, 129, 55, 18, 129, 55, 16, 129, 55, 11,
+/* 7070 */ 129, 193, 129, 80, 4, 129, 30, 4, 80, 4,
+/* 7080 */ 130, 30, 78, 129, 30, 82, 129, 30, 85, 129,
+/* 7090 */ 30, 87, 129, 30, 88, 129, 30, 89, 129, 30,
+/* 7100 */ 90, 130, 30, 4, 80, 4, 115, 6, 129, 30,
+/* 7110 */ 4, 80, 4, 117, 4, 129, 80, 4, 105, 6,
+/* 7120 */ 117, 4, 129, 80, 4, 103, 10, 116, 5, 129,
+/* 7130 */ 80, 4, 102, 19, 129, 80, 4, 101, 19, 129,
+/* 7140 */ 101, 19, 129, 101, 18, 129, 102, 16, 129, 103,
+/* 7150 */ 12, 129, 105, 6, 129, 193, 129, 12, 10, 59,
+/* 7160 */ 11, 129, 9, 16, 55, 19, 129, 7, 20, 53,
+/* 7170 */ 23, 129, 6, 7, 23, 5, 32, 6, 51, 27,
+/* 7180 */ 129, 4, 7, 25, 16, 50, 29, 129, 3, 6,
+/* 7190 */ 27, 16, 49, 31, 129, 2, 6, 28, 16, 48,
+/* 7200 */ 33, 129, 1, 6, 27, 18, 47, 35, 129, 1,
+/* 7210 */ 6, 27, 31, 71, 12, 129, 1, 5, 26, 15,
+/* 7220 */ 44, 10, 75, 8, 129, 1, 5, 25, 14, 45,
+/* 7230 */ 7, 77, 7, 129, 1, 5, 25, 13, 45, 5,
+/* 7240 */ 79, 5, 129, 1, 5, 24, 14, 45, 4, 80,
+/* 7250 */ 4, 129, 1, 5, 24, 13, 45, 4, 80, 4,
+/* 7260 */ 129, 1, 5, 23, 14, 45, 4, 80, 4, 129,
+/* 7270 */ 1, 5, 23, 13, 45, 4, 80, 4, 129, 1,
+/* 7280 */ 6, 22, 13, 45, 5, 79, 5, 129, 1, 6,
+/* 7290 */ 21, 14, 45, 7, 77, 7, 129, 1, 7, 21,
+/* 7300 */ 13, 46, 8, 75, 8, 129, 1, 8, 20, 13,
+/* 7310 */ 46, 12, 71, 12, 129, 1, 10, 18, 15, 47,
+/* 7320 */ 35, 129, 2, 30, 48, 33, 129, 3, 29, 49,
+/* 7330 */ 32, 129, 4, 27, 50, 31, 129, 5, 25, 51,
+/* 7340 */ 27, 80, 2, 86, 4, 129, 7, 21, 53, 23,
+/* 7350 */ 80, 3, 85, 6, 129, 9, 17, 55, 19, 80,
+/* 7360 */ 12, 129, 12, 12, 59, 11, 81, 11, 129, 82,
+/* 7370 */ 10, 129, 84, 7, 129, 86, 4, 129, 193, 129,
+/* 7380 */ 30, 4, 117, 4, 130, 30, 91, 136, 30, 4,
+/* 7390 */ 72, 5, 129, 30, 4, 74, 5, 129, 75, 5,
+/* 7400 */ 129, 76, 5, 129, 76, 6, 129, 77, 6, 130,
+/* 7410 */ 77, 7, 130, 76, 8, 129, 30, 4, 75, 9,
+/* 7420 */ 129, 30, 4, 72, 12, 129, 30, 54, 129, 30,
+/* 7430 */ 53, 130, 30, 52, 129, 30, 51, 129, 30, 49,
+/* 7440 */ 129, 30, 46, 129, 30, 42, 129, 30, 4, 130,
+/* 7450 */ 193, 129, 30, 4, 80, 4, 129, 30, 4, 80,
+/* 7460 */ 4, 100, 6, 129, 30, 54, 98, 10, 129, 30,
+/* 7470 */ 54, 97, 12, 129, 30, 54, 96, 14, 131, 30,
+/* 7480 */ 54, 97, 12, 129, 30, 54, 98, 10, 129, 30,
+/* 7490 */ 54, 100, 6, 129, 30, 4, 130, 193, 129, 7,
+/* 7500 */ 6, 129, 4, 11, 129, 3, 13, 129, 2, 14,
+/* 7510 */ 129, 1, 15, 130, 1, 3, 6, 9, 129, 1,
+/* 7520 */ 3, 7, 6, 129, 1, 3, 130, 1, 4, 129,
+/* 7530 */ 1, 5, 80, 4, 129, 1, 7, 80, 4, 100,
+/* 7540 */ 6, 129, 2, 82, 98, 10, 129, 3, 81, 97,
+/* 7550 */ 12, 129, 4, 80, 96, 14, 129, 5, 79, 96,
+/* 7560 */ 14, 129, 7, 77, 96, 14, 129, 10, 74, 97,
+/* 7570 */ 12, 129, 14, 70, 98, 10, 129, 19, 65, 100,
+/* 7580 */ 6, 129, 193, 129, 30, 4, 117, 4, 130, 30,
+/* 7590 */ 91, 136, 30, 4, 57, 9, 129, 30, 4, 55,
+/* 7600 */ 12, 129, 52, 17, 129, 50, 20, 129, 48, 24,
+/* 7610 */ 129, 46, 27, 129, 44, 21, 69, 6, 129, 41,
+/* 7620 */ 22, 70, 6, 80, 4, 129, 30, 4, 39, 21,
+/* 7630 */ 72, 6, 80, 4, 129, 30, 4, 36, 22, 73,
+/* 7640 */ 11, 129, 30, 26, 75, 9, 129, 30, 23, 76,
+/* 7650 */ 8, 129, 30, 21, 78, 6, 129, 30, 19, 79,
+/* 7660 */ 5, 129, 30, 16, 80, 4, 129, 30, 14, 80,
+/* 7670 */ 4, 129, 30, 12, 129, 30, 10, 129, 30, 7,
+/* 7680 */ 129, 30, 5, 129, 30, 4, 130, 193, 129, 30,
+/* 7690 */ 4, 117, 4, 130, 30, 91, 136, 30, 4, 130,
+/* 7700 */ 193, 129, 30, 4, 80, 4, 130, 30, 54, 136,
+/* 7710 */ 30, 4, 72, 5, 129, 30, 4, 74, 5, 129,
+/* 7720 */ 75, 5, 129, 76, 5, 129, 30, 4, 75, 7,
+/* 7730 */ 129, 30, 4, 74, 9, 129, 30, 54, 132, 30,
+/* 7740 */ 53, 129, 30, 52, 129, 30, 51, 129, 30, 48,
+/* 7750 */ 129, 30, 4, 72, 5, 129, 30, 4, 74, 5,
+/* 7760 */ 129, 75, 5, 129, 76, 5, 129, 30, 4, 75,
+/* 7770 */ 7, 129, 30, 4, 74, 9, 129, 30, 54, 132,
+/* 7780 */ 30, 53, 129, 30, 52, 129, 30, 51, 129, 30,
+/* 7790 */ 48, 129, 30, 4, 130, 193, 129, 30, 4, 80,
+/* 7800 */ 4, 130, 30, 54, 136, 30, 4, 72, 5, 129,
+/* 7810 */ 30, 4, 74, 5, 129, 75, 5, 129, 76, 5,
+/* 7820 */ 129, 76, 6, 129, 77, 6, 130, 77, 7, 130,
+/* 7830 */ 76, 8, 129, 30, 4, 75, 9, 129, 30, 4,
+/* 7840 */ 72, 12, 129, 30, 54, 129, 30, 53, 130, 30,
+/* 7850 */ 52, 129, 30, 51, 129, 30, 49, 129, 30, 46,
+/* 7860 */ 129, 30, 42, 129, 30, 4, 130, 193, 129, 48,
+/* 7870 */ 18, 129, 43, 28, 129, 41, 32, 129, 39, 36,
+/* 7880 */ 129, 37, 40, 129, 35, 44, 129, 34, 46, 129,
+/* 7890 */ 33, 13, 68, 13, 129, 32, 9, 73, 9, 129,
+/* 7900 */ 32, 7, 75, 7, 129, 31, 6, 77, 6, 129,
+/* 7910 */ 31, 5, 78, 5, 129, 30, 5, 79, 5, 129,
+/* 7920 */ 30, 4, 80, 4, 132, 30, 5, 79, 5, 130,
+/* 7930 */ 31, 5, 78, 5, 129, 31, 6, 77, 6, 129,
+/* 7940 */ 32, 7, 75, 7, 129, 32, 9, 73, 9, 129,
+/* 7950 */ 33, 13, 68, 13, 129, 34, 46, 129, 35, 44,
+/* 7960 */ 129, 37, 40, 129, 39, 36, 129, 41, 32, 129,
+/* 7970 */ 43, 28, 129, 48, 18, 129, 193, 129, 1, 3,
+/* 7980 */ 80, 4, 130, 1, 83, 137, 37, 5, 72, 5,
+/* 7990 */ 129, 35, 5, 74, 5, 129, 33, 5, 76, 5,
+/* 8000 */ 129, 32, 5, 77, 5, 129, 31, 5, 78, 5,
+/* 8010 */ 129, 31, 4, 79, 4, 129, 30, 5, 79, 5,
+/* 8020 */ 131, 30, 6, 78, 6, 129, 30, 7, 77, 7,
+/* 8030 */ 129, 31, 8, 75, 8, 129, 31, 11, 72, 11,
+/* 8040 */ 129, 32, 15, 67, 15, 129, 33, 48, 129, 34,
+/* 8050 */ 46, 129, 35, 44, 129, 37, 40, 129, 39, 36,
+/* 8060 */ 129, 42, 30, 129, 46, 22, 129, 193, 129, 46,
+/* 8070 */ 22, 129, 42, 30, 129, 39, 36, 129, 37, 40,
+/* 8080 */ 129, 35, 44, 129, 34, 46, 129, 33, 48, 129,
+/* 8090 */ 32, 15, 67, 15, 129, 31, 11, 72, 11, 129,
+/* 8100 */ 31, 8, 75, 8, 129, 30, 7, 77, 7, 129,
+/* 8110 */ 30, 6, 78, 6, 129, 30, 5, 79, 5, 131,
+/* 8120 */ 31, 4, 79, 4, 129, 31, 5, 78, 5, 129,
+/* 8130 */ 32, 5, 77, 5, 129, 33, 5, 76, 5, 129,
+/* 8140 */ 35, 5, 74, 5, 129, 37, 5, 72, 5, 129,
+/* 8150 */ 1, 83, 136, 1, 3, 80, 4, 130, 193, 129,
+/* 8160 */ 30, 4, 80, 4, 130, 30, 54, 136, 30, 4,
+/* 8170 */ 68, 6, 129, 30, 4, 70, 6, 129, 71, 7,
+/* 8180 */ 129, 72, 7, 129, 73, 7, 129, 74, 7, 129,
+/* 8190 */ 74, 8, 129, 75, 8, 130, 69, 15, 129, 67,
+/* 8200 */ 17, 129, 66, 18, 129, 65, 19, 130, 65, 18,
+/* 8210 */ 130, 66, 16, 129, 67, 13, 129, 69, 8, 129,
+/* 8220 */ 193, 129, 30, 13, 64, 8, 129, 30, 13, 61,
+/* 8230 */ 14, 129, 30, 13, 59, 18, 129, 30, 13, 57,
+/* 8240 */ 22, 129, 33, 8, 56, 24, 129, 32, 7, 55,
+/* 8250 */ 26, 129, 32, 6, 54, 28, 129, 31, 6, 53,
+/* 8260 */ 16, 77, 6, 129, 31, 5, 53, 14, 79, 4,
+/* 8270 */ 129, 30, 5, 52, 14, 80, 4, 129, 30, 5,
+/* 8280 */ 52, 13, 80, 4, 129, 30, 4, 52, 13, 80,
+/* 8290 */ 4, 129, 30, 4, 52, 12, 80, 4, 129, 30,
+/* 8300 */ 4, 51, 13, 80, 4, 130, 30, 4, 50, 13,
+/* 8310 */ 79, 5, 129, 30, 4, 50, 13, 78, 5, 129,
+/* 8320 */ 30, 5, 49, 14, 77, 6, 129, 31, 4, 49,
+/* 8330 */ 13, 76, 6, 129, 31, 5, 48, 14, 75, 7,
+/* 8340 */ 129, 32, 5, 47, 14, 73, 8, 129, 32, 6,
+/* 8350 */ 45, 16, 71, 13, 129, 33, 27, 71, 13, 129,
+/* 8360 */ 34, 26, 71, 13, 129, 35, 24, 71, 13, 129,
+/* 8370 */ 37, 20, 129, 39, 16, 129, 43, 9, 129, 193,
+/* 8380 */ 129, 80, 4, 131, 41, 56, 129, 37, 60, 129,
+/* 8390 */ 35, 62, 129, 33, 64, 129, 32, 65, 129, 31,
+/* 8400 */ 66, 129, 30, 67, 130, 30, 11, 80, 4, 129,
+/* 8410 */ 30, 9, 80, 4, 129, 30, 8, 80, 4, 129,
+/* 8420 */ 31, 7, 80, 4, 129, 31, 6, 129, 32, 5,
+/* 8430 */ 129, 33, 5, 129, 35, 4, 129, 38, 3, 129,
+/* 8440 */ 193, 129, 80, 4, 130, 42, 42, 129, 38, 46,
+/* 8450 */ 129, 35, 49, 129, 33, 51, 129, 32, 52, 129,
+/* 8460 */ 31, 53, 130, 30, 54, 129, 30, 12, 129, 30,
+/* 8470 */ 9, 129, 30, 8, 129, 30, 7, 130, 31, 6,
+/* 8480 */ 130, 32, 6, 129, 33, 5, 129, 34, 5, 129,
+/* 8490 */ 35, 5, 80, 4, 129, 37, 5, 80, 4, 129,
+/* 8500 */ 30, 54, 136, 30, 4, 130, 193, 129, 80, 4,
+/* 8510 */ 130, 77, 7, 129, 74, 10, 129, 70, 14, 129,
+/* 8520 */ 66, 18, 129, 62, 22, 129, 59, 25, 129, 55,
+/* 8530 */ 29, 129, 51, 33, 129, 47, 37, 129, 44, 32,
+/* 8540 */ 80, 4, 129, 40, 32, 80, 4, 129, 36, 32,
+/* 8550 */ 129, 32, 33, 129, 30, 31, 129, 33, 24, 129,
+/* 8560 */ 36, 17, 129, 40, 12, 129, 44, 12, 129, 48,
+/* 8570 */ 12, 129, 51, 13, 129, 55, 13, 129, 59, 13,
+/* 8580 */ 80, 4, 129, 63, 13, 80, 4, 129, 67, 17,
+/* 8590 */ 129, 71, 13, 129, 74, 10, 129, 78, 6, 129,
+/* 8600 */ 80, 4, 131, 193, 129, 80, 4, 130, 77, 7,
+/* 8610 */ 129, 74, 10, 129, 70, 14, 129, 66, 18, 129,
+/* 8620 */ 62, 22, 129, 59, 25, 129, 55, 29, 129, 51,
+/* 8630 */ 33, 129, 47, 37, 129, 44, 32, 80, 4, 129,
+/* 8640 */ 40, 32, 80, 4, 129, 36, 32, 129, 32, 33,
+/* 8650 */ 129, 30, 31, 129, 33, 24, 129, 36, 17, 129,
+/* 8660 */ 40, 12, 129, 44, 12, 129, 47, 13, 129, 44,
+/* 8670 */ 20, 129, 40, 28, 129, 36, 31, 129, 32, 32,
+/* 8680 */ 129, 30, 30, 129, 33, 24, 129, 36, 17, 129,
+/* 8690 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51,
+/* 8700 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129,
+/* 8710 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13,
+/* 8720 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131,
+/* 8730 */ 193, 129, 30, 4, 80, 4, 130, 30, 4, 79,
+/* 8740 */ 5, 129, 30, 5, 77, 7, 129, 30, 6, 74,
+/* 8750 */ 10, 129, 30, 8, 72, 12, 129, 30, 11, 69,
+/* 8760 */ 15, 129, 30, 13, 67, 17, 129, 30, 4, 37,
+/* 8770 */ 8, 64, 20, 129, 30, 4, 39, 8, 62, 22,
+/* 8780 */ 129, 41, 8, 59, 25, 129, 43, 8, 57, 27,
+/* 8790 */ 129, 45, 8, 55, 22, 80, 4, 129, 47, 27,
+/* 8800 */ 80, 4, 129, 49, 23, 129, 47, 22, 129, 44,
+/* 8810 */ 23, 129, 42, 22, 129, 30, 4, 39, 27, 129,
+/* 8820 */ 30, 4, 37, 31, 129, 30, 27, 62, 8, 129,
+/* 8830 */ 30, 25, 64, 8, 129, 30, 22, 66, 8, 80,
+/* 8840 */ 4, 129, 30, 20, 68, 8, 80, 4, 129, 30,
+/* 8850 */ 17, 70, 8, 80, 4, 129, 30, 15, 73, 11,
+/* 8860 */ 129, 30, 12, 75, 9, 129, 30, 10, 77, 7,
+/* 8870 */ 129, 30, 7, 79, 5, 129, 30, 5, 80, 4,
+/* 8880 */ 129, 30, 4, 80, 4, 130, 193, 129, 4, 5,
+/* 8890 */ 80, 4, 129, 2, 9, 80, 4, 129, 1, 11,
+/* 8900 */ 77, 7, 129, 1, 12, 74, 10, 129, 1, 12,
+/* 8910 */ 70, 14, 129, 1, 12, 66, 18, 129, 1, 11,
+/* 8920 */ 62, 22, 129, 2, 9, 59, 25, 129, 4, 11,
+/* 8930 */ 55, 29, 129, 7, 12, 51, 33, 129, 10, 12,
+/* 8940 */ 47, 37, 129, 14, 12, 44, 32, 80, 4, 129,
+/* 8950 */ 17, 13, 40, 32, 80, 4, 129, 21, 13, 36,
+/* 8960 */ 32, 129, 25, 40, 129, 29, 32, 129, 33, 24,
+/* 8970 */ 129, 36, 17, 129, 40, 12, 129, 44, 12, 129,
+/* 8980 */ 48, 12, 129, 51, 13, 129, 55, 13, 129, 59,
+/* 8990 */ 13, 80, 4, 129, 63, 13, 80, 4, 129, 67,
+/* 9000 */ 17, 129, 71, 13, 129, 74, 10, 129, 78, 6,
+/* 9010 */ 129, 80, 4, 131, 193, 129, 30, 1, 71, 13,
+/* 9020 */ 129, 30, 3, 71, 13, 129, 30, 6, 71, 13,
+/* 9030 */ 129, 30, 9, 75, 9, 129, 30, 11, 77, 7,
+/* 9040 */ 129, 30, 14, 79, 5, 129, 30, 17, 79, 5,
+/* 9050 */ 129, 30, 19, 80, 4, 129, 30, 22, 80, 4,
+/* 9060 */ 129, 30, 25, 80, 4, 129, 30, 27, 80, 4,
+/* 9070 */ 129, 30, 4, 36, 24, 80, 4, 129, 30, 4,
+/* 9080 */ 38, 25, 80, 4, 129, 30, 4, 41, 24, 80,
+/* 9090 */ 4, 129, 30, 4, 44, 24, 80, 4, 129, 30,
+/* 9100 */ 4, 46, 25, 80, 4, 129, 30, 4, 49, 25,
+/* 9110 */ 80, 4, 129, 30, 4, 52, 24, 80, 4, 129,
+/* 9120 */ 30, 4, 54, 30, 129, 30, 4, 57, 27, 129,
+/* 9130 */ 30, 4, 59, 25, 129, 30, 4, 62, 22, 129,
+/* 9140 */ 30, 4, 65, 19, 129, 30, 5, 67, 17, 129,
+/* 9150 */ 30, 5, 70, 14, 129, 30, 7, 73, 11, 129,
+/* 9160 */ 30, 9, 76, 8, 129, 30, 13, 78, 6, 129,
+/* 9170 */ 30, 13, 81, 3, 129, 30, 13, 129, 193, 2,
+/* 9180 */ 9, 59, 25, 129, 4, 11, 55, 29, 129, 7,
+/* 9190 */ 12, 51, 33, 129, 10, 12, 47, 37, 129, 14,
+/* 9200 */ 12, 44, 32, 80, 4, 129, 17, 13, 40, 32,
+/* 9210 */ 80, 4, 129, 21, 13, 36, 32, 129, 25, 40,
+/* 9220 */ 129, 29, 32, 129, 33, 24, 129, 36, 17, 129,
+/* 9230 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51,
+/* 9240 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129,
+/* 9250 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13,
+/* 9260 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131,
+/* 9270 */ 193
+};
+
+char line[DWIDTH];
+char *message;
+char print[DWIDTH];
+int debug, i, j, linen, max, nchars, pc, term, trace, x, y;
+int width = DWIDTH; /* -w option: scrunch letters to 80 columns */
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "w:td")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ case 'w':
+ width = atoi(optarg);
+ if (width <= 0)
+ errx(1, "illegal argument for -w option");
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ for (i = 0; i < width; i++) {
+ j = i * 132 / width;
+ print[j] = 1;
+ }
+
+ /* Have now read in the data. Next get the message to be printed. */
+ if (*argv) {
+ for(i=0, j=0; i < argc; i++)
+ j += strlen(argv[i]) + 1;
+ if ((message = malloc((size_t)j)) == NULL)
+ err(1, "malloc");
+ strcpy(message, *argv);
+ while (*++argv) {
+ strcat(message, " ");
+ strcat(message, *argv);
+ }
+ nchars = strlen(message);
+ } else {
+ if ((message = malloc((size_t)MAXMSG)) == NULL)
+ err(1, "malloc");
+ fprintf(stderr,"Message: ");
+ if (fgets(message, MAXMSG, stdin) == NULL) {
+ nchars = 0;
+ message[0] = '\0';
+ } else {
+ nchars = strlen(message);
+
+ /* Get rid of newline. */
+ if (message[nchars - 1] == '\n')
+ message[--nchars] = '\0';
+ }
+ }
+
+ /* some debugging print statements */
+ if (debug) {
+ printf("const int asc_ptr[NCHARS] = {\n");
+ for (i = 0; i < 128; i++) {
+ printf("%4d, ",asc_ptr[i]);
+ if ((i+1) % 8 == 0)
+ printf("\n");
+ }
+ printf("};\nconst unsigned char data_table[NBYTES] = {\n");
+ printf("/* ");
+ for (i = 0; i < 10; i++) printf(" %3d ",i);
+ printf("*/\n");
+ for (i = 0; i < NBYTES; i += 10) {
+ printf("/* %4d */ ",i);
+ for (j = i; j < i+10; j++) {
+ x = data_table[j] & 0377;
+ printf(" %3d, ",x);
+ }
+ putchar('\n');
+ }
+ printf("};\n");
+ }
+
+ /* check message to make sure it's legal */
+ j = 0;
+ for (i = 0; i < nchars; i++)
+ if ((u_char) message[i] >= NCHARS ||
+ asc_ptr[(u_char) message[i]] == 0) {
+ warnx("the character '%c' is not in my character set",
+ message[i]);
+ j++;
+ }
+ if (j)
+ exit(1);
+
+ if (trace)
+ printf("Message '%s' is OK\n",message);
+ /* Now have message. Print it one character at a time. */
+
+ for (i = 0; i < nchars; i++) {
+ if (trace)
+ printf("Char #%d: %c\n", i, message[i]);
+ for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+ pc = asc_ptr[(u_char) message[i]];
+ term = 0;
+ max = 0;
+ linen = 0;
+ while (!term) {
+ if (pc < 0 || pc > NBYTES) {
+ printf("bad pc: %d\n",pc);
+ exit(1);
+ }
+ x = data_table[pc] & 0377;
+ if (trace)
+ printf("pc=%d, term=%d, max=%d, linen=%d, x=%d\n",pc,term,max,linen,x);
+ if (x >= 128) {
+ if (x>192) term++;
+ x = x & 63;
+ while (x--) {
+ if (print[linen++]) {
+ for (j=0; j <= max; j++)
+ if (print[j])
+ putchar(line[j]);
+ putchar('\n');
+ }
+ }
+ for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+ pc++;
+ }
+ else {
+ y = data_table[pc+1];
+ /* compensate for narrow teminals */
+#ifdef notdef
+ x = (x*width + (DWIDTH/2)) / DWIDTH;
+ y = (y*width + (DWIDTH/2)) / DWIDTH;
+#endif
+ max = x+y;
+ while (x < max) line[x++] = '#';
+ pc += 2;
+ if (trace)
+ printf("x=%d, y=%d, max=%d\n",x,y,max);
+ }
+ }
+ }
+
+ free(message);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: banner [-d] [-t] [-w width] message ...\n");
+ exit(1);
+}
diff --git a/text_cmds/cat/cat.1 b/text_cmds/cat/cat.1
new file mode 100644
index 0000000..892c990
--- /dev/null
+++ b/text_cmds/cat/cat.1
@@ -0,0 +1,201 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cat.1 8.3 (Berkeley) 5/2/95
+.\" $FreeBSD: src/bin/cat/cat.1,v 1.25 2005/01/16 16:41:55 ru Exp $
+.\"
+.Dd March 21, 2004
+.Dt CAT 1
+.Os
+.Sh NAME
+.Nm cat
+.Nd concatenate and print files
+.Sh SYNOPSIS
+.Nm
+.Op Fl benstuv
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads files sequentially, writing them to the standard output.
+The
+.Ar file
+operands are processed in command-line order.
+If
+.Ar file
+is a single dash
+.Pq Sq \&-
+or absent,
+.Nm
+reads from the standard input.
+If
+.Ar file
+is a
+.Ux
+domain socket,
+.Nm
+connects to it and then reads it until
+.Dv EOF .
+This complements the
+.Ux
+domain binding capability available in
+.Xr inetd 8 .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Number the non-blank output lines, starting at 1.
+.It Fl e
+Display non-printing characters (see the
+.Fl v
+option), and display a dollar sign
+.Pq Ql \&$
+at the end of each line.
+.It Fl n
+Number the output lines, starting at 1.
+.It Fl s
+Squeeze multiple adjacent empty lines, causing the output to be
+single spaced.
+.It Fl t
+Display non-printing characters (see the
+.Fl v
+option), and display tab characters as
+.Ql ^I .
+.It Fl u
+Disable output buffering.
+.It Fl v
+Display non-printing characters so they are visible.
+Control characters print as
+.Ql ^X
+for control-X; the delete
+character (octal 0177) prints as
+.Ql ^? .
+.Pf Non- Tn ASCII
+characters (with the high bit set) are printed as
+.Ql M-
+(for meta) followed by the character for the low 7 bits.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The command:
+.Pp
+.Dl "cat file1"
+.Pp
+will print the contents of
+.Ar file1
+to the standard output.
+.Pp
+The command:
+.Pp
+.Dl "cat file1 file2 > file3"
+.Pp
+will sequentially print the contents of
+.Ar file1
+and
+.Ar file2
+to the file
+.Ar file3 ,
+truncating
+.Ar file3
+if it already exists.
+See the manual page for your shell (i.e.,
+.Xr sh 1 )
+for more information on redirection.
+.Pp
+The command:
+.Pp
+.Dl "cat file1 - file2 - file3"
+.Pp
+will print the contents of
+.Ar file1 ,
+print data it receives from the standard input until it receives an
+.Dv EOF
+.Pq Sq ^D
+character, print the contents of
+.Ar file2 ,
+read and output contents of the standard input again, then finally output
+the contents of
+.Ar file3 .
+Note that if the standard input referred to a file, the second dash
+on the command-line would have no effect, since the entire contents of the file
+would have already been read and printed by
+.Nm
+when it encountered the first
+.Ql \&-
+operand.
+.Sh SEE ALSO
+.Xr head 1 ,
+.Xr more 1 ,
+.Xr pr 1 ,
+.Xr sh 1 ,
+.Xr tail 1 ,
+.Xr vis 1 ,
+.Xr zcat 1 ,
+.Xr setbuf 3
+.Rs
+.%A Rob Pike
+.%T "UNIX Style, or cat -v Considered Harmful"
+.%J "USENIX Summer Conference Proceedings"
+.%D 1983
+.Re
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.2-92
+specification.
+.Pp
+The flags
+.Op Fl benstv
+are extensions to the specification.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v1 .
+.An Dennis Ritchie
+designed and wrote the first man page.
+It appears to have been
+.Xr cat 1 .
+.Sh BUGS
+Because of the shell language mechanism used to perform output
+redirection, the command
+.Dq Li cat file1 file2 > file1
+will cause the original data in file1 to be destroyed!
+.Pp
+The
+.Nm
+utility does not recognize multibyte characters when the
+.Fl t
+or
+.Fl v
+option is in effect.
diff --git a/text_cmds/cat/cat.c b/text_cmds/cat/cat.c
new file mode 100644
index 0000000..e933951
--- /dev/null
+++ b/text_cmds/cat/cat.c
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+#endif
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/cat/cat.c,v 1.32 2005/01/10 08:39:20 imp Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifndef NO_UDOM_SUPPORT
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+
+int bflag, eflag, nflag, sflag, tflag, vflag;
+int rval;
+const char *filename;
+
+static void usage(void);
+static void scanfiles(char *argv[], int cooked);
+static void cook_cat(FILE *);
+static void raw_cat(int);
+
+#ifndef NO_UDOM_SUPPORT
+static int udom_open(const char *path, int flags);
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "benstuv")) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = nflag = 1; /* -b implies -n */
+ break;
+ case 'e':
+ eflag = vflag = 1; /* -e implies -v */
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = vflag = 1; /* -t implies -v */
+ break;
+ case 'u':
+ setbuf(stdout, NULL);
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+
+ if (bflag || eflag || nflag || sflag || tflag || vflag)
+ scanfiles(argv, 1);
+ else
+ scanfiles(argv, 0);
+ if (fclose(stdout))
+ err(1, "stdout");
+ exit(rval);
+ /* NOTREACHED */
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: cat [-benstuv] [file ...]\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+static void
+scanfiles(char *argv[], int cooked)
+{
+ int i = 0;
+ char *path;
+ FILE *fp;
+
+ while ((path = argv[i]) != NULL || i == 0) {
+ int fd;
+
+ if (path == NULL || strcmp(path, "-") == 0) {
+ filename = "stdin";
+ fd = STDIN_FILENO;
+ } else {
+ filename = path;
+ fd = open(path, O_RDONLY);
+#ifndef NO_UDOM_SUPPORT
+ if (fd < 0 && errno == EOPNOTSUPP)
+ fd = udom_open(path, O_RDONLY);
+#endif
+ }
+ if (fd < 0) {
+ warn("%s", path);
+ rval = 1;
+ } else if (cooked) {
+ if (fd == STDIN_FILENO)
+ cook_cat(stdin);
+ else {
+ fp = fdopen(fd, "r");
+ cook_cat(fp);
+ fclose(fp);
+ }
+ } else {
+ raw_cat(fd);
+ if (fd != STDIN_FILENO)
+ close(fd);
+ }
+ if (path == NULL)
+ break;
+ ++i;
+ }
+}
+
+static void
+cook_cat(FILE *fp)
+{
+ int ch, gobble, line, prev;
+
+ /* Reset EOF condition on stdin. */
+ if (fp == stdin && feof(stdin))
+ clearerr(stdin);
+
+ line = gobble = 0;
+ for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+ if (prev == '\n') {
+ if (sflag) {
+ if (ch == '\n') {
+ if (gobble)
+ continue;
+ gobble = 1;
+ } else
+ gobble = 0;
+ }
+ if (nflag && (!bflag || ch != '\n')) {
+ (void)fprintf(stdout, "%6d\t", ++line);
+ if (ferror(stdout))
+ break;
+ }
+ }
+ if (ch == '\n') {
+ if (eflag && putchar('$') == EOF)
+ break;
+ } else if (ch == '\t') {
+ if (tflag) {
+ if (putchar('^') == EOF || putchar('I') == EOF)
+ break;
+ continue;
+ }
+ } else if (vflag) {
+ if (!isascii(ch) && !isprint(ch)) {
+ if (putchar('M') == EOF || putchar('-') == EOF)
+ break;
+ ch = toascii(ch);
+ }
+ if (iscntrl(ch)) {
+ if (putchar('^') == EOF ||
+ putchar(ch == '\177' ? '?' :
+ ch | 0100) == EOF)
+ break;
+ continue;
+ }
+ }
+ if (putchar(ch) == EOF)
+ break;
+ }
+ if (ferror(fp)) {
+ warn("%s", filename);
+ rval = 1;
+ clearerr(fp);
+ }
+ if (ferror(stdout))
+ err(1, "stdout");
+}
+
+static void
+raw_cat(int rfd)
+{
+ int off, wfd;
+ ssize_t nr, nw;
+ static size_t bsize;
+ static char *buf = NULL;
+ struct stat sbuf;
+
+ wfd = fileno(stdout);
+ if (buf == NULL) {
+ if (fstat(wfd, &sbuf))
+ err(1, "%s", filename);
+ bsize = MAX(sbuf.st_blksize, 1024);
+ if ((buf = malloc(bsize)) == NULL)
+ err(1, "buffer");
+ }
+ while ((nr = read(rfd, buf, bsize)) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
+ err(1, "stdout");
+ if (nr < 0) {
+ warn("%s", filename);
+ rval = 1;
+ }
+}
+
+#ifndef NO_UDOM_SUPPORT
+
+static int
+udom_open(const char *path, int flags)
+{
+ struct sockaddr_un sou;
+ int fd;
+ unsigned int len;
+
+ bzero(&sou, sizeof(sou));
+
+ /*
+ * Construct the unix domain socket address and attempt to connect
+ */
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd >= 0) {
+ sou.sun_family = AF_UNIX;
+ if ((len = strlcpy(sou.sun_path, path,
+ sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ len = offsetof(struct sockaddr_un, sun_path[len+1]);
+
+ if (connect(fd, (void *)&sou, len) < 0) {
+ close(fd);
+ fd = -1;
+ }
+ }
+
+ /*
+ * handle the open flags by shutting down appropriate directions
+ */
+ if (fd >= 0) {
+ switch(flags & O_ACCMODE) {
+ case O_RDONLY:
+ if (shutdown(fd, SHUT_WR) == -1)
+ warn(NULL);
+ break;
+ case O_WRONLY:
+ if (shutdown(fd, SHUT_RD) == -1)
+ warn(NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ return(fd);
+}
+
+#endif
diff --git a/text_cmds/col/README b/text_cmds/col/README
new file mode 100644
index 0000000..89e4ce4
--- /dev/null
+++ b/text_cmds/col/README
@@ -0,0 +1,48 @@
+# @(#)README 8.1 (Berkeley) 6/6/93
+#
+# $FreeBSD: src/usr.bin/col/README,v 1.2 2001/06/17 04:24:16 mikeh Exp $
+
+col - filter out reverse line feeds.
+
+Options are:
+ -b do not print any backspaces (last character written is printed)
+ -f allow half line feeds in output, by default characters between
+ lines are pushed to the line below
+ -p force unknown control sequences to be passed through unchanged
+ -x do not compress spaces into tabs.
+ -l num keep (at least) num lines in memory, 128 are kept by default
+
+In the 32V source code to col(1) the default behavior was to NOT compress
+spaces into tabs. There was a -h option which caused it to compress spaces
+into tabs. There was no -x flag.
+
+The 32V documentation, however, was consistent with the SVID (actually, V7
+at the time) and documented a -x flag (as defined above) while making no
+mention of a -h flag. Just before 4.3BSD went out, CSRG updated the manual
+page to reflect the way the code worked. Suspecting that this was probably
+the wrong way to go, this version adopts the SVID defaults, and no longer
+documents the -h option.
+
+Known differences between AT&T's col and this one (# is delimiter):
+ Input AT&T col this col
+ #\nabc\E7def\n# # def\nabc\r# # def\nabc\n#
+ #a# ## #a\n#
+ - last line always ends with at least one \n (or \E9)
+ #1234567 8\n# #1234567\t8\n# #1234567 8\n#
+ - single space not expanded to tab
+ -f #a\E8b\n# #ab\n# # b\E9\ra\n#
+ - can back up past first line (as far as you want) so you
+ *can* have a super script on the first line
+ #\E9_\ba\E8\nb\n# #\n_\bb\ba\n# #\n_\ba\bb\n#
+ - always print last character written to a position,
+ AT&T col claims to do this but doesn't.
+
+If a character is to be placed on a line that has been flushed, a warning
+is produced (the AT&T col is silent). The -l flag (not in AT&T col) can
+be used to increase the number of lines buffered to avoid the problem.
+
+General algorithm: a limited number of lines are buffered in a linked
+list. When a printable character is read, it is put in the buffer of
+the current line along with the column it's supposed to be in. When
+a line is flushed, the characters in the line are sorted according to
+column and then printed.
diff --git a/text_cmds/col/col.1 b/text_cmds/col/col.1
new file mode 100644
index 0000000..2bac8b8
--- /dev/null
+++ b/text_cmds/col/col.1
@@ -0,0 +1,156 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Michael Rendell.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)col.1 8.1 (Berkeley) 6/29/93
+.\" $FreeBSD: src/usr.bin/col/col.1,v 1.19 2005/02/13 22:25:20 ru Exp $
+.\"
+.Dd August 4, 2004
+.Dt COL 1
+.Os
+.Sh NAME
+.Nm col
+.Nd filter reverse line feeds from input
+.Sh SYNOPSIS
+.Nm
+.Op Fl bfhpx
+.Op Fl l Ar num
+.Sh DESCRIPTION
+The
+.Nm
+utility filters out reverse (and half reverse) line feeds so that the output is
+in the correct order with only forward and half forward line
+feeds, and replaces white-space characters with tabs where possible.
+This can be useful in processing the output of
+.Xr nroff 1
+and
+.Xr tbl 1 .
+.Pp
+The
+.Nm
+utility reads from the standard input and writes to the standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Do not output any backspaces, printing only the last character
+written to each column position.
+.It Fl f
+Forward half line feeds are permitted (``fine'' mode).
+Normally characters printed on a half line boundary are printed
+on the following line.
+.It Fl h
+Do not output multiple spaces instead of tabs (default).
+.It Fl l Ar num
+Buffer at least
+.Ar num
+lines in memory.
+By default, 128 lines are buffered.
+.It Fl p
+Force unknown control sequences to be passed through unchanged.
+Normally,
+.Nm
+will filter out any control sequences from the input other than those
+recognized and interpreted by itself, which are listed below.
+.It Fl x
+Output multiple spaces instead of tabs.
+.El
+.Pp
+The control sequences for carriage motion that
+.Nm
+understands and their decimal values are listed in the following
+table:
+.Pp
+.Bl -tag -width "carriage return" -compact
+.It ESC\-7
+reverse line feed (escape then 7)
+.It ESC\-8
+half reverse line feed (escape then 8)
+.It ESC\-9
+half forward line feed (escape then 9)
+.It backspace
+moves back one column (8); ignored in the first column
+.It carriage return
+(13)
+.It newline
+forward line feed (10); also does carriage return
+.It shift in
+shift to normal character set (15)
+.It shift out
+shift to alternate character set (14)
+.It space
+moves forward one column (32)
+.It tab
+moves forward to next tab stop (9)
+.It vertical tab
+reverse line feed (11)
+.El
+.Pp
+All unrecognized control characters and escape sequences are
+discarded.
+.Pp
+The
+.Nm
+utility keeps track of the character set as characters are read and makes
+sure the character set is correct when they are output.
+.Pp
+If the input attempts to back up to the last flushed line,
+.Nm
+will display a warning message.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr colcrt 1 ,
+.Xr expand 1 ,
+.Xr nroff 1 ,
+.Xr tbl 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -susv2 .
+.Sh HISTORY
+A
+.Nm
+command
+appeared in
+.At v6 .
diff --git a/text_cmds/col/col.c b/text_cmds/col/col.c
new file mode 100644
index 0000000..758cbec
--- /dev/null
+++ b/text_cmds/col/col.c
@@ -0,0 +1,552 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of the Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)col.c 8.5 (Berkeley) 5/4/95";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/col/col.c,v 1.19 2004/07/29 07:28:26 tjr Exp $");
+
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define BS '\b' /* backspace */
+#define TAB '\t' /* tab */
+#define SPACE ' ' /* space */
+#define NL '\n' /* newline */
+#define CR '\r' /* carriage return */
+#define ESC '\033' /* escape */
+#define SI '\017' /* shift in to normal character set */
+#define SO '\016' /* shift out to alternate character set */
+#define VT '\013' /* vertical tab (aka reverse line feed) */
+#define RLF '\007' /* ESC-07 reverse line feed */
+#define RHLF '\010' /* ESC-010 reverse half-line feed */
+#define FHLF '\011' /* ESC-011 forward half-line feed */
+
+/* build up at least this many lines before flushing them out */
+#define BUFFER_MARGIN 32
+
+typedef char CSET;
+
+typedef struct char_str {
+#define CS_NORMAL 1
+#define CS_ALTERNATE 2
+ short c_column; /* column character is in */
+ CSET c_set; /* character set (currently only 2) */
+ wchar_t c_char; /* character in question */
+ int c_width; /* character width */
+} CHAR;
+
+typedef struct line_str LINE;
+struct line_str {
+ CHAR *l_line; /* characters on the line */
+ LINE *l_prev; /* previous line */
+ LINE *l_next; /* next line */
+ int l_lsize; /* allocated sizeof l_line */
+ int l_line_len; /* strlen(l_line) */
+ int l_needs_sort; /* set if chars went in out of order */
+ int l_max_col; /* max column in the line */
+};
+
+LINE *alloc_line(void);
+void dowarn(int);
+void flush_line(LINE *);
+void flush_lines(int);
+void flush_blanks(void);
+void free_line(LINE *);
+void usage(void);
+
+CSET last_set; /* char_set of last char printed */
+LINE *lines;
+int compress_spaces; /* if doing space -> tab conversion */
+int fine; /* if `fine' resolution (half lines) */
+int max_bufd_lines; /* max # lines to keep in memory */
+int nblank_lines; /* # blanks after last flushed line */
+int no_backspaces; /* if not to output any backspaces */
+int pass_unknown_seqs; /* pass unknown control sequences */
+
+#define PUTC(ch) \
+ do { \
+ if (putwchar(ch) == WEOF) \
+ errx(1, "write error"); \
+ } while (0)
+
+int
+main(int argc, char **argv)
+{
+ wint_t ch;
+ CHAR *c;
+ CSET cur_set; /* current character set */
+ LINE *l; /* current line */
+ int extra_lines; /* # of lines above first line */
+ int cur_col; /* current column */
+ int cur_line; /* line number of current position */
+ int max_line; /* max value of cur_line */
+ int this_line; /* line l points to */
+ int nflushd_lines; /* number of lines that were flushed */
+ int adjust, opt, warned, width;
+
+ (void)setlocale(LC_CTYPE, "");
+
+ max_bufd_lines = 128;
+ compress_spaces = 1; /* compress spaces into tabs */
+ while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
+ switch (opt) {
+ case 'b': /* do not output backspaces */
+ no_backspaces = 1;
+ break;
+ case 'f': /* allow half forward line feeds */
+ fine = 1;
+ break;
+ case 'h': /* compress spaces into tabs */
+ compress_spaces = 1;
+ break;
+ case 'l': /* buffered line count */
+ if ((max_bufd_lines = atoi(optarg)) <= 0)
+ errx(1, "bad -l argument %s", optarg);
+ break;
+ case 'p': /* pass unknown control sequences */
+ pass_unknown_seqs = 1;
+ break;
+ case 'x': /* do not compress spaces into tabs */
+ compress_spaces = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (optind != argc)
+ usage();
+
+ /* this value is in half lines */
+ max_bufd_lines *= 2;
+
+ adjust = cur_col = extra_lines = warned = 0;
+ cur_line = max_line = nflushd_lines = this_line = 0;
+ cur_set = last_set = CS_NORMAL;
+ lines = l = alloc_line();
+
+ while ((ch = getwchar()) != WEOF) {
+ if (!iswgraph(ch)) {
+ switch (ch) {
+ case BS: /* can't go back further */
+ if (cur_col == 0)
+ continue;
+ --cur_col;
+ continue;
+ case CR:
+ cur_col = 0;
+ continue;
+ case ESC: /* just ignore EOF */
+ switch(getwchar()) {
+ case RLF:
+ cur_line -= 2;
+ break;
+ case RHLF:
+ cur_line--;
+ break;
+ case FHLF:
+ cur_line++;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ }
+ continue;
+ case NL:
+ cur_line += 2;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ cur_col = 0;
+ continue;
+ case SPACE:
+ ++cur_col;
+ continue;
+ case SI:
+ cur_set = CS_NORMAL;
+ continue;
+ case SO:
+ cur_set = CS_ALTERNATE;
+ continue;
+ case TAB: /* adjust column */
+ cur_col |= 7;
+ ++cur_col;
+ continue;
+ case VT:
+ cur_line -= 2;
+ continue;
+ }
+ if (iswspace(ch)) {
+ if ((width = wcwidth(ch)) > 0)
+ cur_col += width;
+ continue;
+ }
+ if (!pass_unknown_seqs)
+ continue;
+ }
+
+ /* Must stuff ch in a line - are we at the right one? */
+ if (cur_line != this_line - adjust) {
+ LINE *lnew;
+ int nmove;
+
+ adjust = 0;
+ nmove = cur_line - this_line;
+ if (!fine) {
+ /* round up to next line */
+ if (cur_line & 1) {
+ adjust = 1;
+ nmove++;
+ }
+ }
+ if (nmove < 0) {
+ for (; nmove < 0 && l->l_prev; nmove++)
+ l = l->l_prev;
+ if (nmove) {
+ if (nflushd_lines == 0) {
+ /*
+ * Allow backup past first
+ * line if nothing has been
+ * flushed yet.
+ */
+ for (; nmove < 0; nmove++) {
+ lnew = alloc_line();
+ l->l_prev = lnew;
+ lnew->l_next = l;
+ l = lines = lnew;
+ extra_lines++;
+ }
+ } else {
+ if (!warned++)
+ dowarn(cur_line);
+ cur_line -= nmove;
+ }
+ }
+ } else {
+ /* may need to allocate here */
+ for (; nmove > 0 && l->l_next; nmove--)
+ l = l->l_next;
+ for (; nmove > 0; nmove--) {
+ lnew = alloc_line();
+ lnew->l_prev = l;
+ l->l_next = lnew;
+ l = lnew;
+ }
+ }
+ this_line = cur_line + adjust;
+ nmove = this_line - nflushd_lines;
+ if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
+ nflushd_lines += nmove - max_bufd_lines;
+ flush_lines(nmove - max_bufd_lines);
+ }
+ }
+ /* grow line's buffer? */
+ if (l->l_line_len + 1 >= l->l_lsize) {
+ int need;
+
+ need = l->l_lsize ? l->l_lsize * 2 : 90;
+ if ((l->l_line = realloc(l->l_line,
+ (unsigned)need * sizeof(CHAR))) == NULL)
+ err(1, (char *)NULL);
+ l->l_lsize = need;
+ }
+ c = &l->l_line[l->l_line_len++];
+ c->c_char = ch;
+ c->c_set = cur_set;
+ c->c_column = cur_col;
+ c->c_width = wcwidth(ch);
+ /*
+ * If things are put in out of order, they will need sorting
+ * when it is flushed.
+ */
+ if (cur_col < l->l_max_col)
+ l->l_needs_sort = 1;
+ else
+ l->l_max_col = cur_col;
+ if (c->c_width > 0)
+ cur_col += c->c_width;
+ }
+ if (ferror(stdin))
+ err(1, NULL);
+ if (max_line == 0)
+ exit(0); /* no lines, so just exit */
+
+ /* goto the last line that had a character on it */
+ for (; l->l_next; l = l->l_next)
+ this_line++;
+ flush_lines(this_line - nflushd_lines + extra_lines + 1);
+
+ /* make sure we leave things in a sane state */
+ if (last_set != CS_NORMAL)
+ PUTC('\017');
+
+ /* flush out the last few blank lines */
+ nblank_lines = max_line - this_line;
+ if (max_line & 1)
+ nblank_lines++;
+ else if (!nblank_lines)
+ /* missing a \n on the last line? */
+ nblank_lines = 2;
+ flush_blanks();
+ exit(0);
+}
+
+void
+flush_lines(int nflush)
+{
+ LINE *l;
+
+ while (--nflush >= 0) {
+ l = lines;
+ lines = l->l_next;
+ if (l->l_line) {
+ flush_blanks();
+ flush_line(l);
+ }
+ nblank_lines++;
+ if (l->l_line)
+ (void)free(l->l_line);
+ free_line(l);
+ }
+ if (lines)
+ lines->l_prev = NULL;
+}
+
+/*
+ * Print a number of newline/half newlines. If fine flag is set, nblank_lines
+ * is the number of half line feeds, otherwise it is the number of whole line
+ * feeds.
+ */
+void
+flush_blanks(void)
+{
+ int half, i, nb;
+
+ half = 0;
+ nb = nblank_lines;
+ if (nb & 1) {
+ if (fine)
+ half = 1;
+ else
+ nb++;
+ }
+ nb /= 2;
+ for (i = nb; --i >= 0;)
+ PUTC('\n');
+ if (half) {
+ PUTC('\033');
+ PUTC('9');
+ if (!nb)
+ PUTC('\r');
+ }
+ nblank_lines = 0;
+}
+
+/*
+ * Write a line to stdout taking care of space to tab conversion (-h flag)
+ * and character set shifts.
+ */
+void
+flush_line(LINE *l)
+{
+ CHAR *c, *endc;
+ int i, nchars, last_col, this_col;
+
+ last_col = 0;
+ nchars = l->l_line_len;
+
+ if (l->l_needs_sort) {
+ static CHAR *sorted;
+ static int count_size, *count, i, save, sorted_size, tot;
+
+ /*
+ * Do an O(n) sort on l->l_line by column being careful to
+ * preserve the order of characters in the same column.
+ */
+ if (l->l_lsize > sorted_size) {
+ sorted_size = l->l_lsize;
+ if ((sorted = realloc(sorted,
+ (unsigned)sizeof(CHAR) * sorted_size)) == NULL)
+ err(1, (char *)NULL);
+ }
+ if (l->l_max_col >= count_size) {
+ count_size = l->l_max_col + 1;
+ if ((count = realloc(count,
+ (unsigned)sizeof(int) * count_size)) == NULL)
+ err(1, (char *)NULL);
+ }
+ memset(count, 0, sizeof(int) * l->l_max_col + 1);
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ count[c->c_column]++;
+
+ /*
+ * calculate running total (shifted down by 1) to use as
+ * indices into new line.
+ */
+ for (tot = 0, i = 0; i <= l->l_max_col; i++) {
+ save = count[i];
+ count[i] = tot;
+ tot += save;
+ }
+
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ sorted[count[c->c_column]++] = *c;
+ c = sorted;
+ } else
+ c = l->l_line;
+ while (nchars > 0) {
+ this_col = c->c_column;
+ endc = c;
+ do {
+ ++endc;
+ } while (--nchars > 0 && this_col == endc->c_column);
+
+ /* if -b only print last character */
+ if (no_backspaces) {
+ c = endc - 1;
+ if (nchars > 0 &&
+ this_col + c->c_width > endc->c_column)
+ continue;
+ }
+
+ if (this_col > last_col) {
+ int nspace = this_col - last_col;
+
+ if (compress_spaces && nspace > 1) {
+ while (1) {
+ int tab_col, tab_size;;
+
+ tab_col = (last_col + 8) & ~7;
+ if (tab_col > this_col)
+ break;
+ tab_size = tab_col - last_col;
+ if (tab_size == 1)
+ PUTC(' ');
+ else
+ PUTC('\t');
+ nspace -= tab_size;
+ last_col = tab_col;
+ }
+ }
+ while (--nspace >= 0)
+ PUTC(' ');
+ last_col = this_col;
+ }
+
+ for (;;) {
+ if (c->c_set != last_set) {
+ switch (c->c_set) {
+ case CS_NORMAL:
+ PUTC('\017');
+ break;
+ case CS_ALTERNATE:
+ PUTC('\016');
+ }
+ last_set = c->c_set;
+ }
+ PUTC(c->c_char);
+ if ((c + 1) < endc)
+ for (i = 0; i < c->c_width; i++)
+ PUTC('\b');
+ if (++c >= endc)
+ break;
+ }
+ last_col += (c - 1)->c_width;
+ }
+}
+
+#define NALLOC 64
+
+static LINE *line_freelist;
+
+LINE *
+alloc_line(void)
+{
+ LINE *l;
+ int i;
+
+ if (!line_freelist) {
+ if ((l = realloc(NULL, sizeof(LINE) * NALLOC)) == NULL)
+ err(1, (char *)NULL);
+ line_freelist = l;
+ for (i = 1; i < NALLOC; i++, l++)
+ l->l_next = l + 1;
+ l->l_next = NULL;
+ }
+ l = line_freelist;
+ line_freelist = l->l_next;
+
+ memset(l, 0, sizeof(LINE));
+ return (l);
+}
+
+void
+free_line(LINE *l)
+{
+
+ l->l_next = line_freelist;
+ line_freelist = l;
+}
+
+void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: col [-bfhpx] [-l nline]\n");
+ exit(1);
+}
+
+void
+dowarn(int line)
+{
+
+ warnx("warning: can't back up %s",
+ line < 0 ? "past first line" : "-- line already flushed");
+}
diff --git a/text_cmds/colrm/colrm.1 b/text_cmds/colrm/colrm.1
new file mode 100644
index 0000000..f97a8c4
--- /dev/null
+++ b/text_cmds/colrm/colrm.1
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)colrm.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/colrm/colrm.1,v 1.11 2005/01/17 07:44:14 ru Exp $
+.\"
+.Dd August 4, 2004
+.Dt COLRM 1
+.Os
+.Sh NAME
+.Nm colrm
+.Nd remove columns from a file
+.Sh SYNOPSIS
+.Nm
+.Op Ar start Op Ar stop
+.Sh DESCRIPTION
+The
+.Nm
+utility removes selected columns from the lines of a file.
+A column is defined as a single character in a line.
+Input is read from the standard input.
+Output is written to the standard output.
+.Pp
+If only the
+.Ar start
+column is specified, columns numbered less than the
+.Ar start
+column will be written.
+If both
+.Ar start
+and
+.Ar stop
+columns are specified, columns numbered less than the
+.Ar start
+column
+or greater than the
+.Ar stop
+column will be written.
+Column numbering starts with one, not zero.
+.Pp
+Tab characters increment the column count to the next multiple of eight.
+Backspace characters decrement the column count by one.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr column 1 ,
+.Xr cut 1 ,
+.Xr paste 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/text_cmds/colrm/colrm.c b/text_cmds/colrm/colrm.c
new file mode 100644
index 0000000..cf176ab
--- /dev/null
+++ b/text_cmds/colrm/colrm.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)colrm.c 8.2 (Berkeley) 5/4/95";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/colrm/colrm.c,v 1.12 2004/07/29 09:09:22 tjr Exp $");
+
+#include <sys/types.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#define TAB 8
+
+void check(FILE *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ u_long column, start, stop;
+ int ch, width;
+ char *p;
+
+ setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ start = stop = 0;
+ switch(argc) {
+ case 2:
+ if(argv[1]) stop = strtol(argv[1], &p, 10);
+ if (stop <= 0 || *p)
+ errx(1, "illegal column -- %s", argv[1]);
+ /* FALLTHROUGH */
+ case 1:
+ if(argv[0]) start = strtol(argv[0], &p, 10);
+ if (start <= 0 || *p)
+ errx(1, "illegal column -- %s", argv[0]);
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if (stop && start > stop)
+ errx(1, "illegal start and stop columns");
+
+ for (column = 0;;) {
+ switch (ch = getwchar()) {
+ case WEOF:
+ check(stdin);
+ break;
+ case '\b':
+ if (column)
+ --column;
+ break;
+ case '\n':
+ column = 0;
+ break;
+ case '\t':
+ column = (column + TAB) & ~(TAB - 1);
+ break;
+ default:
+ if ((width = wcwidth(ch)) > 0)
+ column += width;
+ break;
+ }
+
+ if ((!start || column < start || (stop && column > stop)) &&
+ putwchar(ch) == WEOF)
+ check(stdout);
+ }
+}
+
+void
+check(FILE *stream)
+{
+ if (feof(stream))
+ exit(0);
+ if (ferror(stream))
+ err(1, "%s", stream == stdin ? "stdin" : "stdout");
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: colrm [start [stop]]\n");
+ exit(1);
+}
+
diff --git a/text_cmds/column/column.1 b/text_cmds/column/column.1
new file mode 100644
index 0000000..b87eaa4
--- /dev/null
+++ b/text_cmds/column/column.1
@@ -0,0 +1,101 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)column.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/column/column.1,v 1.16 2010/12/11 08:32:16 joel Exp $
+.\"
+.Dd July 29, 2004
+.Dt COLUMN 1
+.Os
+.Sh NAME
+.Nm column
+.Nd columnate lists
+.Sh SYNOPSIS
+.Nm
+.Op Fl tx
+.Op Fl c Ar columns
+.Op Fl s Ar sep
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility formats its input into multiple columns.
+Rows are filled before columns.
+Input is taken from
+.Ar file
+operands, or, by default, from the standard input.
+Empty lines are ignored.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Output is formatted for a display
+.Ar columns
+wide.
+.It Fl s
+Specify a set of characters to be used to delimit columns for the
+.Fl t
+option.
+.It Fl t
+Determine the number of columns the input contains and create a table.
+Columns are delimited with whitespace, by default, or with the characters
+supplied using the
+.Fl s
+option.
+Useful for pretty-printing displays.
+.It Fl x
+Fill columns before filling rows.
+.El
+.Sh ENVIRONMENT
+The
+.Ev COLUMNS , LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+.Dl (printf \&"PERM LINKS OWNER GROUP SIZE MONTH DAY \&"\ \&;\ \&\e
+.Dl printf \&"HH:MM/YEAR NAME\en\&"\ \&;\ \&\e
+.Dl ls -l \&| sed 1d) \&| column -t
+.Sh SEE ALSO
+.Xr colrm 1 ,
+.Xr ls 1 ,
+.Xr paste 1 ,
+.Xr sort 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 Reno .
+.Sh BUGS
+Input lines are limited to
+.Dv LINE_MAX
+(2048) bytes in length.
diff --git a/text_cmds/column/column.c b/text_cmds/column/column.c
new file mode 100644
index 0000000..471421c
--- /dev/null
+++ b/text_cmds/column/column.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)column.c 8.4 (Berkeley) 5/4/95";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/column/column.c,v 1.19 2011/11/06 08:14:34 ed Exp $");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define TAB 8
+
+static void c_columnate(void);
+static void input(FILE *);
+static void maketbl(void);
+static void print(void);
+static void r_columnate(void);
+static void usage(void);
+static int width(const wchar_t *);
+
+static int termwidth = 80; /* default terminal width */
+
+static int entries; /* number of records */
+static int eval; /* exit value */
+static int maxlength; /* longest record */
+static wchar_t **list; /* array of pointers to records */
+static const wchar_t *separator = L"\t "; /* field separator for table option */
+
+int
+main(int argc, char **argv)
+{
+ struct winsize win;
+ FILE *fp;
+ int ch, tflag, xflag;
+ char *p;
+ const char *src;
+ wchar_t *newsep;
+ size_t seplen;
+
+ setlocale(LC_ALL, "");
+
+ if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
+ if ((p = getenv("COLUMNS")))
+ termwidth = atoi(p);
+ } else
+ termwidth = win.ws_col;
+
+ tflag = xflag = 0;
+ while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
+ switch(ch) {
+ case 'c':
+ termwidth = atoi(optarg);
+ break;
+ case 's':
+ src = optarg;
+ seplen = mbsrtowcs(NULL, &src, 0, NULL);
+ if (seplen == (size_t)-1)
+ err(1, "bad separator");
+ newsep = malloc((seplen + 1) * sizeof(wchar_t));
+ if (newsep == NULL)
+ err(1, NULL);
+ mbsrtowcs(newsep, &src, seplen + 1, NULL);
+ separator = newsep;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ input(stdin);
+ else for (; *argv; ++argv)
+ if ((fp = fopen(*argv, "r"))) {
+ input(fp);
+ (void)fclose(fp);
+ } else {
+ warn("%s", *argv);
+ eval = 1;
+ }
+
+ if (!entries)
+ exit(eval);
+
+ maxlength = roundup(maxlength + 1, TAB);
+ if (tflag)
+ maketbl();
+ else if (maxlength >= termwidth)
+ print();
+ else if (xflag)
+ c_columnate();
+ else
+ r_columnate();
+ exit(eval);
+}
+
+static void
+c_columnate(void)
+{
+ int chcnt, col, cnt, endcol, numcols;
+ wchar_t **lp;
+
+ numcols = termwidth / maxlength;
+ endcol = maxlength;
+ for (chcnt = col = 0, lp = list;; ++lp) {
+ wprintf(L"%ls", *lp);
+ chcnt += width(*lp);
+ if (!--entries)
+ break;
+ if (++col == numcols) {
+ chcnt = col = 0;
+ endcol = maxlength;
+ putwchar('\n');
+ } else {
+ while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
+ (void)putwchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ }
+ if (chcnt)
+ putwchar('\n');
+}
+
+static void
+r_columnate(void)
+{
+ int base, chcnt, cnt, col, endcol, numcols, numrows, row;
+
+ numcols = termwidth / maxlength;
+ numrows = entries / numcols;
+ if (entries % numcols)
+ ++numrows;
+
+ for (row = 0; row < numrows; ++row) {
+ endcol = maxlength;
+ for (base = row, chcnt = col = 0; col < numcols; ++col) {
+ wprintf(L"%ls", list[base]);
+ chcnt += width(list[base]);
+ if ((base += numrows) >= entries)
+ break;
+ while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) {
+ (void)putwchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ putwchar('\n');
+ }
+}
+
+static void
+print(void)
+{
+ int cnt;
+ wchar_t **lp;
+
+ for (cnt = entries, lp = list; cnt--; ++lp)
+ (void)wprintf(L"%ls\n", *lp);
+}
+
+typedef struct _tbl {
+ wchar_t **list;
+ int cols, *len;
+} TBL;
+#define DEFCOLS 25
+
+static void
+maketbl(void)
+{
+ TBL *t;
+ int coloff, cnt;
+ wchar_t *p, **lp;
+ int *lens, maxcols;
+ TBL *tbl;
+ wchar_t **cols;
+ wchar_t *last;
+
+ if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL)
+ err(1, (char *)NULL);
+ if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL)
+ err(1, (char *)NULL);
+ if ((lens = calloc(maxcols, sizeof(int))) == NULL)
+ err(1, (char *)NULL);
+ for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
+ for (coloff = 0, p = *lp;
+ (cols[coloff] = wcstok(p, separator, &last));
+ p = NULL)
+ if (++coloff == maxcols) {
+ if (!(cols = realloc(cols, ((u_int)maxcols +
+ DEFCOLS) * sizeof(char *))) ||
+ !(lens = realloc(lens,
+ ((u_int)maxcols + DEFCOLS) * sizeof(int))))
+ err(1, NULL);
+ memset((char *)lens + maxcols * sizeof(int),
+ 0, DEFCOLS * sizeof(int));
+ maxcols += DEFCOLS;
+ }
+ if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL)
+ err(1, (char *)NULL);
+ if ((t->len = calloc(coloff, sizeof(int))) == NULL)
+ err(1, (char *)NULL);
+ for (t->cols = coloff; --coloff >= 0;) {
+ t->list[coloff] = cols[coloff];
+ t->len[coloff] = width(cols[coloff]);
+ if (t->len[coloff] > lens[coloff])
+ lens[coloff] = t->len[coloff];
+ }
+ }
+ for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
+ for (coloff = 0; coloff < t->cols - 1; ++coloff)
+ (void)wprintf(L"%ls%*ls", t->list[coloff],
+ lens[coloff] - t->len[coloff] + 2, L" ");
+ (void)wprintf(L"%ls\n", t->list[coloff]);
+ }
+}
+
+#define DEFNUM 1000
+#define MAXLINELEN (LINE_MAX + 1)
+
+static void
+input(FILE *fp)
+{
+ static int maxentry;
+ int len;
+ wchar_t *p, buf[MAXLINELEN];
+
+ if (!list)
+ if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) ==
+ NULL)
+ err(1, (char *)NULL);
+ while (fgetws(buf, MAXLINELEN, fp)) {
+ for (p = buf; *p && iswspace(*p); ++p);
+ if (!*p)
+ continue;
+ if (!(p = wcschr(p, L'\n'))) {
+ warnx("line too long");
+ eval = 1;
+ continue;
+ }
+ *p = L'\0';
+ len = width(buf);
+ if (maxlength < len)
+ maxlength = len;
+ if (entries == maxentry) {
+ maxentry += DEFNUM;
+ if (!(list = realloc(list,
+ (u_int)maxentry * sizeof(*list))))
+ err(1, NULL);
+ }
+ list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t));
+ if (list[entries] == NULL)
+ err(1, NULL);
+ wcscpy(list[entries], buf);
+ entries++;
+ }
+}
+
+/* Like wcswidth(), but ignores non-printing characters. */
+static int
+width(const wchar_t *wcs)
+{
+ int w, cw;
+
+ for (w = 0; *wcs != L'\0'; wcs++)
+ if ((cw = wcwidth(*wcs)) > 0)
+ w += cw;
+ return (w);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: column [-tx] [-c columns] [-s sep] [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/comm/comm.1 b/text_cmds/comm/comm.1
new file mode 100644
index 0000000..45a726d
--- /dev/null
+++ b/text_cmds/comm/comm.1
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)comm.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/comm/comm.1,v 1.14 2005/01/25 22:28:34 tjr Exp $
+.\"
+.Dd January 26, 2005
+.Os
+.Dt COMM 1
+.Sh NAME
+.Nm comm
+.Nd select or reject lines common to two files
+.Sh SYNOPSIS
+.Nm
+.Op Fl 123i
+.Ar file1 file2
+.Sh DESCRIPTION
+The
+.Nm
+utility reads
+.Ar file1
+and
+.Ar file2 ,
+which should be
+sorted lexically, and produces three text
+columns as output: lines only in
+.Ar file1 ;
+lines only in
+.Ar file2 ;
+and lines in both files.
+.Pp
+The filename ``-'' means the standard input.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 1
+Suppress printing of column 1.
+.It Fl 2
+Suppress printing of column 2.
+.It Fl 3
+Suppress printing of column 3.
+.It Fl i
+Case insensitive comparison of lines.
+.El
+.Pp
+Each column will have a number of tab characters prepended to it
+equal to the number of lower numbered columns that are being printed.
+For example, if column number two is being suppressed, lines printed
+in column number one will not have any tabs preceding them, and lines
+printed in column number three will have one.
+.Pp
+The
+.Nm
+utility assumes that the files are lexically sorted; all characters
+participate in line comparisons.
+.Sh ENVIRONMENT
+The
+.Ev LANG ,
+.Ev LC_ALL ,
+.Ev LC_COLLATE ,
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr cmp 1 ,
+.Xr diff 1 ,
+.Xr sort 1 ,
+.Xr uniq 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.2-92 .
+.Pp
+The
+.Fl i
+option is an extension to the
+.Tn POSIX
+standard.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v4 .
+.Sh BUGS
+Input lines are limited to
+.Dv LINE_MAX
+(2048) characters in length.
diff --git a/text_cmds/comm/comm.c b/text_cmds/comm/comm.c
new file mode 100644
index 0000000..fc6c663
--- /dev/null
+++ b/text_cmds/comm/comm.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "From: @(#)comm.c 8.4 (Berkeley) 5/4/95";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/comm/comm.c,v 1.21 2004/07/02 22:48:29 tjr Exp $");
+
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define MAXLINELEN (LINE_MAX + 1)
+
+const wchar_t *tabs[] = { L"", L"\t", L"\t\t" };
+
+FILE *file(const char *);
+void show(FILE *, const char *, const wchar_t *, wchar_t *);
+int wcsicoll(const wchar_t *, const wchar_t *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int comp, file1done = 0, file2done = 0, read1, read2;
+ int ch, flag1, flag2, flag3, iflag;
+ FILE *fp1, *fp2;
+ const wchar_t *col1, *col2, *col3;
+ wchar_t line1[MAXLINELEN], line2[MAXLINELEN];
+ const wchar_t **p;
+
+ flag1 = flag2 = flag3 = 1;
+ iflag = 0;
+
+ (void) setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "123i")) != -1)
+ switch(ch) {
+ case '1':
+ flag1 = 0;
+ break;
+ case '2':
+ flag2 = 0;
+ break;
+ case '3':
+ flag3 = 0;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 || !argv[0] || !argv[1])
+ usage();
+
+ fp1 = file(argv[0]);
+ fp2 = file(argv[1]);
+
+ /* for each column printed, add another tab offset */
+ p = tabs;
+ col1 = col2 = col3 = NULL;
+ if (flag1)
+ col1 = *p++;
+ if (flag2)
+ col2 = *p++;
+ if (flag3)
+ col3 = *p;
+
+ for (read1 = read2 = 1;;) {
+ /* read next line, check for EOF */
+ if (read1) {
+ file1done = !fgetws(line1, MAXLINELEN, fp1);
+ if (file1done && ferror(fp1))
+ err(1, "%s", argv[0]);
+ }
+ if (read2) {
+ file2done = !fgetws(line2, MAXLINELEN, fp2);
+ if (file2done && ferror(fp2))
+ err(1, "%s", argv[1]);
+ }
+
+ /* if one file done, display the rest of the other file */
+ if (file1done) {
+ if (!file2done && col2)
+ show(fp2, argv[1], col2, line2);
+ break;
+ }
+ if (file2done) {
+ if (!file1done && col1)
+ show(fp1, argv[0], col1, line1);
+ break;
+ }
+
+ /* lines are the same */
+ if(iflag)
+ comp = wcsicoll(line1, line2);
+ else
+ comp = wcscoll(line1, line2);
+
+ if (!comp) {
+ read1 = read2 = 1;
+ if (col3)
+ (void)printf("%ls%ls", col3, line1);
+ continue;
+ }
+
+ /* lines are different */
+ if (comp < 0) {
+ read1 = 1;
+ read2 = 0;
+ if (col1)
+ (void)printf("%ls%ls", col1, line1);
+ } else {
+ read1 = 0;
+ read2 = 1;
+ if (col2)
+ (void)printf("%ls%ls", col2, line2);
+ }
+ }
+ exit(0);
+}
+
+void
+show(FILE *fp, const char *fn, const wchar_t *offset, wchar_t *buf)
+{
+
+ do {
+ (void)printf("%ls%ls", offset, buf);
+ } while (fgetws(buf, MAXLINELEN, fp));
+ if (ferror(fp))
+ err(1, "%s", fn);
+}
+
+FILE *
+file(const char *name)
+{
+ FILE *fp;
+
+ if (!strcmp(name, "-"))
+ return (stdin);
+ if ((fp = fopen(name, "r")) == NULL) {
+ err(1, "%s", name);
+ }
+ return (fp);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: comm [-123i] file1 file2\n");
+ exit(1);
+}
+
+int
+wcsicoll(const wchar_t *s1, const wchar_t *s2)
+{
+ wchar_t *p, line1[MAXLINELEN], line2[MAXLINELEN];
+
+ for (p = line1; *s1; s1++)
+ *p++ = towlower(*s1);
+ *p = '\0';
+ for (p = line2; *s2; s2++)
+ *p++ = towlower(*s2);
+ *p = '\0';
+ return (wcscoll(line1, line2));
+}
diff --git a/text_cmds/csplit/csplit.1 b/text_cmds/csplit/csplit.1
new file mode 100644
index 0000000..98b834f
--- /dev/null
+++ b/text_cmds/csplit/csplit.1
@@ -0,0 +1,157 @@
+.\" Copyright (c) 2002 Tim J. Robbins.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/usr.bin/csplit/csplit.1,v 1.11 2005/01/25 22:29:51 tjr Exp $
+.\"
+.Dd January 26, 2005
+.Dt CSPLIT 1
+.Os
+.Sh NAME
+.Nm csplit
+.Nd split files based on context
+.Sh SYNOPSIS
+.Nm
+.Op Fl ks
+.Op Fl f Ar prefix
+.Op Fl n Ar number
+.Ar file args ...
+.Sh DESCRIPTION
+The
+.Nm
+utility splits
+.Ar file
+into pieces using the patterns
+.Ar args .
+If
+.Ar file
+is
+a dash
+.Pq Sq Fl ,
+.Nm
+reads from standard input.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar prefix
+Give created files names beginning with
+.Ar prefix .
+The default is
+.Dq Pa xx .
+.It Fl k
+Do not remove output files if an error occurs or a
+.Dv HUP ,
+.Dv INT
+or
+.Dv TERM
+signal is received.
+.It Fl n Ar number
+Use
+.Ar number
+of decimal digits after the
+.Ar prefix
+to form the file name.
+The default is 2.
+.It Fl s
+Do not write the size of each output file to standard output as it is
+created.
+.El
+.Pp
+The
+.Ar args
+operands may be a combination of the following patterns:
+.Bl -tag -width indent
+.It Xo
+.Sm off
+.Cm / Ar regexp Cm / Op Oo Cm + | - Oc Ar offset
+.Sm on
+.Xc
+Create a file containing the input from the current line to (but not including)
+the next line matching the given basic regular expression.
+An optional
+.Ar offset
+from the line that matched may be specified.
+.It Xo
+.Sm off
+.Cm % Ar regexp Cm % Op Oo Cm + | - Oc Ar offset
+.Sm on
+.Xc
+Same as above but a file is not created for the output.
+.It Ar line_no
+Create containing the input from the current line to (but not including)
+the specified line number.
+.It Cm { Ns Ar num Ns Cm }
+Repeat the previous pattern the specified number of times.
+If it follows a line number pattern, a new file will be created for each
+.Ar line_no
+lines,
+.Ar num
+times.
+The first line of the file is line number 1 for historic reasons.
+.El
+.Pp
+After all the patterns have been processed, the remaining input data
+(if there is any) will be written to a new file.
+.Pp
+Requesting to split at a line before the current line number or past the
+end of the file will result in an error.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_COLLATE
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Split the
+.Xr mdoc 7
+file
+.Pa foo.1
+into one file for each section (up to 20):
+.Pp
+.Dl "csplit -k foo.1 '%^\e.Sh%' '/^\e.Sh/' '{20}'"
+.Pp
+Split standard input after the first 99 lines and every 100 lines thereafter:
+.Pp
+.Dl "csplit -k - 100 '{19}'"
+.Sh SEE ALSO
+.Xr sed 1 ,
+.Xr split 1 ,
+.Xr re_format 7
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+A
+.Nm
+command appeared in PWB UNIX.
+.Sh BUGS
+Input lines are limited to
+.Dv LINE_MAX
+(2048) bytes in length.
diff --git a/text_cmds/csplit/csplit.c b/text_cmds/csplit/csplit.c
new file mode 100644
index 0000000..57bc823
--- /dev/null
+++ b/text_cmds/csplit/csplit.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * csplit -- split files based on context
+ *
+ * This utility splits its input into numbered output files by line number
+ * or by a regular expression. Regular expression matches have an optional
+ * offset with them, allowing the split to occur a specified number of
+ * lines before or after the match.
+ *
+ * To handle negative offsets, we stop reading when the match occurs and
+ * store the offset that the file should have been split at, then use
+ * this output file as input until all the "overflowed" lines have been read.
+ * The file is then closed and truncated to the correct length.
+ *
+ * We assume that the output files can be seeked upon (ie. they cannot be
+ * symlinks to named pipes or character devices), but make no such
+ * assumption about the input.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/csplit/csplit.c,v 1.9 2004/03/22 11:15:03 tjr Exp $");
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void cleanup(void);
+void do_lineno(const char *);
+void do_rexp(const char *);
+char *csplit_getline(void);
+void handlesig(int);
+FILE *newfile(void);
+void toomuch(FILE *, long);
+void usage(void);
+
+/*
+ * Command line options
+ */
+const char *prefix; /* File name prefix */
+long sufflen; /* Number of decimal digits for suffix */
+int sflag; /* Suppress output of file names */
+int kflag; /* Keep output if error occurs */
+
+/*
+ * Other miscellaneous globals (XXX too many)
+ */
+long lineno; /* Current line number in input file */
+long reps; /* Number of repetitions for this pattern */
+long nfiles; /* Number of files output so far */
+long maxfiles; /* Maximum number of files we can create */
+char currfile[PATH_MAX]; /* Current output file */
+const char *infn; /* Name of the input file */
+FILE *infile; /* Input file handle */
+FILE *overfile; /* Overflow file for toomuch() */
+off_t truncofs; /* Offset this file should be truncated at */
+int doclean; /* Should cleanup() remove output? */
+
+int
+main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ long i;
+ int ch;
+ const char *expr;
+ char *ep, *p;
+ FILE *ofp;
+
+ setlocale(LC_ALL, "");
+
+ kflag = sflag = 0;
+ prefix = "xx";
+ sufflen = 2;
+ while ((ch = getopt(argc, argv, "ksf:n:")) > 0) {
+ switch (ch) {
+ case 'f':
+ prefix = optarg;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'n':
+ errno = 0;
+ sufflen = strtol(optarg, &ep, 10);
+ if (sufflen <= 0 || *ep != '\0' || errno != 0)
+ errx(1, "%s: bad suffix length", optarg);
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ if (sufflen + strlen(prefix) >= PATH_MAX)
+ errx(1, "name too long");
+
+ argc -= optind;
+ argv += optind;
+
+ if ((infn = *argv++) == NULL)
+ usage();
+ if (strcmp(infn, "-") == 0) {
+ infile = stdin;
+ infn = "stdin";
+ } else if ((infile = fopen(infn, "r")) == NULL)
+ err(1, "%s", infn);
+
+ if (!kflag) {
+ doclean = 1;
+ atexit(cleanup);
+ sa.sa_flags = 0;
+ sa.sa_handler = handlesig;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sigaddset(&sa.sa_mask, SIGINT);
+ sigaddset(&sa.sa_mask, SIGTERM);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ }
+
+ lineno = 0;
+ nfiles = 0;
+ truncofs = 0;
+ overfile = NULL;
+
+ /* Ensure 10^sufflen < LONG_MAX. */
+ for (maxfiles = 1, i = 0; i < sufflen; i++) {
+ if (maxfiles > LONG_MAX / 10)
+ errx(1, "%ld: suffix too long (limit %ld)",
+ sufflen, i);
+ maxfiles *= 10;
+ }
+
+ /* Create files based on supplied patterns. */
+ while (nfiles < maxfiles - 1 && (expr = *argv++) != NULL) {
+ /* Look ahead & see if this pattern has any repetitions. */
+ if (*argv != NULL && **argv == '{') {
+ errno = 0;
+ reps = strtol(*argv + 1, &ep, 10);
+ if (reps < 0 || *ep != '}' || errno != 0)
+ errx(1, "%s: bad repetition count", *argv + 1);
+ argv++;
+ } else
+ reps = 0;
+
+ if (*expr == '/' || *expr == '%') {
+ do
+ do_rexp(expr);
+ while (reps-- != 0 && nfiles < maxfiles - 1);
+ } else if (isdigit((unsigned char)*expr))
+ do_lineno(expr);
+ else
+ errx(1, "%s: unrecognised pattern", expr);
+ }
+
+ /* Copy the rest into a new file. */
+ if (!feof(infile)) {
+ ofp = newfile();
+ while ((p = csplit_getline()) != NULL && fputs(p, ofp) != EOF)
+ ;
+ if (!sflag)
+ printf("%jd\n", (intmax_t)ftello(ofp));
+ if (fclose(ofp) != 0)
+ err(1, "%s", currfile);
+ }
+
+ toomuch(NULL, 0);
+ doclean = 0;
+
+ return (0);
+}
+
+void
+usage(void)
+{
+
+ fprintf(stderr,
+"usage: csplit [-ks] [-f prefix] [-n number] file args ...\n");
+ exit(1);
+}
+
+void
+handlesig(int sig __unused)
+{
+ const char msg[] = "csplit: caught signal, cleaning up\n";
+
+ write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ cleanup();
+ _exit(2);
+}
+
+/* Create a new output file. */
+FILE *
+newfile(void)
+{
+ FILE *fp;
+
+ if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix,
+ (int)sufflen, nfiles) >= sizeof(currfile))
+ errc(1, ENAMETOOLONG, NULL);
+ if ((fp = fopen(currfile, "w+")) == NULL)
+ err(1, "%s", currfile);
+ nfiles++;
+
+ return (fp);
+}
+
+/* Remove partial output, called before exiting. */
+void
+cleanup(void)
+{
+ char fnbuf[PATH_MAX];
+ long i;
+
+ if (!doclean)
+ return;
+
+ /*
+ * NOTE: One cannot portably assume to be able to call snprintf()
+ * from inside a signal handler. It does, however, appear to be safe
+ * to do on FreeBSD. The solution to this problem is worse than the
+ * problem itself.
+ */
+
+ for (i = 0; i < nfiles; i++) {
+ snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix,
+ (int)sufflen, i);
+ unlink(fnbuf);
+ }
+}
+
+/* Read a line from the input into a static buffer. */
+char *
+csplit_getline(void)
+{
+ static char lbuf[LINE_MAX];
+ FILE *src;
+
+ src = overfile != NULL ? overfile : infile;
+
+again: if (fgets(lbuf, sizeof(lbuf), src) == NULL) {
+ if (src == overfile) {
+ src = infile;
+ goto again;
+ }
+ return (NULL);
+ }
+ if (ferror(src))
+ err(1, "%s", infn);
+ lineno++;
+
+ return (lbuf);
+}
+
+/* Conceptually rewind the input (as obtained by csplit_getline()) back `n' lines. */
+void
+toomuch(FILE *ofp, long n)
+{
+ char buf[BUFSIZ];
+ size_t i, nread;
+
+ if (overfile != NULL) {
+ /*
+ * Truncate the previous file we overflowed into back to
+ * the correct length, close it.
+ */
+ if (fflush(overfile) != 0)
+ err(1, "overflow");
+ if (ftruncate(fileno(overfile), truncofs) != 0)
+ err(1, "overflow");
+ if (fclose(overfile) != 0)
+ err(1, "overflow");
+ overfile = NULL;
+ }
+
+ if (n == 0)
+ /* Just tidying up */
+ return;
+
+ lineno -= n;
+
+ /*
+ * Wind the overflow file backwards to `n' lines before the
+ * current one.
+ */
+ do {
+ if (ftello(ofp) < (off_t)sizeof(buf))
+ rewind(ofp);
+ else
+ fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR);
+ if (ferror(ofp))
+ errx(1, "%s: can't seek", currfile);
+ if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0)
+ errx(1, "can't read overflowed output");
+ if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0)
+ err(1, "%s", currfile);
+ for (i = 1; i <= nread; i++)
+ if (buf[nread - i] == '\n' && n-- == 0)
+ break;
+ if (ftello(ofp) == 0)
+ break;
+ } while (n > 0);
+ if (fseeko(ofp, nread - i + 1, SEEK_CUR) != 0)
+ err(1, "%s", currfile);
+
+ /*
+ * csplit_getline() will read from here. Next call will truncate to
+ * truncofs in this file.
+ */
+ overfile = ofp;
+ truncofs = ftello(overfile);
+}
+
+/* Handle splits for /regexp/ and %regexp% patterns. */
+void
+do_rexp(const char *expr)
+{
+ regex_t cre;
+ intmax_t nwritten;
+ long ofs;
+ int first;
+ char *ecopy, *ep, *p, *pofs, *re;
+ FILE *ofp;
+
+ if ((ecopy = strdup(expr)) == NULL)
+ err(1, "strdup");
+
+ re = ecopy + 1;
+ if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\')
+ errx(1, "%s: missing trailing %c", expr, *expr);
+ *pofs++ = '\0';
+
+ if (*pofs != '\0') {
+ errno = 0;
+ ofs = strtol(pofs, &ep, 10);
+ if (*ep != '\0' || errno != 0)
+ errx(1, "%s: bad offset", pofs);
+ } else
+ ofs = 0;
+
+ if (regcomp(&cre, re, REG_BASIC|REG_NOSUB|REG_NEWLINE) != 0)
+ errx(1, "%s: bad regular expression", re);
+
+ if (*expr == '/')
+ /* /regexp/: Save results to a file. */
+ ofp = newfile();
+ else {
+ /* %regexp%: Make a temporary file for overflow. */
+ if ((ofp = tmpfile()) == NULL)
+ err(1, "tmpfile");
+ }
+
+ /* Read and output lines until we get a match. */
+ first = 1;
+ while ((p = csplit_getline()) != NULL) {
+ if (fputs(p, ofp) == EOF)
+ break;
+ if (!first && regexec(&cre, p, 0, NULL, 0) == 0)
+ break;
+ first = 0;
+ }
+
+ if (p == NULL)
+ errx(1, "%s: no match", re);
+
+ if (ofs <= 0) {
+ /*
+ * Negative (or zero) offset: throw back any lines we should
+ * not have read yet.
+ */
+ if (p != NULL) {
+ toomuch(ofp, -ofs + 1);
+ nwritten = (intmax_t)truncofs;
+ } else
+ nwritten = (intmax_t)ftello(ofp);
+ } else {
+ /*
+ * Positive offset: copy the requested number of lines
+ * after the match.
+ */
+ while (--ofs > 0 && (p = csplit_getline()) != NULL)
+ fputs(p, ofp);
+ toomuch(NULL, 0);
+ nwritten = (intmax_t)ftello(ofp);
+ if (fclose(ofp) != 0)
+ err(1, "%s", currfile);
+ }
+
+ if (!sflag && *expr == '/')
+ printf("%jd\n", nwritten);
+
+ regfree(&cre);
+ free(ecopy);
+}
+
+/* Handle splits based on line number. */
+void
+do_lineno(const char *expr)
+{
+ long lastline, tgtline;
+ char *ep, *p;
+ FILE *ofp;
+
+ errno = 0;
+ tgtline = strtol(expr, &ep, 10);
+ if (tgtline <= 0 || errno != 0 || *ep != '\0')
+ errx(1, "%s: bad line number", expr);
+ lastline = tgtline;
+ if (lastline <= lineno)
+ errx(1, "%s: can't go backwards", expr);
+
+ while (nfiles < maxfiles - 1) {
+ ofp = newfile();
+ while (lineno + 1 != lastline) {
+ if ((p = csplit_getline()) == NULL)
+ errx(1, "%ld: out of range", lastline);
+ if (fputs(p, ofp) == EOF)
+ break;
+ }
+ if (!sflag)
+ printf("%jd\n", (intmax_t)ftello(ofp));
+ if (fclose(ofp) != 0)
+ err(1, "%s", currfile);
+ if (reps-- == 0)
+ break;
+ lastline += tgtline;
+ }
+}
diff --git a/text_cmds/cut/cut.1 b/text_cmds/cut/cut.1
new file mode 100644
index 0000000..1565876
--- /dev/null
+++ b/text_cmds/cut/cut.1
@@ -0,0 +1,166 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cut.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/cut/cut.1,v 1.32 2007/02/28 10:13:32 ru Exp $
+.\"
+.Dd December 21, 2006
+.Dt CUT 1
+.Os
+.Sh NAME
+.Nm cut
+.Nd cut out selected portions of each line of a file
+.Sh SYNOPSIS
+.Nm
+.Fl b Ar list
+.Op Fl n
+.Op Ar
+.Nm
+.Fl c Ar list
+.Op Ar
+.Nm
+.Fl f Ar list
+.Op Fl d Ar delim
+.Op Fl s
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility cuts out selected portions of each line (as specified by
+.Ar list )
+from each
+.Ar file
+and writes them to the standard output.
+If no
+.Ar file
+arguments are specified, or a file argument is a single dash
+.Pq Sq Fl ,
+.Nm
+reads from the standard input.
+The items specified by
+.Ar list
+can be in terms of column position or in terms of fields delimited
+by a special character.
+Column numbering starts from 1.
+.Pp
+The
+.Ar list
+option argument
+is a comma or whitespace separated set of numbers and/or
+number ranges.
+Number ranges consist of a number, a dash
+.Pq Sq \- ,
+and a second number
+and select the fields or columns from the first number to the second,
+inclusive.
+Numbers or number ranges may be preceded by a dash, which selects all
+fields or columns from 1 to the last number.
+Numbers or number ranges may be followed by a dash, which selects all
+fields or columns from the last number to the end of the line.
+Numbers and number ranges may be repeated, overlapping, and in any order.
+If a field or column is specified multiple times, it will appear only
+once in the output.
+It is not an error to select fields or columns not present in the
+input line.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b Ar list
+The
+.Ar list
+specifies byte positions.
+.It Fl c Ar list
+The
+.Ar list
+specifies character positions.
+.It Fl d Ar delim
+Use
+.Ar delim
+as the field delimiter character instead of the tab character.
+.It Fl f Ar list
+The
+.Ar list
+specifies fields, separated in the input by the field delimiter character
+(see the
+.Fl d
+option.)
+Output fields are separated by a single occurrence of the field delimiter
+character.
+.It Fl n
+Do not split multi-byte characters.
+Characters will only be output if at least one byte is selected, and,
+after a prefix of zero or more unselected bytes, the rest of the bytes
+that form the character are selected.
+.It Fl s
+Suppress lines with no field delimiter characters.
+Unless specified, lines with no delimiters are passed through unmodified.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Extract users' login names and shells from the system
+.Xr passwd 5
+file as
+.Dq name:shell
+pairs:
+.Pp
+.Dl "cut -d : -f 1,7 /etc/passwd"
+.Pp
+Show the names and login times of the currently logged in users:
+.Pp
+.Dl "who | cut -c 1-16,26-38"
+.Sh SEE ALSO
+.Xr colrm 1 ,
+.Xr paste 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.2-92 .
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.Tn AT&T
+System III
+.Ux .
diff --git a/text_cmds/cut/cut.c b/text_cmds/cut/cut.c
new file mode 100644
index 0000000..82fdc06
--- /dev/null
+++ b/text_cmds/cut/cut.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cut/cut.c,v 1.30 2004/11/05 10:45:23 tjr Exp $");
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <sysexits.h>
+
+int bflag;
+int cflag;
+wchar_t dchar;
+char dcharmb[MB_LEN_MAX + 1];
+int dflag;
+int fflag;
+int nflag;
+int sflag;
+
+size_t autostart, autostop, maxval;
+char * positions;
+
+int b_cut(FILE *, const char *);
+int b_n_cut(FILE *, const char *);
+int c_cut(FILE *, const char *);
+int f_cut(FILE *, const char *);
+void get_list(char *);
+void needpos(size_t);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int (*fcn)(FILE *, const char *);
+ int ch, rval;
+ size_t n;
+
+ setlocale(LC_ALL, "");
+
+ fcn = NULL;
+ dchar = '\t'; /* default delimiter is \t */
+ strcpy(dcharmb, "\t");
+
+ while ((ch = getopt(argc, argv, "b:c:d:f:sn")) != -1)
+ switch(ch) {
+ case 'b':
+ get_list(optarg);
+ bflag = 1;
+ break;
+ case 'c':
+ get_list(optarg);
+ cflag = 1;
+ break;
+ case 'd':
+ n = mbrtowc(&dchar, optarg, MB_LEN_MAX, NULL);
+ if (dchar == '\0' || n != strlen(optarg))
+ errx(1, "bad delimiter");
+ strcpy(dcharmb, optarg);
+ dflag = 1;
+ break;
+ case 'f':
+ get_list(optarg);
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fflag) {
+ if (bflag || cflag || nflag)
+ usage();
+ } else if (!(bflag || cflag) || dflag || sflag)
+ usage();
+ else if (!bflag && nflag)
+ usage();
+
+ if (fflag)
+ fcn = f_cut;
+ else if (cflag)
+ fcn = MB_CUR_MAX > 1 ? c_cut : b_cut;
+ else if (bflag)
+ fcn = nflag && MB_CUR_MAX > 1 ? b_n_cut : b_cut;
+
+ rval = 0;
+ if (*argv)
+ for (; *argv; ++argv) {
+ if (strcmp(*argv, "-") == 0) {
+ rval |= fcn(stdin, "stdin");
+ if (ferror(stdin)) {
+ errx(EX_IOERR, "Error reading stdin");
+ }
+ } else {
+ if (!(fp = fopen(*argv, "r"))) {
+ warn("%s", *argv);
+ rval = 1;
+ continue;
+ }
+ fcn(fp, *argv);
+ if (ferror(fp)) {
+ errx(EX_IOERR, "Error reading %s", *argv);
+ }
+ (void)fclose(fp);
+ }
+ }
+ else
+ rval = fcn(stdin, "stdin");
+ exit(rval);
+}
+
+void
+get_list(char *list)
+{
+ size_t setautostart, start, stop;
+ char *pos;
+ char *p;
+
+ /*
+ * set a byte in the positions array to indicate if a field or
+ * column is to be selected; use +1, it's 1-based, not 0-based.
+ * Numbers and number ranges may be overlapping, repeated, and in
+ * any order. We handle "-3-5" although there's no real reason too.
+ */
+ for (; (p = strsep(&list, ", \t")) != NULL;) {
+ setautostart = start = stop = 0;
+ if (*p == '-') {
+ ++p;
+ setautostart = 1;
+ }
+ if (isdigit((unsigned char)*p)) {
+ start = stop = strtol(p, &p, 10);
+ if (setautostart && start > autostart)
+ autostart = start;
+ }
+ if (*p == '-') {
+ if (isdigit((unsigned char)p[1]))
+ stop = strtol(p + 1, &p, 10);
+ if (*p == '-') {
+ ++p;
+ if (!autostop || autostop > stop)
+ autostop = stop;
+ }
+ }
+ if (*p)
+ errx(1, "[-cf] list: illegal list value");
+ if (!stop || !start)
+ errx(1, "[-cf] list: values may not include zero");
+ if (maxval < stop) {
+ maxval = stop;
+ needpos(maxval + 1);
+ }
+ for (pos = positions + start; start++ <= stop; *pos++ = 1);
+ }
+
+ /* overlapping ranges */
+ if (autostop && maxval > autostop) {
+ maxval = autostop;
+ needpos(maxval + 1);
+ }
+
+ /* set autostart */
+ if (autostart)
+ memset(positions + 1, '1', autostart);
+}
+
+void
+needpos(size_t n)
+{
+ static size_t npos;
+ size_t oldnpos;
+
+ /* Grow the positions array to at least the specified size. */
+ if (n > npos) {
+ oldnpos = npos;
+ if (npos == 0)
+ npos = n;
+ while (n > npos)
+ npos *= 2;
+ if ((positions = realloc(positions, npos)) == NULL)
+ err(1, "realloc");
+ memset((char *)positions + oldnpos, 0, npos - oldnpos);
+ }
+}
+
+int
+b_cut(FILE *fp, const char *fname __unused)
+{
+ int ch, col;
+ char *pos;
+
+ ch = 0;
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getc(fp)) == EOF)
+ return (0);
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putchar(ch);
+ }
+ if (ch != '\n') {
+ if (autostop)
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ (void)putchar(ch);
+ else
+ while ((ch = getc(fp)) != EOF && ch != '\n');
+ }
+ (void)putchar('\n');
+ }
+ return (0);
+}
+
+/*
+ * Cut based on byte positions, taking care not to split multibyte characters.
+ * Although this function also handles the case where -n is not specified,
+ * b_cut() ought to be much faster.
+ */
+int
+b_n_cut(FILE *fp, const char *fname)
+{
+ size_t col, i, lbuflen;
+ char *lbuf;
+ int canwrite, clen, warned;
+ mbstate_t mbs;
+
+ memset(&mbs, 0, sizeof(mbs));
+ warned = 0;
+ while ((lbuf = fgetln(fp, &lbuflen)) != NULL) {
+ for (col = 0; lbuflen > 0; col += clen) {
+ if ((clen = mbrlen(lbuf, lbuflen, &mbs)) < 0) {
+ if (!warned) {
+ warn("%s", fname);
+ warned = 1;
+ }
+ memset(&mbs, 0, sizeof(mbs));
+ clen = 1;
+ }
+ if (clen == 0 || *lbuf == '\n')
+ break;
+ if (col < maxval && !positions[1 + col]) {
+ /*
+ * Print the character if (1) after an initial
+ * segment of un-selected bytes, the rest of
+ * it is selected, and (2) the last byte is
+ * selected.
+ */
+ i = col;
+ while (i < col + clen && i < maxval &&
+ !positions[1 + i])
+ i++;
+ canwrite = i < col + clen;
+ for (; i < col + clen && i < maxval; i++)
+ canwrite &= positions[1 + i];
+ if (canwrite)
+ fwrite(lbuf, 1, clen, stdout);
+ } else {
+ /*
+ * Print the character if all of it has
+ * been selected.
+ */
+ canwrite = 1;
+ for (i = col; i < col + clen; i++)
+ if ((i >= maxval && !autostop) ||
+ (i < maxval && !positions[1 + i])) {
+ canwrite = 0;
+ break;
+ }
+ if (canwrite)
+ fwrite(lbuf, 1, clen, stdout);
+ }
+ lbuf += clen;
+ lbuflen -= clen;
+ }
+ if (lbuflen > 0)
+ putchar('\n');
+ }
+ return (warned);
+}
+
+int
+c_cut(FILE *fp, const char *fname)
+{
+ wint_t ch;
+ int col;
+ char *pos;
+
+ ch = 0;
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getwc(fp)) == WEOF)
+ goto out;
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putwchar(ch);
+ }
+ if (ch != '\n') {
+ if (autostop)
+ while ((ch = getwc(fp)) != WEOF && ch != '\n')
+ (void)putwchar(ch);
+ else
+ while ((ch = getwc(fp)) != WEOF && ch != '\n');
+ }
+ (void)putwchar('\n');
+ }
+out:
+ if (ferror(fp)) {
+ warn("%s", fname);
+ return (1);
+ }
+ return (0);
+}
+
+int
+f_cut(FILE *fp, const char *fname)
+{
+ wchar_t ch;
+ int field, i, isdelim;
+ char *pos, *p;
+ wchar_t sep;
+ int output;
+ char *lbuf, *mlbuf;
+ size_t clen, lbuflen, reallen;
+
+ mlbuf = NULL;
+ for (sep = dchar; (lbuf = fgetln(fp, &lbuflen)) != NULL;) {
+ reallen = lbuflen;
+ /* Assert EOL has a newline. */
+ if (*(lbuf + lbuflen - 1) != '\n') {
+ /* Can't have > 1 line with no trailing newline. */
+ mlbuf = malloc(lbuflen + 1);
+ if (mlbuf == NULL)
+ err(1, "malloc");
+ memcpy(mlbuf, lbuf, lbuflen);
+ *(mlbuf + lbuflen) = '\n';
+ lbuf = mlbuf;
+ reallen++;
+ }
+ output = 0;
+ for (isdelim = 0, p = lbuf;; p += clen) {
+ clen = mbrtowc(&ch, p, lbuf + reallen - p, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2) {
+ warnc(EILSEQ, "%s", fname);
+ free(mlbuf);
+ return (1);
+ }
+ if (clen == 0)
+ clen = 1;
+ /* this should work if newline is delimiter */
+ if (ch == sep)
+ isdelim = 1;
+ if (ch == '\n') {
+ if (!isdelim && !sflag)
+ (void)fwrite(lbuf, lbuflen, 1, stdout);
+ break;
+ }
+ }
+ if (!isdelim)
+ continue;
+
+ pos = positions + 1;
+ for (field = maxval, p = lbuf; field; --field, ++pos) {
+ if (*pos && output++)
+ for (i = 0; dcharmb[i] != '\0'; i++)
+ putchar(dcharmb[i]);
+ for (;;) {
+ clen = mbrtowc(&ch, p, lbuf + reallen - p,
+ NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2) {
+ warnc(EILSEQ, "%s", fname);
+ free(mlbuf);
+ return (1);
+ }
+ if (clen == 0)
+ clen = 1;
+ p += clen;
+ if (ch == '\n' || ch == sep)
+ break;
+ if (*pos)
+ for (i = 0; i < (int)clen; i++)
+ putchar(p[i - clen]);
+ }
+ if (ch == '\n')
+ break;
+ }
+ if (ch != '\n') {
+ if (autostop) {
+ if (output)
+ for (i = 0; dcharmb[i] != '\0'; i++)
+ putchar(dcharmb[i]);
+ for (; (ch = *p) != '\n'; ++p)
+ (void)putchar(ch);
+ } else
+ for (; (ch = *p) != '\n'; ++p);
+ }
+ (void)putchar('\n');
+ }
+ free(mlbuf);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: cut -b list [-n] [file ...]",
+ " cut -c list [file ...]",
+ " cut -f list [-s] [-d delim] [file ...]");
+ exit(1);
+}
diff --git a/text_cmds/ed/POSIX b/text_cmds/ed/POSIX
new file mode 100644
index 0000000..0a363d6
--- /dev/null
+++ b/text_cmds/ed/POSIX
@@ -0,0 +1,101 @@
+$FreeBSD: src/bin/ed/POSIX,v 1.8 2000/07/17 10:40:48 sheldonh Exp $
+
+This version of ed(1) is not strictly POSIX compliant, as described in
+the POSIX 1003.2 document. The following is a summary of the omissions,
+extensions and possible deviations from POSIX 1003.2.
+
+OMISSIONS
+---------
+1) For backwards compatibility, the POSIX rule that says a range of
+ addresses cannot be used where only a single address is expected has
+ been relaxed.
+
+2) To support the BSD `s' command (see extension [1] below),
+ substitution patterns cannot be delimited by numbers or the characters
+ `r', `g' and `p'. In contrast, POSIX specifies any character expect
+ space or newline can used as a delimiter.
+
+EXTENSIONS
+----------
+1) BSD commands have been implemented wherever they do not conflict with
+ the POSIX standard. The BSD-ism's included are:
+ i) `s' (i.e., s[n][rgp]*) to repeat a previous substitution,
+ ii) `W' for appending text to an existing file,
+ iii) `wq' for exiting after a write,
+ iv) `z' for scrolling through the buffer, and
+ v) BSD line addressing syntax (i.e., `^' and `%') is recognized.
+
+2) If crypt(3) is available, files can be read and written using DES
+ encryption. The `x' command prompts the user to enter a key used for
+ encrypting/ decrypting subsequent reads and writes. If only a newline
+ is entered as the key, then encryption is disabled. Otherwise, a key
+ is read in the same manner as a password entry. The key remains in
+ effect until encryption is disabled. For more information on the
+ encryption algorithm, see the bdes(1) man page. Encryption/decryption
+ should be fully compatible with SunOS des(1).
+
+3) The POSIX interactive global commands `G' and `V' are extended to
+ support multiple commands, including `a', `i' and `c'. The command
+ format is the same as for the global commands `g' and `v', i.e., one
+ command per line with each line, except for the last, ending in a
+ backslash (\).
+
+4) An extension to the POSIX file commands `E', `e', `r', `W' and `w' is
+ that <file> arguments are processed for backslash escapes, i.e., any
+ character preceded by a backslash is interpreted literally. If the
+ first unescaped character of a <file> argument is a bang (!), then the
+ rest of the line is interpreted as a shell command, and no escape
+ processing is performed by ed.
+
+5) For SunOS ed(1) compatibility, ed runs in restricted mode if invoked
+ as red. This limits editing of files in the local directory only and
+ prohibits shell commands.
+
+DEVIATIONS
+----------
+1) Though ed is not a stream editor, it can be used to edit binary files.
+ To assist in binary editing, when a file containing at least one ASCII
+ NUL character is written, a newline is not appended if it did not
+ already contain one upon reading. In particular, reading /dev/null
+ prior to writing prevents appending a newline to a binary file.
+
+ For example, to create a file with ed containing a single NUL character:
+ $ ed file
+ a
+ ^@
+ .
+ r /dev/null
+ wq
+
+ Similarly, to remove a newline from the end of binary `file':
+ $ ed file
+ r /dev/null
+ wq
+
+2) Since the behavior of `u' (undo) within a `g' (global) command list is
+ not specified by POSIX, it follows the behavior of the SunOS ed:
+ undo forces a global command list to be executed only once, rather than
+ for each line matching a global pattern. In addtion, each instance of
+ `u' within a global command undoes all previous commands (including
+ undo's) in the command list. This seems the best way, since the
+ alternatives are either too complicated to implement or too confusing
+ to use.
+
+ The global/undo combination is useful for masking errors that
+ would otherwise cause a script to fail. For instance, an ed script
+ to remove any occurences of either `censor1' or `censor2' might be
+ written as:
+ ed - file <<EOF
+ 1g/.*/u\
+ ,s/censor1//g\
+ ,s/censor2//g
+ ...
+
+3) The `m' (move) command within a `g' command list also follows the SunOS
+ ed implementation: any moved lines are removed from the global command's
+ `active' list.
+
+4) If ed is invoked with a name argument prefixed by a bang (!), then the
+ remainder of the argument is interpreted as a shell command. To invoke
+ ed on a file whose name starts with bang, prefix the name with a
+ backslash.
diff --git a/text_cmds/ed/README b/text_cmds/ed/README
new file mode 100644
index 0000000..1f50f10
--- /dev/null
+++ b/text_cmds/ed/README
@@ -0,0 +1,24 @@
+$FreeBSD: src/bin/ed/README,v 1.7 1999/08/27 23:14:12 peter Exp $
+
+ed is an 8-bit-clean, POSIX-compliant line editor. It should work with
+any regular expression package that conforms to the POSIX interface
+standard, such as GNU regex(3).
+
+If reliable signals are supported (e.g., POSIX sigaction(2)), it should
+compile with little trouble. Otherwise, the macros SPL1() and SPL0()
+should be redefined to disable interrupts.
+
+The following compiler directives are recognized:
+DES - to add encryption support (requires crypt(3))
+NO_REALLOC_NULL - if realloc(3) does not accept a NULL pointer
+BACKWARDS - for backwards compatibility
+NEED_INSQUE - if insque(3) is missing
+
+The file `POSIX' describes extensions to and deviations from the POSIX
+standard.
+
+The ./test directory contains regression tests for ed. The README
+file in that directory explains how to run these.
+
+For a description of the ed algorithm, see Kernighan and Plauger's book
+"Software Tools in Pascal," Addison-Wesley, 1981.
diff --git a/text_cmds/ed/buf.c b/text_cmds/ed/buf.c
new file mode 100644
index 0000000..93c1b92
--- /dev/null
+++ b/text_cmds/ed/buf.c
@@ -0,0 +1,284 @@
+/* buf.c: This file contains the scratch-file buffer routines for the
+ ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/buf.c,v 1.22 2002/06/30 05:13:53 obrien Exp $");
+
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "ed.h"
+
+
+FILE *sfp; /* scratch file pointer */
+off_t sfseek; /* scratch file position */
+int seek_write; /* seek before writing */
+line_t buffer_head; /* incore buffer */
+
+/* get_sbuf_line: get a line of text from the scratch file; return pointer
+ to the text */
+char *
+get_sbuf_line(line_t *lp)
+{
+ static char *sfbuf = NULL; /* buffer */
+ static int sfbufsz = 0; /* buffer size */
+
+ int len, ct;
+
+ if (lp == &buffer_head)
+ return NULL;
+ seek_write = 1; /* force seek on write */
+ /* out of position */
+ if (sfseek != lp->seek) {
+ sfseek = lp->seek;
+ if (fseeko(sfp, sfseek, SEEK_SET) < 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot seek temp file";
+ return NULL;
+ }
+ }
+ len = lp->len;
+ REALLOC(sfbuf, sfbufsz, len + 1, NULL);
+ if ((ct = fread(sfbuf, sizeof(char), len, sfp)) < 0 || ct != len) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot read temp file";
+ return NULL;
+ }
+ sfseek += len; /* update file position */
+ sfbuf[len] = '\0';
+ return sfbuf;
+}
+
+
+/* put_sbuf_line: write a line of text to the scratch file and add a line node
+ to the editor buffer; return a pointer to the end of the text */
+const char *
+put_sbuf_line(const char *cs)
+{
+ line_t *lp;
+ int len, ct;
+ const char *s;
+
+ if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ return NULL;
+ }
+ /* assert: cs is '\n' terminated */
+ for (s = cs; *s != '\n'; s++)
+ ;
+ if (s - cs >= LINECHARS) {
+ errmsg = "line too long";
+ return NULL;
+ }
+ len = s - cs;
+ /* out of position */
+ if (seek_write) {
+ if (fseeko(sfp, (off_t)0, SEEK_END) < 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot seek temp file";
+ return NULL;
+ }
+ sfseek = ftello(sfp);
+ seek_write = 0;
+ }
+ /* assert: SPL1() */
+ if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
+ sfseek = -1;
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot write temp file";
+ return NULL;
+ }
+ lp->len = len;
+ lp->seek = sfseek;
+ add_line_node(lp);
+ sfseek += len; /* update file position */
+ return ++s;
+}
+
+
+/* add_line_node: add a line node in the editor buffer after the current line */
+void
+add_line_node(line_t *lp)
+{
+ line_t *cp;
+
+ cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */
+ INSQUE(lp, cp);
+ addr_last++;
+ current_addr++;
+}
+
+
+/* get_line_node_addr: return line number of pointer */
+long
+get_line_node_addr(line_t *lp)
+{
+ line_t *cp = &buffer_head;
+ long n = 0;
+
+ while (cp != lp && (cp = cp->q_forw) != &buffer_head)
+ n++;
+ if (n && cp == &buffer_head) {
+ errmsg = "invalid address";
+ return ERR;
+ }
+ return n;
+}
+
+
+/* get_addressed_line_node: return pointer to a line node in the editor buffer */
+line_t *
+get_addressed_line_node(long n)
+{
+ static line_t *lp = &buffer_head;
+ static long on = 0;
+
+ SPL1();
+ if (n > on)
+ if (n <= (on + addr_last) >> 1)
+ for (; on < n; on++)
+ lp = lp->q_forw;
+ else {
+ lp = buffer_head.q_back;
+ for (on = addr_last; on > n; on--)
+ lp = lp->q_back;
+ }
+ else
+ if (n >= on >> 1)
+ for (; on > n; on--)
+ lp = lp->q_back;
+ else {
+ lp = &buffer_head;
+ for (on = 0; on < n; on++)
+ lp = lp->q_forw;
+ }
+ SPL0();
+ return lp;
+}
+
+
+extern int newline_added;
+
+char sfn[15] = ""; /* scratch file name */
+
+/* open_sbuf: open scratch file */
+int
+open_sbuf(void)
+{
+ int fd;
+ int u;
+
+ isbinary = newline_added = 0;
+ u = umask(077);
+ strcpy(sfn, "/tmp/ed.XXXXXX");
+ if ((fd = mkstemp(sfn)) == -1 ||
+ (sfp = fdopen(fd, "w+")) == NULL) {
+ if (fd != -1)
+ close(fd);
+ perror(sfn);
+ errmsg = "cannot open temp file";
+ umask(u);
+ return ERR;
+ }
+ umask(u);
+ return 0;
+}
+
+
+/* close_sbuf: close scratch file */
+int
+close_sbuf(void)
+{
+ if (sfp) {
+ if (fclose(sfp) < 0) {
+ fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
+ errmsg = "cannot close temp file";
+ return ERR;
+ }
+ sfp = NULL;
+ unlink(sfn);
+ }
+ sfseek = seek_write = 0;
+ return 0;
+}
+
+
+/* quit: remove_lines scratch file and exit */
+void
+quit(int n)
+{
+ if (sfp) {
+ fclose(sfp);
+ unlink(sfn);
+ }
+ exit(n);
+}
+
+
+unsigned char ctab[256]; /* character translation table */
+
+/* init_buffers: open scratch buffer; initialize line queue */
+void
+init_buffers(void)
+{
+ int i = 0;
+
+ /* Read stdin one character at a time to avoid i/o contention
+ with shell escapes invoked by nonterminal input, e.g.,
+ ed - <<EOF
+ !cat
+ hello, world
+ EOF */
+ setbuffer(stdin, stdinbuf, 1);
+
+ /* Ensure stdout is line buffered. This avoids bogus delays
+ of output if stdout is piped through utilities to a terminal. */
+ setvbuf(stdout, NULL, _IOLBF, 0);
+ if (open_sbuf() < 0)
+ quit(2);
+ REQUE(&buffer_head, &buffer_head);
+ for (i = 0; i < 256; i++)
+ ctab[i] = i;
+}
+
+
+/* translit_text: translate characters in a string */
+char *
+translit_text(char *s, int len, int from, int to)
+{
+ static int i = 0;
+
+ unsigned char *us;
+
+ ctab[i] = i; /* restore table to initial state */
+ ctab[i = from] = to;
+ for (us = (unsigned char *) s; len-- > 0; us++)
+ *us = ctab[*us];
+ return s;
+}
diff --git a/text_cmds/ed/cbc.c b/text_cmds/ed/cbc.c
new file mode 100644
index 0000000..22dd0d2
--- /dev/null
+++ b/text_cmds/ed/cbc.c
@@ -0,0 +1,402 @@
+/* cbc.c: This file contains the encryption routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/cbc.c,v 1.20 2004/04/06 20:06:47 markm Exp $");
+
+#include <sys/types.h>
+#include <errno.h>
+#include <pwd.h>
+#ifdef DES
+#include <time.h>
+#include <openssl/des.h>
+#define ED_DES_INCLUDES
+#endif
+
+#include "ed.h"
+
+
+/*
+ * BSD and System V systems offer special library calls that do
+ * block move_liness and fills, so if possible we take advantage of them
+ */
+#define MEMCPY(dest,src,len) memcpy((dest),(src),(len))
+#define MEMZERO(dest,len) memset((dest), 0, (len))
+
+/* Hide the calls to the primitive encryption routines. */
+#define DES_XFORM(buf) \
+ DES_ecb_encrypt(buf, buf, &schedule, \
+ inverse ? DES_DECRYPT : DES_ENCRYPT);
+
+/*
+ * read/write - no error checking
+ */
+#define READ(buf, n, fp) fread(buf, sizeof(char), n, fp)
+#define WRITE(buf, n, fp) fwrite(buf, sizeof(char), n, fp)
+
+/*
+ * global variables and related macros
+ */
+
+enum { /* encrypt, decrypt, authenticate */
+ MODE_ENCRYPT, MODE_DECRYPT, MODE_AUTHENTICATE
+} mode = MODE_ENCRYPT;
+
+#ifdef DES
+DES_cblock ivec; /* initialization vector */
+DES_cblock pvec; /* padding vector */
+#endif
+
+char bits[] = { /* used to extract bits from a char */
+ '\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001'
+};
+
+int pflag; /* 1 to preserve parity bits */
+
+#ifdef DES
+DES_key_schedule schedule; /* expanded DES key */
+#endif
+
+unsigned char des_buf[8]; /* shared buffer for get_des_char/put_des_char */
+int des_ct = 0; /* count for get_des_char/put_des_char */
+int des_n = 0; /* index for put_des_char/get_des_char */
+
+/* init_des_cipher: initialize DES */
+void
+init_des_cipher(void)
+{
+#ifdef DES
+ int i;
+
+ des_ct = des_n = 0;
+
+ /* initialize the initialization vector */
+ MEMZERO(ivec, 8);
+
+ /* initialize the padding vector */
+ for (i = 0; i < 8; i++)
+ pvec[i] = (char) (arc4random() % 256);
+#endif
+}
+
+
+/* get_des_char: return next char in an encrypted file */
+int
+get_des_char(FILE *fp)
+{
+#ifdef DES
+ if (des_n >= des_ct) {
+ des_n = 0;
+ des_ct = cbc_decode(des_buf, fp);
+ }
+ return (des_ct > 0) ? des_buf[des_n++] : EOF;
+#else
+ return (getc(fp));
+#endif
+}
+
+
+/* put_des_char: write a char to an encrypted file; return char written */
+int
+put_des_char(int c, FILE *fp)
+{
+#ifdef DES
+ if (des_n == sizeof des_buf) {
+ des_ct = cbc_encode(des_buf, des_n, fp);
+ des_n = 0;
+ }
+ return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF;
+#else
+ return (fputc(c, fp));
+#endif
+}
+
+
+/* flush_des_file: flush an encrypted file's output; return status */
+int
+flush_des_file(FILE *fp)
+{
+#ifdef DES
+ if (des_n == sizeof des_buf) {
+ des_ct = cbc_encode(des_buf, des_n, fp);
+ des_n = 0;
+ }
+ return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF;
+#else
+ return (fflush(fp));
+#endif
+}
+
+#ifdef DES
+/*
+ * get keyword from tty or stdin
+ */
+int
+get_keyword(void)
+{
+ char *p; /* used to obtain the key */
+ DES_cblock msgbuf; /* I/O buffer */
+
+ /*
+ * get the key
+ */
+ if (*(p = getpass("Enter key: "))) {
+
+ /*
+ * copy it, nul-padded, into the key area
+ */
+ expand_des_key(msgbuf, p);
+ MEMZERO(p, _PASSWORD_LEN);
+ set_des_key(&msgbuf);
+ MEMZERO(msgbuf, sizeof msgbuf);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * print a warning message and, possibly, terminate
+ */
+void
+des_error(const char *s)
+{
+ errmsg = s ? s : strerror(errno);
+}
+
+/*
+ * map a hex character to an integer
+ */
+int
+hex_to_binary(int c, int radix)
+{
+ switch(c) {
+ case '0': return(0x0);
+ case '1': return(0x1);
+ case '2': return(radix > 2 ? 0x2 : -1);
+ case '3': return(radix > 3 ? 0x3 : -1);
+ case '4': return(radix > 4 ? 0x4 : -1);
+ case '5': return(radix > 5 ? 0x5 : -1);
+ case '6': return(radix > 6 ? 0x6 : -1);
+ case '7': return(radix > 7 ? 0x7 : -1);
+ case '8': return(radix > 8 ? 0x8 : -1);
+ case '9': return(radix > 9 ? 0x9 : -1);
+ case 'A': case 'a': return(radix > 10 ? 0xa : -1);
+ case 'B': case 'b': return(radix > 11 ? 0xb : -1);
+ case 'C': case 'c': return(radix > 12 ? 0xc : -1);
+ case 'D': case 'd': return(radix > 13 ? 0xd : -1);
+ case 'E': case 'e': return(radix > 14 ? 0xe : -1);
+ case 'F': case 'f': return(radix > 15 ? 0xf : -1);
+ }
+ /*
+ * invalid character
+ */
+ return(-1);
+}
+
+/*
+ * convert the key to a bit pattern
+ * obuf bit pattern
+ * kbuf the key itself
+ */
+void
+expand_des_key(char *obuf, char *kbuf)
+{
+ int i, j; /* counter in a for loop */
+ int nbuf[64]; /* used for hex/key translation */
+
+ /*
+ * leading '0x' or '0X' == hex key
+ */
+ if (kbuf[0] == '0' && (kbuf[1] == 'x' || kbuf[1] == 'X')) {
+ kbuf = &kbuf[2];
+ /*
+ * now translate it, bombing on any illegal hex digit
+ */
+ for (i = 0; kbuf[i] && i < 16; i++)
+ if ((nbuf[i] = hex_to_binary((int) kbuf[i], 16)) == -1)
+ des_error("bad hex digit in key");
+ while (i < 16)
+ nbuf[i++] = 0;
+ for (i = 0; i < 8; i++)
+ obuf[i] =
+ ((nbuf[2*i]&0xf)<<4) | (nbuf[2*i+1]&0xf);
+ /* preserve parity bits */
+ pflag = 1;
+ return;
+ }
+ /*
+ * leading '0b' or '0B' == binary key
+ */
+ if (kbuf[0] == '0' && (kbuf[1] == 'b' || kbuf[1] == 'B')) {
+ kbuf = &kbuf[2];
+ /*
+ * now translate it, bombing on any illegal binary digit
+ */
+ for (i = 0; kbuf[i] && i < 16; i++)
+ if ((nbuf[i] = hex_to_binary((int) kbuf[i], 2)) == -1)
+ des_error("bad binary digit in key");
+ while (i < 64)
+ nbuf[i++] = 0;
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ obuf[i] = (obuf[i]<<1)|nbuf[8*i+j];
+ /* preserve parity bits */
+ pflag = 1;
+ return;
+ }
+ /*
+ * no special leader -- ASCII
+ */
+ (void)strncpy(obuf, kbuf, 8);
+}
+
+/*****************
+ * DES FUNCTIONS *
+ *****************/
+/*
+ * This sets the DES key and (if you're using the deszip version)
+ * the direction of the transformation. This uses the Sun
+ * to map the 64-bit key onto the 56 bits that the key schedule
+ * generation routines use: the old way, which just uses the user-
+ * supplied 64 bits as is, and the new way, which resets the parity
+ * bit to be the same as the low-order bit in each character. The
+ * new way generates a greater variety of key schedules, since many
+ * systems set the parity (high) bit of each character to 0, and the
+ * DES ignores the low order bit of each character.
+ */
+void
+set_des_key(DES_cblock *buf) /* key block */
+{
+ int i, j; /* counter in a for loop */
+ int par; /* parity counter */
+
+ /*
+ * if the parity is not preserved, flip it
+ */
+ if (!pflag) {
+ for (i = 0; i < 8; i++) {
+ par = 0;
+ for (j = 1; j < 8; j++)
+ if ((bits[j] & (*buf)[i]) != 0)
+ par++;
+ if ((par & 0x01) == 0x01)
+ (*buf)[i] &= 0x7f;
+ else
+ (*buf)[i] = ((*buf)[i] & 0x7f) | 0x80;
+ }
+ }
+
+ DES_set_odd_parity(buf);
+ DES_set_key(buf, &schedule);
+}
+
+
+/*
+ * This encrypts using the Cipher Block Chaining mode of DES
+ */
+int
+cbc_encode(unsigned char *msgbuf, int n, FILE *fp)
+{
+ int inverse = 0; /* 0 to encrypt, 1 to decrypt */
+
+ /*
+ * do the transformation
+ */
+ if (n == 8) {
+ for (n = 0; n < 8; n++)
+ msgbuf[n] ^= ivec[n];
+ DES_XFORM((DES_cblock *)msgbuf);
+ MEMCPY(ivec, msgbuf, 8);
+ return WRITE(msgbuf, 8, fp);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+/*
+ MEMZERO(msgbuf + n, 8 - n);
+*/
+ /*
+ * Pad the last block randomly
+ */
+ (void)MEMCPY(msgbuf + n, pvec, 8 - n);
+ msgbuf[7] = n;
+ for (n = 0; n < 8; n++)
+ msgbuf[n] ^= ivec[n];
+ DES_XFORM((DES_cblock *)msgbuf);
+ return WRITE(msgbuf, 8, fp);
+}
+
+/*
+ * This decrypts using the Cipher Block Chaining mode of DES
+ * msgbuf I/O buffer
+ * fp input file descriptor
+ */
+int
+cbc_decode(unsigned char *msgbuf, FILE *fp)
+{
+ DES_cblock tbuf; /* temp buffer for initialization vector */
+ int n; /* number of bytes actually read */
+ int c; /* used to test for EOF */
+ int inverse = 1; /* 0 to encrypt, 1 to decrypt */
+
+ if ((n = READ(msgbuf, 8, fp)) == 8) {
+ /*
+ * do the transformation
+ */
+ MEMCPY(tbuf, msgbuf, 8);
+ DES_XFORM((DES_cblock *)msgbuf);
+ for (c = 0; c < 8; c++)
+ msgbuf[c] ^= ivec[c];
+ MEMCPY(ivec, tbuf, 8);
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = fgetc(fp)) == EOF) {
+ n = msgbuf[7];
+ if (n < 0 || n > 7) {
+ des_error("decryption failed (block corrupted)");
+ return EOF;
+ }
+ } else
+ (void)ungetc(c, fp);
+ return n;
+ }
+ if (n > 0)
+ des_error("decryption failed (incomplete block)");
+ else if (n < 0)
+ des_error("cannot read file");
+ return EOF;
+}
+#endif /* DES */
diff --git a/text_cmds/ed/ed.1 b/text_cmds/ed/ed.1
new file mode 100644
index 0000000..8c4b6b2
--- /dev/null
+++ b/text_cmds/ed/ed.1
@@ -0,0 +1,1004 @@
+.\" $FreeBSD: src/bin/ed/ed.1,v 1.35 2005/01/16 16:41:56 ru Exp $
+.Dd July 3, 2004
+.Dt ED 1
+.Os
+.Sh NAME
+.Nm ed ,
+.Nm red
+.Nd text editor
+.Sh SYNOPSIS
+.Nm
+.Op Fl
+.Op Fl sx
+.Op Fl p Ar string
+.Op Ar file
+.Nm red
+.Op Fl
+.Op Fl sx
+.Op Fl p Ar string
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility is a line-oriented text editor.
+It is used to create, display, modify and otherwise manipulate text
+files.
+When invoked as
+.Nm red ,
+the editor runs in
+.Qq restricted
+mode, in which the only difference is that the editor restricts the
+use of filenames which start with
+.Ql \&!
+(interpreted as shell commands by
+.Nm )
+or contain a
+.Ql \&/ .
+Note that editing outside of the current directory is only prohibited
+if the user does not have write access to the current directory.
+If a user has write access to the current directory, then symbolic
+links can be created in the current directory, in which case
+.Nm red
+will not stop the user from editing the file that the symbolic link
+points to.
+.Pp
+If invoked with a
+.Ar file
+argument, then a copy of
+.Ar file
+is read into the editor's buffer.
+Changes are made to this copy and not directly to
+.Ar file
+itself.
+Upon quitting
+.Nm ,
+any changes not explicitly saved with a
+.Em w
+command are lost.
+.Pp
+Editing is done in two distinct modes:
+.Em command
+and
+.Em input .
+When first invoked,
+.Nm
+is in command mode.
+In this mode commands are read from the standard input and
+executed to manipulate the contents of the editor buffer.
+A typical command might look like:
+.Pp
+.Sm off
+.Cm ,s No / Em old Xo
+.No / Em new
+.No / Cm g
+.Xc
+.Sm on
+.Pp
+which replaces all occurrences of the string
+.Em old
+with
+.Em new .
+.Pp
+When an input command, such as
+.Em a
+(append),
+.Em i
+(insert) or
+.Em c
+(change), is given,
+.Nm
+enters input mode.
+This is the primary means
+of adding text to a file.
+In this mode, no commands are available;
+instead, the standard input is written
+directly to the editor buffer.
+Lines consist of text up to and
+including a
+.Em newline
+character.
+Input mode is terminated by
+entering a single period
+.Pq Em .\&
+on a line.
+.Pp
+All
+.Nm
+commands operate on whole lines or ranges of lines; e.g.,
+the
+.Em d
+command deletes lines; the
+.Em m
+command moves lines, and so on.
+It is possible to modify only a portion of a line by means of replacement,
+as in the example above.
+However even here, the
+.Em s
+command is applied to whole lines at a time.
+.Pp
+In general,
+.Nm
+commands consist of zero or more line addresses, followed by a single
+character command and possibly additional parameters; i.e.,
+commands have the structure:
+.Pp
+.Sm off
+.Xo
+.Op Ar address Op , Ar address
+.Ar command Op Ar parameters
+.Xc
+.Sm on
+.Pp
+The address(es) indicate the line or range of lines to be affected by the
+command.
+If fewer addresses are given than the command accepts, then
+default addresses are supplied.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl s
+Suppress diagnostics.
+This should be used if
+.Nm Ns 's
+standard input is from a script.
+.It Fl x
+Prompt for an encryption key to be used in subsequent reads and writes
+(see the
+.Em x
+command).
+Unsupported on Mac OS X.
+.It Fl p Ar string
+Specify a command prompt.
+This may be toggled on and off with the
+.Em P
+command.
+.It Ar file
+Specify the name of a file to read.
+If
+.Ar file
+is prefixed with a
+bang (!), then it is interpreted as a shell command.
+In this case,
+what is read is
+the standard output of
+.Ar file
+executed via
+.Xr sh 1 .
+To read a file whose name begins with a bang, prefix the
+name with a backslash (\\).
+The default filename is set to
+.Ar file
+only if it is not prefixed with a bang.
+.El
+.Sh LINE ADDRESSING
+An address represents the number of a line in the buffer.
+The
+.Nm
+utility maintains a
+.Em current address
+which is
+typically supplied to commands as the default address when none is specified.
+When a file is first read, the current address is set to the last line
+of the file.
+In general, the current address is set to the last line
+affected by a command.
+.Pp
+A line address is
+constructed from one of the bases in the list below, optionally followed
+by a numeric offset.
+The offset may include any combination
+of digits, operators (i.e.,
+.Em + ,
+.Em -
+and
+.Em ^ )
+and whitespace.
+Addresses are read from left to right, and their values are computed
+relative to the current address.
+.Pp
+One exception to the rule that addresses represent line numbers is the
+address
+.Em 0
+(zero).
+This means "before the first line,"
+and is legal wherever it makes sense.
+.Pp
+An address range is two addresses separated either by a comma or
+semi-colon.
+The value of the first address in a range cannot exceed the
+value of the second.
+If only one address is given in a range, then
+the second address is set to the given address.
+If an
+.Em n Ns -tuple
+of addresses is given where
+.Em "n\ >\ 2" ,
+then the corresponding range is determined by the last two addresses in
+the
+.Em n Ns -tuple .
+If only one address is expected, then the last address is used.
+.Pp
+Each address in a comma-delimited range is interpreted relative to the
+current address.
+In a semi-colon-delimited range, the first address is
+used to set the current address, and the second address is interpreted
+relative to the first.
+.Pp
+The following address symbols are recognized:
+.Bl -tag -width indent
+.It .
+The current line (address) in the buffer.
+.It $
+The last line in the buffer.
+.It n
+The
+.Em n Ns th,
+line in the buffer
+where
+.Em n
+is a number in the range
+.Em [0,$] .
+.It - or ^
+The previous line.
+This is equivalent to
+.Em -1
+and may be repeated with cumulative effect.
+.It -n or ^n
+The
+.Em n Ns th
+previous line, where
+.Em n
+is a non-negative number.
+.It +
+The next line.
+This is equivalent to
+.Em +1
+and may be repeated with cumulative effect.
+.It +n
+The
+.Em n Ns th
+next line, where
+.Em n
+is a non-negative number.
+.It , or %
+The first through last lines in the buffer.
+This is equivalent to
+the address range
+.Em 1,$ .
+.It ;
+The current through last lines in the buffer.
+This is equivalent to
+the address range
+.Em .,$ .
+.It /re/
+The next line containing the regular expression
+.Em re .
+The search wraps to the beginning of the buffer and continues down to the
+current line, if necessary.
+// repeats the last search.
+.It ?re?
+The
+previous line containing the regular expression
+.Em re .
+The search wraps to the end of the buffer and continues up to the
+current line, if necessary.
+?? repeats the last search.
+.It 'lc
+The
+line previously marked by a
+.Em k
+(mark) command, where
+.Em lc
+is a lower case letter.
+.El
+.Sh REGULAR EXPRESSIONS
+Regular expressions are patterns used in selecting text.
+For example, the command:
+.Pp
+.Sm off
+.Cm g No / Em string Xo
+.No /
+.Xc
+.Sm on
+.Pp
+prints all lines containing
+.Em string .
+Regular expressions are also
+used by the
+.Em s
+command for selecting old text to be replaced with new.
+.Pp
+In addition to a specifying string literals, regular expressions can
+represent
+classes of strings.
+Strings thus represented are said to be matched
+by the corresponding regular expression.
+If it is possible for a regular expression
+to match several strings in a line, then the left-most longest match is
+the one selected.
+.Pp
+The following symbols are used in constructing regular expressions:
+.Bl -tag -width indent
+.It c
+Any character
+.Em c
+not listed below, including
+.Ql \&{ ,
+.Ql \&} ,
+.Ql \&( ,
+.Ql \&) ,
+.Ql <
+and
+.Ql > ,
+matches itself.
+.It Pf \e c
+Any backslash-escaped character
+.Em c ,
+except for
+.Ql \&{ ,
+.Ql \&} ,
+.Ql \&( ,
+.Ql \&) ,
+.Ql <
+and
+.Ql > ,
+matches itself.
+.It .
+Match any single character.
+.It Op char-class
+Match any single character in
+.Em char-class .
+To include a
+.Ql \&]
+in
+.Em char-class ,
+it must be the first character.
+A range of characters may be specified by separating the end characters
+of the range with a
+.Ql - ,
+e.g.,
+.Ql a-z
+specifies the lower case characters.
+The following literal expressions can also be used in
+.Em char-class
+to specify sets of characters:
+.Pp
+.Bl -column "[:alnum:]" "[:cntrl:]" "[:lower:]" "[:xdigit:]" -compact
+.It [:alnum:] Ta [:cntrl:] Ta [:lower:] Ta [:space:]
+.It [:alpha:] Ta [:digit:] Ta [:print:] Ta [:upper:]
+.It [:blank:] Ta [:graph:] Ta [:punct:] Ta [:xdigit:]
+.El
+.Pp
+If
+.Ql -
+appears as the first or last
+character of
+.Em char-class ,
+then it matches itself.
+All other characters in
+.Em char-class
+match themselves.
+.Pp
+Patterns in
+.Em char-class
+of the form:
+.Pp
+.Bl -item -compact -offset 2n
+.It
+.Op \&. Ns Ar col-elm Ns .\&
+or,
+.It
+.Op = Ns Ar col-elm Ns =
+.El
+.Pp
+where
+.Ar col-elm
+is a
+.Em collating element
+are interpreted according to the current locale settings
+(not currently supported).
+See
+.Xr regex 3
+and
+.Xr re_format 7
+for an explanation of these constructs.
+.It Op ^char-class
+Match any single character, other than newline, not in
+.Em char-class .
+.Em Char-class
+is defined
+as above.
+.It ^
+If
+.Em ^
+is the first character of a regular expression, then it
+anchors the regular expression to the beginning of a line.
+Otherwise, it matches itself.
+.It $
+If
+.Em $
+is the last character of a regular expression, it
+anchors the regular expression to the end of a line.
+Otherwise, it matches itself.
+.It Pf \e <
+Anchor the single character regular expression or subexpression
+immediately following it to the beginning of a word.
+(This may not be available)
+.It Pf \e >
+Anchor the single character regular expression or subexpression
+immediately following it to the end of a word.
+(This may not be available)
+.It Pf \e (re\e)
+Define a subexpression
+.Em re .
+Subexpressions may be nested.
+A subsequent backreference of the form
+.Pf \e Em n ,
+where
+.Em n
+is a number in the range [1,9], expands to the text matched by the
+.Em n Ns th
+subexpression.
+For example, the regular expression
+.Ql \e(.*\e)\e1
+matches any string
+consisting of identical adjacent substrings.
+Subexpressions are ordered relative to
+their left delimiter.
+.It *
+Match the single character regular expression or subexpression
+immediately preceding it zero or more times.
+If
+.Em *
+is the first
+character of a regular expression or subexpression, then it matches
+itself.
+The
+.Em *
+operator sometimes yields unexpected results.
+For example, the regular expression
+.Ql b*
+matches the beginning of
+the string
+.Ql abbb
+(as opposed to the substring
+.Ql bbb ) ,
+since a null match
+is the only left-most match.
+.It \e{n,m\e} or \e{n,\e} or \e{n\e}
+Match the single character regular expression or subexpression
+immediately preceding it at least
+.Em n
+and at most
+.Em m
+times.
+If
+.Em m
+is omitted, then it matches at least
+.Em n
+times.
+If the comma is also omitted, then it matches exactly
+.Em n
+times.
+.El
+.Pp
+Additional regular expression operators may be defined depending on the
+particular
+.Xr regex 3
+implementation.
+.Sh COMMANDS
+All
+.Nm
+commands are single characters, though some require additional parameters.
+If a command's parameters extend over several lines, then
+each line except for the last
+must be terminated with a backslash (\\).
+.Pp
+In general, at most one command is allowed per line.
+However, most commands accept a print suffix, which is any of
+.Em p
+(print),
+.Em l
+(list),
+or
+.Em n
+(enumerate),
+to print the last line affected by the command.
+.Pp
+An interrupt (typically ^C) has the effect of aborting the current command
+and returning the editor to command mode.
+.Pp
+The
+.Nm
+utility
+recognizes the following commands.
+The commands are shown together with
+the default address or address range supplied if none is
+specified (in parenthesis).
+.Bl -tag -width indent
+.It (.)a
+Append text to the buffer after the addressed line.
+Text is entered in input mode.
+The current address is set to last line entered.
+.It (.,.)c
+Change lines in the buffer.
+The addressed lines are deleted
+from the buffer, and text is appended in their place.
+Text is entered in input mode.
+The current address is set to last line entered.
+.It (.,.)d
+Delete the addressed lines from the buffer.
+If there is a line after the deleted range, then the current address is set
+to this line.
+Otherwise the current address is set to the line
+before the deleted range.
+.It e Ar file
+Edit
+.Ar file ,
+and sets the default filename.
+If
+.Ar file
+is not specified, then the default filename is used.
+Any lines in the buffer are deleted before
+the new file is read.
+The current address is set to the last line read.
+.It e Ar !command
+Edit the standard output of
+.Ar !command ,
+(see
+.Ar !command
+below).
+The default filename is unchanged.
+Any lines in the buffer are deleted before the output of
+.Ar command
+is read.
+The current address is set to the last line read.
+.It E Ar file
+Edit
+.Ar file
+unconditionally.
+This is similar to the
+.Em e
+command,
+except that unwritten changes are discarded without warning.
+The current address is set to the last line read.
+.It f Ar file
+Set the default filename to
+.Ar file .
+If
+.Ar file
+is not specified, then the default unescaped filename is printed.
+.It (1,$)g/re/command-list
+Apply
+.Ar command-list
+to each of the addressed lines matching a regular expression
+.Ar re .
+The current address is set to the
+line currently matched before
+.Ar command-list
+is executed.
+At the end of the
+.Em g
+command, the current address is set to the last line affected by
+.Ar command-list .
+.Pp
+Each command in
+.Ar command-list
+must be on a separate line,
+and every line except for the last must be terminated by a backslash
+(\\).
+Any commands are allowed, except for
+.Em g ,
+.Em G ,
+.Em v ,
+and
+.Em V .
+A newline alone in
+.Ar command-list
+is equivalent to a
+.Em p
+command.
+.It (1,$)G/re/
+Interactively edit the addressed lines matching a regular expression
+.Ar re .
+For each matching line,
+the line is printed,
+the current address is set,
+and the user is prompted to enter a
+.Ar command-list .
+At the end of the
+.Em G
+command, the current address
+is set to the last line affected by (the last)
+.Ar command-list .
+.Pp
+The format of
+.Ar command-list
+is the same as that of the
+.Em g
+command.
+A newline alone acts as a null command list.
+A single
+.Ql &
+repeats the last non-null command list.
+.It H
+Toggle the printing of error explanations.
+By default, explanations are not printed.
+It is recommended that ed scripts begin with this command to
+aid in debugging.
+.It h
+Print an explanation of the last error.
+.It (.)i
+Insert text in the buffer before the current line.
+Text is entered in input mode.
+The current address is set to the last line entered.
+.It (.,.+1)j
+Join the addressed lines.
+The addressed lines are
+deleted from the buffer and replaced by a single
+line containing their joined text.
+The current address is set to the resultant line.
+.It (.)klc
+Mark a line with a lower case letter
+.Em lc .
+The line can then be addressed as
+.Em 'lc
+(i.e., a single quote followed by
+.Em lc )
+in subsequent commands.
+The mark is not cleared until the line is
+deleted or otherwise modified.
+.It (.,.)l
+Print the addressed lines unambiguously.
+If a single line fills for than one screen (as might be the case
+when viewing a binary file, for instance), a
+.Dq Li --More--
+prompt is printed on the last line.
+The
+.Nm
+utility waits until the RETURN key is pressed
+before displaying the next screen.
+The current address is set to the last line
+printed.
+.It (.,.)m(.)
+Move lines in the buffer.
+The addressed lines are moved to after the
+right-hand destination address, which may be the address
+.Em 0
+(zero).
+The current address is set to the
+last line moved.
+.It (.,.)n
+Print the addressed lines along with
+their line numbers.
+The current address is set to the last line
+printed.
+.It (.,.)p
+Print the addressed lines.
+The current address is set to the last line
+printed.
+.It P
+Toggle the command prompt on and off.
+Unless a prompt was specified by with command-line option
+.Fl p Ar string ,
+the command prompt is by default turned off.
+.It q
+Quit
+.Nm .
+.It Q
+Quit
+.Nm
+unconditionally.
+This is similar to the
+.Em q
+command,
+except that unwritten changes are discarded without warning.
+.It ($)r Ar file
+Read
+.Ar file
+to after the addressed line.
+If
+.Ar file
+is not specified, then the default
+filename is used.
+If there was no default filename prior to the command,
+then the default filename is set to
+.Ar file .
+Otherwise, the default filename is unchanged.
+The current address is set to the last line read.
+.It ($)r Ar !command
+Read
+to after the addressed line
+the standard output of
+.Ar !command ,
+(see the
+.Ar !command
+below).
+The default filename is unchanged.
+The current address is set to the last line read.
+.It (.,.)s/re/replacement/
+.It (.,.)s/re/replacement/g
+.It (.,.)s/re/replacement/n
+Replace text in the addressed lines
+matching a regular expression
+.Ar re
+with
+.Ar replacement .
+By default, only the first match in each line is replaced.
+If the
+.Em g
+(global) suffix is given, then every match to be replaced.
+The
+.Em n
+suffix, where
+.Em n
+is a positive number, causes only the
+.Em n Ns th
+match to be replaced.
+It is an error if no substitutions are performed on any of the addressed
+lines.
+The current address is set the last line affected.
+.Pp
+.Ar Re
+and
+.Ar replacement
+may be delimited by any character other than space and newline
+(see the
+.Em s
+command below).
+If one or two of the last delimiters is omitted, then the last line
+affected is printed as though the print suffix
+.Em p
+were specified.
+.Pp
+An unescaped
+.Ql &
+in
+.Ar replacement
+is replaced by the currently matched text.
+The character sequence
+.Em \em ,
+where
+.Em m
+is a number in the range [1,9], is replaced by the
+.Em m th
+backreference expression of the matched text.
+If
+.Ar replacement
+consists of a single
+.Ql % ,
+then
+.Ar replacement
+from the last substitution is used.
+Newlines may be embedded in
+.Ar replacement
+if they are escaped with a backslash (\\).
+.It (.,.)s
+Repeat the last substitution.
+This form of the
+.Em s
+command accepts a count suffix
+.Em n ,
+or any combination of the characters
+.Em r ,
+.Em g ,
+and
+.Em p .
+If a count suffix
+.Em n
+is given, then only the
+.Em n Ns th
+match is replaced.
+The
+.Em r
+suffix causes
+the regular expression of the last search to be used instead of the
+that of the last substitution.
+The
+.Em g
+suffix toggles the global suffix of the last substitution.
+The
+.Em p
+suffix toggles the print suffix of the last substitution
+The current address is set to the last line affected.
+.It (.,.)t(.)
+Copy (i.e., transfer) the addressed lines to after the right-hand
+destination address, which may be the address
+.Em 0
+(zero).
+The current address is set to the last line
+copied.
+.It u
+Undo the last command and restores the current address
+to what it was before the command.
+The global commands
+.Em g ,
+.Em G ,
+.Em v ,
+and
+.Em V .
+are treated as a single command by undo.
+.Em u
+is its own inverse.
+.It (1,$)v/re/command-list
+Apply
+.Ar command-list
+to each of the addressed lines not matching a regular expression
+.Ar re .
+This is similar to the
+.Em g
+command.
+.It (1,$)V/re/
+Interactively edit the addressed lines not matching a regular expression
+.Ar re .
+This is similar to the
+.Em G
+command.
+.It (1,$)w Ar file
+Write the addressed lines to
+.Ar file .
+Any previous contents of
+.Ar file
+is lost without warning.
+If there is no default filename, then the default filename is set to
+.Ar file ,
+otherwise it is unchanged.
+If no filename is specified, then the default
+filename is used.
+The current address is unchanged.
+.It (1,$)wq Ar file
+Write the addressed lines to
+.Ar file ,
+and then executes a
+.Em q
+command.
+.It (1,$)w Ar !command
+Write the addressed lines to the standard input of
+.Ar !command ,
+(see the
+.Em !command
+below).
+The default filename and current address are unchanged.
+.It (1,$)W Ar file
+Append the addressed lines to the end of
+.Ar file .
+This is similar to the
+.Em w
+command, expect that the previous contents of file is not clobbered.
+The current address is unchanged.
+.It x
+Prompt for an encryption key which is used in subsequent reads and
+writes.
+If a newline alone is entered as the key, then encryption is
+turned off.
+Otherwise, echoing is disabled while a key is read.
+Unsupported on Mac OS X.
+.It Pf (.+1)z n
+Scroll
+.Ar n
+lines at a time starting at addressed line.
+If
+.Ar n
+is not specified, then the current window size is used.
+The current address is set to the last line printed.
+.It !command
+Execute
+.Ar command
+via
+.Xr sh 1 .
+If the first character of
+.Ar command
+is
+.Ql \&! ,
+then it is replaced by text of the
+previous
+.Ar !command .
+The
+.Nm
+utility does not process
+.Ar command
+for backslash (\\) escapes.
+However, an unescaped
+.Em %
+is replaced by the default filename.
+When the shell returns from execution, a
+.Ql \&!
+is printed to the standard output.
+The current line is unchanged.
+.It ($)=
+Print the line number of the addressed line.
+.It (.+1)newline
+Print the addressed line, and sets the current address to
+that line.
+.El
+.Sh FILES
+.Bl -tag -width /tmp/ed.* -compact
+.It /tmp/ed.*
+buffer file
+.It ed.hup
+the file to which
+.Nm
+attempts to write the buffer if the terminal hangs up
+.El
+.Sh DIAGNOSTICS
+When an error occurs,
+.Nm
+prints a
+.Ql \&?
+and either returns to command mode
+or exits if its input is from a script.
+An explanation of the last error can be
+printed with the
+.Em h
+(help) command.
+.Pp
+Since the
+.Em g
+(global) command masks any errors from failed searches and substitutions,
+it can be used to perform conditional operations in scripts; e.g.,
+.Pp
+.Sm off
+.Cm g No / Em old Xo
+.No / Cm s
+.No // Em new
+.No /
+.Xc
+.Sm on
+.Pp
+replaces any occurrences of
+.Em old
+with
+.Em new .
+If the
+.Em u
+(undo) command occurs in a global command list, then
+the command list is executed only once.
+.Pp
+If diagnostics are not disabled, attempting to quit
+.Nm
+or edit another file before writing a modified buffer
+results in an error.
+If the command is entered a second time, it succeeds,
+but any changes to the buffer are lost.
+.Sh SEE ALSO
+.Xr sed 1 ,
+.Xr sh 1 ,
+.Xr vi 1 ,
+.Xr regex 3 ,
+.Xr compat 5
+.Pp
+USD:12-13
+.Rs
+.%A B. W. Kernighan
+.%A P. J. Plauger
+.%B Software Tools in Pascal
+.%O Addison-Wesley
+.%D 1981
+.Re
+.Sh LIMITATIONS
+The
+.Nm
+utility processes
+.Ar file
+arguments for backslash escapes, i.e., in a filename,
+any characters preceded by a backslash (\\) are
+interpreted literally.
+.Pp
+If a text (non-binary) file is not terminated by a newline character,
+then
+.Nm
+appends one on reading/writing it.
+In the case of a binary file,
+.Nm
+does not append a newline on reading/writing.
+.Pp
+per line overhead: 4 ints
+.Sh HISTORY
+An
+.Nm
+command appeared in
+Version 1 AT&T UNIX.
+.Sh BUGS
+The
+.Nm
+utility does not recognize multibyte characters.
diff --git a/text_cmds/ed/ed.h b/text_cmds/ed/ed.h
new file mode 100644
index 0000000..f35a001
--- /dev/null
+++ b/text_cmds/ed/ed.h
@@ -0,0 +1,282 @@
+/* ed.h: type and constant definitions for the ed editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ *
+ * @(#)ed.h,v 1.5 1994/02/01 00:34:39 alm Exp
+ * $FreeBSD: src/bin/ed/ed.h,v 1.20 2005/01/10 08:39:22 imp Exp $
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERR (-2)
+#define EMOD (-3)
+#define FATAL (-4)
+
+#define MINBUFSZ 512 /* minimum buffer size - must be > 0 */
+#define SE_MAX 30 /* max subexpressions in a regular expression */
+#ifdef INT_MAX
+# define LINECHARS INT_MAX /* max chars per line */
+#else
+# define LINECHARS MAXINT /* max chars per line */
+#endif
+
+/* gflags */
+#define GLB 001 /* global command */
+#define GPR 002 /* print after command */
+#define GLS 004 /* list after command */
+#define GNP 010 /* enumerate after command */
+#define GSG 020 /* global substitute */
+#define GINT 040 /* interactive cmd: G or V */
+
+typedef regex_t pattern_t;
+
+/* Line node */
+typedef struct line {
+ struct line *q_forw;
+ struct line *q_back;
+ off_t seek; /* address of line in scratch buffer */
+ int len; /* length of line */
+} line_t;
+
+
+typedef struct undo {
+
+/* type of undo nodes */
+#define UADD 0
+#define UDEL 1
+#define UMOV 2
+#define VMOV 3
+
+ int type; /* command type */
+ line_t *h; /* head of list */
+ line_t *t; /* tail of list */
+} undo_t;
+
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1)
+#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1)
+
+/* SPL1: disable some interrupts (requires reliable signals) */
+#define SPL1() mutex++
+
+/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */
+#define SPL0() \
+if (--mutex == 0) { \
+ if (sigflags & (1 << (SIGHUP - 1))) handle_hup(SIGHUP); \
+ if (sigflags & (1 << (SIGINT - 1))) handle_int(SIGINT); \
+}
+
+/* STRTOL: convert a string to long */
+#define STRTOL(i, p) { \
+ if (((i = strtol(p, &p, 10)) == LONG_MIN || i == LONG_MAX) && \
+ errno == ERANGE) { \
+ errmsg = "number out of range"; \
+ i = 0; \
+ return ERR; \
+ } \
+}
+
+#if defined(sun) || defined(NO_REALLOC_NULL)
+/* REALLOC: assure at least a minimum size for buffer b */
+#define REALLOC(b,n,i,err) \
+if ((i) > (n)) { \
+ int ti = (n); \
+ char *ts; \
+ SPL1(); \
+ if ((b) != NULL) { \
+ if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \
+ fprintf(stderr, "%s\n", strerror(errno)); \
+ errmsg = "out of memory"; \
+ SPL0(); \
+ return err; \
+ } \
+ } else { \
+ if ((ts = (char *) malloc(ti += max((i), MINBUFSZ))) == NULL) { \
+ fprintf(stderr, "%s\n", strerror(errno)); \
+ errmsg = "out of memory"; \
+ SPL0(); \
+ return err; \
+ } \
+ } \
+ (n) = ti; \
+ (b) = ts; \
+ SPL0(); \
+}
+#else /* NO_REALLOC_NULL */
+/* REALLOC: assure at least a minimum size for buffer b */
+#define REALLOC(b,n,i,err) \
+if ((i) > (n)) { \
+ int ti = (n); \
+ char *ts; \
+ SPL1(); \
+ if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \
+ fprintf(stderr, "%s\n", strerror(errno)); \
+ errmsg = "out of memory"; \
+ SPL0(); \
+ return err; \
+ } \
+ (n) = ti; \
+ (b) = ts; \
+ SPL0(); \
+}
+#endif /* NO_REALLOC_NULL */
+
+/* REQUE: link pred before succ */
+#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred)
+
+/* INSQUE: insert elem in circular queue after pred */
+#define INSQUE(elem, pred) \
+{ \
+ REQUE((elem), (pred)->q_forw); \
+ REQUE((pred), elem); \
+}
+
+/* REMQUE: remove_lines elem from circular queue */
+#define REMQUE(elem) REQUE((elem)->q_back, (elem)->q_forw);
+
+/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */
+#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n')
+
+/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */
+#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0')
+
+#ifdef ED_DES_INCLUDES
+void des_error(const char *);
+void expand_des_key(char *, char *);
+void set_des_key(DES_cblock *);
+#endif
+
+/* Other DES support stuff */
+void init_des_cipher(void);
+int flush_des_file(FILE *);
+int get_des_char(FILE *);
+int put_des_char(int, FILE *);
+
+/* Local Function Declarations */
+void add_line_node(line_t *);
+int append_lines(long);
+int apply_subst_template(const char *, regmatch_t *, int, int);
+int build_active_list(int);
+int cbc_decode(unsigned char *, FILE *);
+int cbc_encode(unsigned char *, int, FILE *);
+int check_addr_range(long, long);
+void clear_active_list(void);
+void clear_undo_stack(void);
+int close_sbuf(void);
+int copy_lines(long);
+int delete_lines(long, long);
+int display_lines(long, long, int);
+line_t *dup_line_node(line_t *);
+int exec_command(void);
+long exec_global(int, int);
+int extract_addr_range(void);
+char *extract_pattern(int);
+int extract_subst_tail(int *, long *);
+char *extract_subst_template(void);
+int filter_lines(long, long, char *);
+line_t *get_addressed_line_node(long);
+pattern_t *get_compiled_pattern(void);
+char *get_extended_line(int *, int);
+char *get_filename(void);
+int get_keyword(void);
+long get_line_node_addr(line_t *);
+long get_matching_node_addr(pattern_t *, int);
+long get_marked_node_addr(int);
+char *get_sbuf_line(line_t *);
+int get_shell_command(void);
+int get_stream_line(FILE *);
+int get_tty_line(void);
+void handle_hup(int);
+void handle_int(int);
+void handle_winch(int);
+int has_trailing_escape(char *, char *);
+int hex_to_binary(int, int);
+void init_buffers(void);
+int is_legal_filename(char *);
+int join_lines(long, long);
+int mark_line_node(line_t *, int);
+int move_lines(long);
+line_t *next_active_node(void);
+long next_addr(void);
+int open_sbuf(void);
+char *parse_char_class(char *);
+int pop_undo_stack(void);
+undo_t *push_undo_stack(int, long, long);
+const char *put_sbuf_line(const char *);
+int put_stream_line(FILE *, const char *, int);
+int put_tty_line(const char *, int, long, int);
+void quit(int);
+long read_file(char *, long);
+long read_stream(FILE *, long);
+void replace_marks(line_t *, line_t *);
+int search_and_replace(pattern_t *, int, int);
+int set_active_node(line_t *);
+void signal_hup(int);
+void signal_int(int);
+char *strip_escapes(char *);
+int substitute_matching_text(pattern_t *, line_t *, int, int);
+char *translit_text(char *, int, int, int);
+void unmark_line_node(line_t *);
+void unset_active_nodes(line_t *, line_t *);
+long write_file(char *, const char *, long, long);
+long write_stream(FILE *, long, long);
+
+/* global buffers */
+extern char stdinbuf[];
+extern char *ibuf;
+extern char *ibufp;
+extern int ibufsz;
+
+/* global flags */
+extern int isbinary;
+extern int isglobal;
+extern int modified;
+extern int mutex;
+extern int sigflags;
+
+/* global vars */
+extern long addr_last;
+extern long current_addr;
+extern const char *errmsg;
+extern long first_addr;
+extern int lineno;
+extern long second_addr;
+extern long u_addr_last;
+extern long u_current_addr;
+extern int posixly_correct;
diff --git a/text_cmds/ed/glbl.c b/text_cmds/ed/glbl.c
new file mode 100644
index 0000000..9c6799c
--- /dev/null
+++ b/text_cmds/ed/glbl.c
@@ -0,0 +1,218 @@
+/* glob.c: This file contains the global command routines for the ed line
+ editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/glbl.c,v 1.13 2002/06/30 05:13:53 obrien Exp $");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include "ed.h"
+
+
+/* build_active_list: add line matching a pattern to the global-active list */
+int
+build_active_list(int isgcmd)
+{
+ pattern_t *pat;
+ line_t *lp;
+ long n;
+ char *s;
+ char delimiter;
+
+ if ((delimiter = *ibufp) == ' ' || delimiter == '\n') {
+ errmsg = "invalid pattern delimiter";
+ return ERR;
+ } else if ((pat = get_compiled_pattern()) == NULL)
+ return ERR;
+ else if (*ibufp == delimiter)
+ ibufp++;
+ clear_active_list();
+ lp = get_addressed_line_node(first_addr);
+ for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) {
+ if ((s = get_sbuf_line(lp)) == NULL)
+ return ERR;
+ if (isbinary)
+ NUL_TO_NEWLINE(s, lp->len);
+ if (!regexec(pat, s, 0, NULL, 0) == isgcmd &&
+ set_active_node(lp) < 0)
+ return ERR;
+ }
+ return 0;
+}
+
+
+/* exec_global: apply command list in the command buffer to the active
+ lines in a range; return command status */
+long
+exec_global(int interact, int gflag)
+{
+ static char *ocmd = NULL;
+ static int ocmdsz = 0;
+
+ line_t *lp = NULL;
+ int status;
+ int n;
+ char *cmd = NULL;
+
+ if (!interact) {
+ if (posixly_correct) {
+ if ((cmd = get_extended_line(&n, 0)) == NULL)
+ return ERR;
+ } else if (!strcmp(ibufp, "\n"))
+ cmd = "p\n"; /* null cmd-list == `p' */
+ else if ((cmd = get_extended_line(&n, 0)) == NULL)
+ return ERR;
+ }
+ clear_undo_stack();
+ while ((lp = next_active_node()) != NULL) {
+ if ((current_addr = get_line_node_addr(lp)) < 0)
+ return ERR;
+ if (interact) {
+ /* print current_addr; get a command in global syntax */
+ gflag |= GINT;
+ if (display_lines(current_addr, current_addr, gflag) < 0)
+ return ERR;
+ while ((n = get_tty_line()) > 0 &&
+ ibuf[n - 1] != '\n')
+ clearerr(stdin);
+ if (n < 0)
+ return ERR;
+ else if (n == 0) {
+ errmsg = "unexpected end-of-file";
+ return ERR;
+ } else if (n == 1 && !strcmp(ibuf, "\n"))
+ continue;
+ else if (n == 2 && !strcmp(ibuf, "&\n")) {
+ if (cmd == NULL) {
+ errmsg = "no previous command";
+ return ERR;
+ } else cmd = ocmd;
+ } else if ((cmd = get_extended_line(&n, 0)) == NULL)
+ return ERR;
+ else {
+ REALLOC(ocmd, ocmdsz, n + 1, ERR);
+ memcpy(ocmd, cmd, n + 1);
+ cmd = ocmd;
+ }
+
+ }
+ ibufp = cmd;
+ for (; *ibufp;)
+ if ((status = extract_addr_range()) < 0 ||
+ (status = exec_command()) < 0 ||
+ (status > 0 && (status = display_lines(
+ current_addr, current_addr, status)) < 0))
+ return status;
+ }
+ return 0;
+}
+
+
+line_t **active_list; /* list of lines active in a global command */
+long active_last; /* index of last active line in active_list */
+long active_size; /* size of active_list */
+long active_ptr; /* active_list index (non-decreasing) */
+long active_ndx; /* active_list index (modulo active_last) */
+
+/* set_active_node: add a line node to the global-active list */
+int
+set_active_node(line_t *lp)
+{
+ if (active_last + 1 > active_size) {
+ int ti = active_size;
+ line_t **ts;
+ SPL1();
+#if defined(sun) || defined(NO_REALLOC_NULL)
+ if (active_list != NULL) {
+#endif
+ if ((ts = (line_t **) realloc(active_list,
+ (ti += MINBUFSZ) * sizeof(line_t **))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ SPL0();
+ return ERR;
+ }
+#if defined(sun) || defined(NO_REALLOC_NULL)
+ } else {
+ if ((ts = (line_t **) malloc((ti += MINBUFSZ) *
+ sizeof(line_t **))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ SPL0();
+ return ERR;
+ }
+ }
+#endif
+ active_size = ti;
+ active_list = ts;
+ SPL0();
+ }
+ active_list[active_last++] = lp;
+ return 0;
+}
+
+
+/* unset_active_nodes: remove a range of lines from the global-active list */
+void
+unset_active_nodes(line_t *np, line_t *mp)
+{
+ line_t *lp;
+ long i;
+
+ for (lp = np; lp != mp; lp = lp->q_forw)
+ for (i = 0; i < active_last; i++)
+ if (active_list[active_ndx] == lp) {
+ active_list[active_ndx] = NULL;
+ active_ndx = INC_MOD(active_ndx, active_last - 1);
+ break;
+ } else active_ndx = INC_MOD(active_ndx, active_last - 1);
+}
+
+
+/* next_active_node: return the next global-active line node */
+line_t *
+next_active_node(void)
+{
+ while (active_ptr < active_last && active_list[active_ptr] == NULL)
+ active_ptr++;
+ return (active_ptr < active_last) ? active_list[active_ptr++] : NULL;
+}
+
+
+/* clear_active_list: clear the global-active list */
+void
+clear_active_list(void)
+{
+ SPL1();
+ active_size = active_last = active_ptr = active_ndx = 0;
+ free(active_list);
+ active_list = NULL;
+ SPL0();
+}
diff --git a/text_cmds/ed/io.c b/text_cmds/ed/io.c
new file mode 100644
index 0000000..ba8e682
--- /dev/null
+++ b/text_cmds/ed/io.c
@@ -0,0 +1,341 @@
+/* io.c: This file contains the i/o routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/io.c,v 1.14 2003/01/01 18:48:39 schweikh Exp $");
+
+#include "ed.h"
+
+
+extern int scripted;
+
+/* read_file: read a named file/pipe into the buffer; return line count */
+long
+read_file(char *fn, long n)
+{
+ FILE *fp;
+ long size;
+
+
+ fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+ errmsg = "cannot open input file";
+ return ERR;
+ } else if ((size = read_stream(fp, n)) < 0)
+ return ERR;
+ else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) {
+ fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+ errmsg = "cannot close input file";
+ return ERR;
+ }
+ fprintf(stdout, !scripted ? "%lu\n" : "", size);
+ return current_addr - n;
+}
+
+
+extern int des;
+
+char *sbuf; /* file i/o buffer */
+int sbufsz; /* file i/o buffer size */
+int newline_added; /* if set, newline appended to input file */
+
+/* read_stream: read a stream into the editor buffer; return status */
+long
+read_stream(FILE *fp, long n)
+{
+ line_t *lp = get_addressed_line_node(n);
+ undo_t *up = NULL;
+ unsigned long size = 0;
+ int o_newline_added = newline_added;
+ int o_isbinary = isbinary;
+ int appended = (n == addr_last);
+ int len;
+
+ isbinary = newline_added = 0;
+ if (des)
+ init_des_cipher();
+ for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) {
+ SPL1();
+ if (put_sbuf_line(sbuf) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ lp = lp->q_forw;
+ if (up)
+ up->t = lp;
+ else if ((up = push_undo_stack(UADD, current_addr,
+ current_addr)) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ SPL0();
+ }
+ if (len < 0)
+ return ERR;
+ if (appended && size && o_isbinary && o_newline_added)
+ fputs("newline inserted\n", stderr);
+ else if (newline_added && (!appended || (!isbinary && !o_isbinary)))
+ fputs("newline appended\n", stderr);
+ if (isbinary && newline_added && !appended)
+ size += 1;
+ if (!size)
+ newline_added = 1;
+ newline_added = appended ? newline_added : o_newline_added;
+ isbinary = isbinary | o_isbinary;
+ if (des)
+ size += 8 - size % 8; /* adjust DES size */
+ return size;
+}
+
+
+/* get_stream_line: read a line of text from a stream; return line length */
+int
+get_stream_line(FILE *fp)
+{
+ int c;
+ int i = 0;
+
+ while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) &&
+ !ferror(fp))) && c != '\n') {
+ REALLOC(sbuf, sbufsz, i + 1, ERR);
+ if (!(sbuf[i++] = c))
+ isbinary = 1;
+ }
+ REALLOC(sbuf, sbufsz, i + 2, ERR);
+ if (c == '\n')
+ sbuf[i++] = c;
+ else if (ferror(fp)) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot read input file";
+ return ERR;
+ } else if (i) {
+ sbuf[i++] = '\n';
+ newline_added = 1;
+ }
+ sbuf[i] = '\0';
+ return (isbinary && newline_added && i) ? --i : i;
+}
+
+
+/* write_file: write a range of lines to a named file/pipe; return line count */
+long
+write_file(char *fn, const char *mode, long n, long m)
+{
+ FILE *fp;
+ long size;
+
+ fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode);
+ if (fp == NULL) {
+ fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+ errmsg = "cannot open output file";
+ return ERR;
+ } else if ((size = write_stream(fp, n, m)) < 0)
+ return ERR;
+ else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) {
+ fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+ errmsg = "cannot close output file";
+ return ERR;
+ }
+ fprintf(stdout, !scripted ? "%lu\n" : "", size);
+ return n ? m - n + 1 : 0;
+}
+
+
+/* write_stream: write a range of lines to a stream; return status */
+long
+write_stream(FILE *fp, long n, long m)
+{
+ line_t *lp = get_addressed_line_node(n);
+ unsigned long size = 0;
+ char *s;
+ int len;
+
+ if (des)
+ init_des_cipher();
+ for (; n && n <= m; n++, lp = lp->q_forw) {
+ if ((s = get_sbuf_line(lp)) == NULL)
+ return ERR;
+ len = lp->len;
+ if (n != addr_last || !isbinary || !newline_added)
+ s[len++] = '\n';
+ if (put_stream_line(fp, s, len) < 0)
+ return ERR;
+ size += len;
+ }
+ if (des) {
+ flush_des_file(fp); /* flush buffer */
+ size += 8 - size % 8; /* adjust DES size */
+ }
+ return size;
+}
+
+
+/* put_stream_line: write a line of text to a stream; return status */
+int
+put_stream_line(FILE *fp, const char *s, int len)
+{
+ while (len--)
+ if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "cannot write file";
+ return ERR;
+ }
+ return 0;
+}
+
+/* get_extended_line: get an extended line from stdin */
+char *
+get_extended_line(int *sizep, int nonl)
+{
+ static char *cvbuf = NULL; /* buffer */
+ static int cvbufsz = 0; /* buffer size */
+
+ int l, n;
+ char *t = ibufp;
+
+ while (*t++ != '\n')
+ ;
+ if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) {
+ *sizep = l;
+ return ibufp;
+ }
+ *sizep = -1;
+ REALLOC(cvbuf, cvbufsz, l, NULL);
+ memcpy(cvbuf, ibufp, l);
+ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
+ if (nonl) l--; /* strip newline */
+ for (;;) {
+ if ((n = get_tty_line()) < 0)
+ return NULL;
+ else if (n == 0 || ibuf[n - 1] != '\n') {
+ errmsg = "unexpected end-of-file";
+ return NULL;
+ }
+ REALLOC(cvbuf, cvbufsz, l + n, NULL);
+ memcpy(cvbuf + l, ibuf, n);
+ l += n;
+ if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1))
+ break;
+ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
+ if (nonl) l--; /* strip newline */
+ }
+ REALLOC(cvbuf, cvbufsz, l + 1, NULL);
+ cvbuf[l] = '\0';
+ *sizep = l;
+ return cvbuf;
+}
+
+
+/* get_tty_line: read a line of text from stdin; return line length */
+int
+get_tty_line(void)
+{
+ int oi = 0;
+ int i = 0;
+ int c;
+
+ for (;;)
+ switch (c = getchar()) {
+ default:
+ oi = 0;
+ REALLOC(ibuf, ibufsz, i + 2, ERR);
+ if (!(ibuf[i++] = c)) isbinary = 1;
+ if (c != '\n')
+ continue;
+ lineno++;
+ ibuf[i] = '\0';
+ ibufp = ibuf;
+ return i;
+ case EOF:
+ if (ferror(stdin)) {
+ fprintf(stderr, "stdin: %s\n", strerror(errno));
+ errmsg = "cannot read stdin";
+ clearerr(stdin);
+ ibufp = NULL;
+ return ERR;
+ } else {
+ clearerr(stdin);
+ if (i != oi) {
+ oi = i;
+ continue;
+ } else if (i)
+ ibuf[i] = '\0';
+ ibufp = ibuf;
+ return i;
+ }
+ }
+}
+
+
+
+#define ESCAPES "\a\b\f\n\r\t\v\\"
+#define ESCCHARS "abfnrtv\\"
+
+extern int rows;
+extern int cols;
+
+/* put_tty_line: print text to stdout */
+int
+put_tty_line(const char *s, int l, long n, int gflag)
+{
+ int col = 0;
+ char *cp;
+
+ if ((gflag & GNP) && !(gflag & GINT)) {
+ printf("%ld\t", n);
+ col = 8;
+ }
+ for (; l--; s++) {
+ if ((gflag & GLS) && ++col > cols) {
+ fputs("\\\n", stdout);
+ col = 1;
+ }
+ if (gflag & GLS) {
+ if (31 < *s && *s < 127 && *s != '\\')
+ putchar(*s);
+ else {
+ putchar('\\');
+ col++;
+ if (*s && (cp = strchr(ESCAPES, *s)) != NULL)
+ putchar(ESCCHARS[cp - ESCAPES]);
+ else {
+ putchar((((unsigned char) *s & 0300) >> 6) + '0');
+ putchar((((unsigned char) *s & 070) >> 3) + '0');
+ putchar(((unsigned char) *s & 07) + '0');
+ col += 2;
+ }
+ }
+
+ } else
+ putchar(*s);
+ }
+ if (posixly_correct && (gflag & GLS) && !(gflag & GINT))
+ putchar('$');
+ putchar('\n');
+ return 0;
+}
diff --git a/text_cmds/ed/main.c b/text_cmds/ed/main.c
new file mode 100644
index 0000000..0747c54
--- /dev/null
+++ b/text_cmds/ed/main.c
@@ -0,0 +1,1455 @@
+/* main.c: This file contains the main control and user-interface routines
+ for the ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static const char copyright[] =
+"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\
+ All rights reserved.\n";
+#endif
+#endif /* not lint */
+
+/*
+ * CREDITS
+ *
+ * This program is based on the editor algorithm described in
+ * Brian W. Kernighan and P. J. Plauger's book "Software Tools
+ * in Pascal," Addison-Wesley, 1981.
+ *
+ * The buffering algorithm is attributed to Rodney Ruddock of
+ * the University of Guelph, Guelph, Ontario.
+ *
+ * The cbc.c encryption code is adapted from
+ * the bdes program by Matt Bishop of Dartmouth College,
+ * Hanover, NH.
+ *
+ */
+
+#include <sys/types.h>
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <locale.h>
+#include <pwd.h>
+#include <setjmp.h>
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "ed.h"
+
+
+#ifdef _POSIX_SOURCE
+sigjmp_buf env;
+#else
+jmp_buf env;
+#endif
+
+/* static buffers */
+char stdinbuf[1]; /* stdin buffer */
+char *shcmd; /* shell command buffer */
+int shcmdsz; /* shell command buffer size */
+int shcmdi; /* shell command buffer index */
+char *ibuf; /* ed command-line buffer */
+int ibufsz; /* ed command-line buffer size */
+char *ibufp; /* pointer to ed command-line buffer */
+
+/* global flags */
+int des = 0; /* if set, use crypt(3) for i/o */
+int garrulous = 0; /* if set, print all error messages */
+int isbinary; /* if set, buffer contains ASCII NULs */
+int isglobal; /* if set, doing a global command */
+int modified; /* if set, buffer modified since last write */
+int mutex = 0; /* if set, signals set "sigflags" */
+int red = 0; /* if set, restrict shell/directory access */
+int scripted = 0; /* if set, suppress diagnostics */
+int sigflags = 0; /* if set, signals received while mutex set */
+int sigactive = 0; /* if set, signal handlers are enabled */
+int posixly_correct = 0; /* if set, POSIX behavior as per */
+/* http://www.opengroup.org/onlinepubs/009695399/utilities/ed.html */
+
+char old_filename[PATH_MAX + 1] = ""; /* default filename */
+long current_addr; /* current address in editor buffer */
+long addr_last; /* last address in editor buffer */
+int lineno; /* script line number */
+const char *prompt; /* command-line prompt */
+const char *dps = "*"; /* default command-line prompt */
+
+const char usage[] = "usage: %s [-] [-sx] [-p string] [file]\n";
+
+/* ed: line editor */
+int
+main(int argc, char *argv[])
+{
+ int c, n;
+ long status = 0;
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &argc;
+ (void) &argv;
+#endif
+
+ (void)setlocale(LC_ALL, "");
+
+ posixly_correct = COMPAT_MODE("bin/ed", "Unix2003");
+
+ red = argv[0]!=NULL && (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
+top:
+ while ((c = getopt(argc, argv, "p:sx")) != -1)
+ switch(c) {
+ case 'p': /* set prompt */
+ prompt = optarg;
+ break;
+ case 's': /* run script */
+ scripted = 1;
+ break;
+ case 'x': /* use crypt */
+#ifdef DES
+ des = get_keyword();
+#else
+ fprintf(stderr, "crypt unavailable\n?\n");
+#endif
+ break;
+
+ default:
+ fprintf(stderr, usage, red ? "red" : "ed");
+ exit(1);
+ }
+ argv += optind;
+ argc -= optind;
+ if (argc>0 && *argv && !strcmp(*argv, "-")) {
+ scripted = 1;
+ if (argc > 1) {
+ optind = 1;
+ goto top;
+ }
+ argv++;
+ argc--;
+ }
+ /* assert: reliable signals! */
+#ifdef SIGWINCH
+ handle_winch(SIGWINCH);
+ if (isatty(0)) signal(SIGWINCH, handle_winch);
+#endif
+ signal(SIGHUP, signal_hup);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, signal_int);
+#ifdef _POSIX_SOURCE
+ if ((status = sigsetjmp(env, 1)))
+#else
+ if ((status = setjmp(env)))
+#endif
+ {
+ fputs("\n?\n", stderr);
+ errmsg = "interrupt";
+ } else {
+ init_buffers();
+ sigactive = 1; /* enable signal handlers */
+ if (argc>0 && *argv && is_legal_filename(*argv)) {
+ if (read_file(*argv, 0) < 0 && !isatty(0))
+ quit(2);
+ else if (**argv != '!') {
+ if (strlen(*argv) < sizeof(old_filename)) {
+ strcpy(old_filename, *argv);
+ } else {
+ fprintf(stderr, "%s: filename too long\n", *argv);
+ quit(2);
+ }
+ }
+ } else if (argc>0) {
+ fputs("?\n", stdout);
+ if (**argv == '\0')
+ errmsg = "invalid filename";
+ if (!isatty(0))
+ quit(2);
+ }
+ }
+ for (;;) {
+ if (status < 0 && garrulous)
+ fprintf(stderr, "%s\n", errmsg);
+ if (prompt) {
+ printf("%s", prompt);
+ fflush(stdout);
+ }
+ if ((n = get_tty_line()) < 0) {
+ status = ERR;
+ continue;
+ } else if (n == 0) {
+ if (modified && !scripted) {
+ fputs("?\n", stderr);
+ errmsg = "warning: file modified";
+ if (!isatty(0)) {
+ fprintf(stderr, garrulous ?
+ "script, line %d: %s\n" :
+ "", lineno, errmsg);
+ quit(2);
+ }
+ clearerr(stdin);
+ modified = 0;
+ status = EMOD;
+ continue;
+ } else
+ quit(0);
+ } else if (ibuf[n - 1] != '\n') {
+ /* discard line */
+ errmsg = "unexpected end-of-file";
+ clearerr(stdin);
+ status = ERR;
+ continue;
+ }
+ isglobal = 0;
+ if ((status = extract_addr_range()) >= 0 &&
+ (status = exec_command()) >= 0)
+ if (!status ||
+ (status = display_lines(current_addr, current_addr,
+ status)) >= 0)
+ continue;
+ switch (status) {
+ case EOF:
+ quit(0);
+ case EMOD:
+ modified = 0;
+ fputs("?\n", stderr); /* give warning */
+ errmsg = "warning: file modified";
+ if (!isatty(0)) {
+ fprintf(stderr, garrulous ?
+ "script, line %d: %s\n" :
+ "", lineno, errmsg);
+ quit(2);
+ }
+ break;
+ case FATAL:
+ if (!isatty(0))
+ fprintf(stderr, garrulous ?
+ "script, line %d: %s\n" : "",
+ lineno, errmsg);
+ else
+ fprintf(stderr, garrulous ? "%s\n" : "",
+ errmsg);
+ quit(3);
+ default:
+ fputs("?\n", stdout);
+ if (!isatty(0)) {
+ fprintf(stderr, garrulous ?
+ "script, line %d: %s\n" : "",
+ lineno, errmsg);
+ quit(2);
+ }
+ break;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+long first_addr, second_addr, addr_cnt;
+
+/* extract_addr_range: get line addresses from the command buffer until an
+ illegal address is seen; return status */
+int
+extract_addr_range(void)
+{
+ long addr;
+
+ addr_cnt = 0;
+ first_addr = second_addr = current_addr;
+ while ((addr = next_addr()) >= 0) {
+ addr_cnt++;
+ first_addr = second_addr;
+ second_addr = addr;
+ if (*ibufp != ',' && *ibufp != ';')
+ break;
+ else if (*ibufp++ == ';')
+ current_addr = addr;
+ }
+ if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr)
+ first_addr = second_addr;
+ return (addr == ERR) ? ERR : 0;
+}
+
+
+#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++
+
+#define MUST_BE_FIRST() do { \
+ if (!first) { \
+ errmsg = "invalid address"; \
+ return ERR; \
+ } \
+} while (0);
+
+/* next_addr: return the next line address in the command buffer */
+long
+next_addr(void)
+{
+ const char *hd;
+ long addr = current_addr;
+ long n;
+ int first = 1;
+ int c;
+ int add = 0;
+
+ SKIP_BLANKS();
+ for (hd = ibufp;; first = 0)
+ switch (c = *ibufp) {
+ case '+':
+ case '\t':
+ case ' ':
+ case '-':
+ case '^':
+ ibufp++;
+ SKIP_BLANKS();
+ if (isdigit((unsigned char)*ibufp)) {
+ STRTOL(n, ibufp);
+ addr += (c == '-' || c == '^') ? -n : n;
+ } else if (!isspace((unsigned char)c))
+ addr += (c == '-' || c == '^') ? -1 : 1;
+ break;
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ STRTOL(n, ibufp);
+ if (first || !add) /* don't add for ,\d or ;\d */
+ addr = n;
+ else
+ addr += n;
+ break;
+ case '.':
+ case '$':
+ MUST_BE_FIRST();
+ ibufp++;
+ addr = (c == '.') ? current_addr : addr_last;
+ add++;
+ break;
+ case '/':
+ case '?':
+ MUST_BE_FIRST();
+ if ((addr = get_matching_node_addr(
+ get_compiled_pattern(), c == '/')) < 0)
+ return ERR;
+ else if (c == *ibufp)
+ ibufp++;
+ add++;
+ break;
+ case '\'':
+ MUST_BE_FIRST();
+ ibufp++;
+ if ((addr = get_marked_node_addr(*ibufp++)) < 0)
+ return ERR;
+ add++;
+ break;
+ case '%':
+ case ',':
+ case ';':
+ if (first) {
+ ibufp++;
+ addr_cnt++;
+ second_addr = (c == ';') ? current_addr : 1;
+ addr = addr_last;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (ibufp == hd)
+ return EOF;
+ else if (addr < 0 || addr_last < addr) {
+ errmsg = "invalid address";
+ return ERR;
+ } else
+ return addr;
+ }
+ /* NOTREACHED */
+}
+
+
+/* GET_THIRD_ADDR: get a legal address from the command buffer */
+#define GET_THIRD_ADDR(addr) \
+{ \
+ long ol1, ol2; \
+\
+ ol1 = first_addr, ol2 = second_addr; \
+ if (extract_addr_range() < 0) \
+ return ERR; \
+ else if (!posixly_correct && addr_cnt == 0) { \
+ errmsg = "destination expected"; \
+ return ERR; \
+ } else if (second_addr < 0 || addr_last < second_addr) { \
+ errmsg = "invalid address"; \
+ return ERR; \
+ } \
+ addr = second_addr; \
+ first_addr = ol1, second_addr = ol2; \
+}
+
+
+/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
+#define GET_COMMAND_SUFFIX() { \
+ int done = 0; \
+ do { \
+ switch(*ibufp) { \
+ case 'p': \
+ gflag |= GPR, ibufp++; \
+ break; \
+ case 'l': \
+ gflag |= GLS, ibufp++; \
+ break; \
+ case 'n': \
+ gflag |= GNP, ibufp++; \
+ break; \
+ default: \
+ done++; \
+ } \
+ } while (!done); \
+ if (*ibufp++ != '\n') { \
+ errmsg = "invalid command suffix"; \
+ return ERR; \
+ } \
+}
+
+
+/* sflags */
+#define SGG 001 /* complement previous global substitute suffix */
+#define SGP 002 /* complement previous print suffix */
+#define SGR 004 /* use last regex instead of last pat */
+#define SGF 010 /* repeat last substitution */
+
+int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */
+
+long rows = 22; /* scroll length: ws_row - 2 */
+
+/* exec_command: execute the next command in command buffer; return print
+ request, if any */
+int
+exec_command(void)
+{
+ static pattern_t *pat = NULL;
+ static int sgflag = 0;
+ static long sgnum = 0;
+
+ pattern_t *tpat;
+ char *fnp;
+ int gflag = 0;
+ int sflags = 0;
+ long addr = 0;
+ int n = 0;
+ int c;
+
+ SKIP_BLANKS();
+ switch(c = *ibufp++) {
+ case 'a':
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (append_lines(second_addr) < 0)
+ return ERR;
+ break;
+ case 'c':
+ if (first_addr == 0)
+ first_addr = 1;
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (delete_lines(first_addr, second_addr) < 0 ||
+ append_lines(current_addr) < 0)
+ return ERR;
+ if (current_addr == 0 && addr_last != 0)
+ current_addr = 1;
+ break;
+ case 'd':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (delete_lines(first_addr, second_addr) < 0)
+ return ERR;
+ else if ((addr = INC_MOD(current_addr, addr_last)) != 0)
+ current_addr = addr;
+ if (current_addr == 0 && addr_last != 0)
+ current_addr = 1;
+ break;
+ case 'e':
+ if (modified && !scripted)
+ return EMOD;
+ /* FALLTHROUGH */
+ case 'E':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ } else if (!isspace((unsigned char)*ibufp)) {
+ errmsg = "unexpected command suffix";
+ return ERR;
+ } else if ((fnp = get_filename()) == NULL)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (delete_lines(1, addr_last) < 0)
+ return ERR;
+ clear_undo_stack();
+ if (close_sbuf() < 0)
+ return ERR;
+ else if (open_sbuf() < 0)
+ return FATAL;
+ if (*fnp && *fnp != '!') {
+ if (strlen(fnp) < sizeof(old_filename)) {
+ strcpy(old_filename, fnp);
+ } else {
+ fprintf(stderr, "%s: filename too long\n", fnp);
+ quit(2);
+ }
+ }
+
+ if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') {
+ errmsg = "no current filename";
+ return ERR;
+ }
+ if (read_file(*fnp ? fnp : old_filename, 0) < 0)
+ return ERR;
+ clear_undo_stack();
+ modified = 0;
+ u_current_addr = u_addr_last = -1;
+ break;
+ case 'f':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ } else if (!isspace((unsigned char)*ibufp)) {
+ errmsg = "unexpected command suffix";
+ return ERR;
+ } else if ((fnp = get_filename()) == NULL)
+ return ERR;
+ else if (*fnp == '!') {
+ errmsg = "invalid redirection";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if (*fnp) {
+ if (strlen(fnp) < sizeof(old_filename)) {
+ strcpy(old_filename, fnp);
+ } else {
+ fprintf(stderr, "%s: filename too long\n", fnp);
+ quit(2);
+ }
+ }
+ printf("%s\n", strip_escapes(old_filename));
+ break;
+ case 'g':
+ case 'v':
+ case 'G':
+ case 'V':
+ if (isglobal) {
+ errmsg = "cannot nest global commands";
+ return ERR;
+ } else if (check_addr_range(1, addr_last) < 0)
+ return ERR;
+ else if (build_active_list(c == 'g' || c == 'G') < 0)
+ return ERR;
+ else if ((n = (c == 'G' || c == 'V')))
+ GET_COMMAND_SUFFIX();
+ isglobal++;
+ n = exec_global(n, gflag);
+ if (n == EOF)
+ return EOF;
+ else if (n < 0)
+ return ERR;
+ break;
+ case 'h':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if (*errmsg) fprintf(stderr, "%s\n", errmsg);
+ break;
+ case 'H':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if ((garrulous = 1 - garrulous) && *errmsg)
+ fprintf(stderr, "%s\n", errmsg);
+ break;
+ case 'i':
+ if (second_addr == 0) {
+ second_addr = 1;
+ }
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (append_lines(second_addr - 1) < 0)
+ return ERR;
+ if (u_addr_last == addr_last) /* nothing inserted */
+ current_addr = second_addr;
+ break;
+ case 'j':
+ if (check_addr_range(current_addr, current_addr + 1) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (first_addr != second_addr &&
+ join_lines(first_addr, second_addr) < 0)
+ return ERR;
+ break;
+ case 'k':
+ c = *ibufp++;
+ if (second_addr == 0) {
+ errmsg = "invalid address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if (mark_line_node(get_addressed_line_node(second_addr), c) < 0)
+ return ERR;
+ break;
+ case 'l':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (display_lines(first_addr, second_addr, gflag | GLS) < 0)
+ return ERR;
+ gflag = 0;
+ break;
+ case 'm':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_THIRD_ADDR(addr);
+ if (first_addr <= addr && addr < second_addr) {
+ errmsg = "invalid destination";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (move_lines(addr) < 0)
+ return ERR;
+ break;
+ case 'n':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (display_lines(first_addr, second_addr, gflag | GNP) < 0)
+ return ERR;
+ gflag = 0;
+ break;
+ case 'p':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (display_lines(first_addr, second_addr, gflag | GPR) < 0)
+ return ERR;
+ gflag = 0;
+ break;
+ case 'P':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ prompt = prompt ? NULL : optarg ? optarg : dps;
+ break;
+ case 'q':
+ case 'Q':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ gflag = (modified && !scripted && c == 'q') ? EMOD : EOF;
+ break;
+ case 'r':
+ if (!isspace((unsigned char)*ibufp)) {
+ errmsg = "unexpected command suffix";
+ return ERR;
+ } else if (addr_cnt == 0)
+ second_addr = addr_last;
+ if ((fnp = get_filename()) == NULL)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (*old_filename == '\0' && *fnp != '!') {
+ if (strlen(fnp) < sizeof(old_filename)) {
+ strcpy(old_filename, fnp);
+ } else {
+ fprintf(stderr, "%s: filename too long\n", fnp);
+ quit(2);
+ }
+ }
+ if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') {
+ errmsg = "no current filename";
+ return ERR;
+ }
+ if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0)
+ return ERR;
+ else if (addr && addr != addr_last)
+ modified = 1;
+ break;
+ case 's':
+ /*
+ * POSIX requires ed to process srabcr123r as
+ * replace abc with 123 delimiter='r'
+ */
+ if (!posixly_correct) do {
+ switch(*ibufp) {
+ case '\n':
+ sflags |=SGF;
+ break;
+ case 'g':
+ sflags |= SGG;
+ ibufp++;
+ break;
+ case 'p':
+ sflags |= SGP;
+ ibufp++;
+ break;
+ case 'r':
+ sflags |= SGR;
+ ibufp++;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ STRTOL(sgnum, ibufp);
+ sflags |= SGF;
+ sgflag &= ~GSG; /* override GSG */
+ break;
+ default:
+ if (sflags) {
+ errmsg = "invalid command suffix";
+ return ERR;
+ }
+ }
+ } while (sflags && *ibufp != '\n');
+ if (sflags && !pat) {
+ errmsg = "no previous substitution";
+ return ERR;
+ } else if (sflags & SGG)
+ sgnum = 0; /* override numeric arg */
+ if (*ibufp != '\n' && *(ibufp + 1) == '\n') {
+ errmsg = "invalid pattern delimiter";
+ return ERR;
+ }
+ tpat = pat;
+ SPL1();
+ if ((!sflags || (sflags & SGR)) &&
+ (tpat = get_compiled_pattern()) == NULL) {
+ SPL0();
+ return ERR;
+ } else if (tpat != pat) {
+ if (pat) {
+ regfree(pat);
+ free(pat);
+ }
+ pat = tpat;
+ patlock = 1; /* reserve pattern */
+ }
+ SPL0();
+ if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0)
+ return ERR;
+ else if (isglobal)
+ sgflag |= GLB;
+ else
+ sgflag &= ~GLB;
+ if (sflags & SGG)
+ sgflag ^= GSG;
+ if (sflags & SGP)
+ sgflag ^= GPR, sgflag &= ~(GLS | GNP);
+ do {
+ switch(*ibufp) {
+ case 'p':
+ sgflag |= GPR, ibufp++;
+ break;
+ case 'l':
+ sgflag |= GLS, ibufp++;
+ break;
+ case 'n':
+ sgflag |= GNP, ibufp++;
+ break;
+ default:
+ n++;
+ }
+ } while (!n);
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (search_and_replace(pat, sgflag, sgnum) < 0)
+ return ERR;
+ break;
+ case 't':
+ if (check_addr_range(current_addr, current_addr) < 0)
+ return ERR;
+ GET_THIRD_ADDR(addr);
+ GET_COMMAND_SUFFIX();
+ if (!isglobal) clear_undo_stack();
+ if (copy_lines(addr) < 0)
+ return ERR;
+ break;
+ case 'u':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+ if (pop_undo_stack() < 0)
+ return ERR;
+ break;
+ case 'w':
+ case 'W':
+ if ((n = *ibufp) == 'q' || n == 'Q') {
+ gflag = EOF;
+ ibufp++;
+ }
+ if (!isspace((unsigned char)*ibufp)) {
+ errmsg = "unexpected command suffix";
+ return ERR;
+ } else if ((fnp = get_filename()) == NULL)
+ return ERR;
+ if (addr_cnt == 0 && !addr_last)
+ first_addr = second_addr = 0;
+ else if (check_addr_range(1, addr_last) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (*old_filename == '\0' && *fnp != '!') {
+ if (strlen(fnp) < sizeof(old_filename)) {
+ strcpy(old_filename, fnp);
+ } else {
+ fprintf(stderr, "%s: filename too long\n", fnp);
+ quit(2);
+ }
+ }
+ if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') {
+ errmsg = "no current filename";
+ return ERR;
+ }
+ if ((addr = write_file(*fnp ? fnp : old_filename,
+ (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
+ return ERR;
+ else if (addr == addr_last)
+ modified = 0;
+ else if (modified && !scripted && n == 'q')
+ gflag = EMOD;
+ break;
+ case 'x':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ }
+ GET_COMMAND_SUFFIX();
+#ifdef DES
+ des = get_keyword();
+ break;
+#else
+ errmsg = "crypt unavailable";
+ return ERR;
+#endif
+ case 'z':
+ if (check_addr_range(first_addr = 1, current_addr + (posixly_correct ? !isglobal : 1)) < 0)
+ return ERR;
+ else if ('0' < *ibufp && *ibufp <= '9')
+ STRTOL(rows, ibufp);
+ GET_COMMAND_SUFFIX();
+ if (display_lines(second_addr, min(addr_last,
+ second_addr + rows), gflag) < 0)
+ return ERR;
+ gflag = 0;
+ break;
+ case '=':
+ GET_COMMAND_SUFFIX();
+ printf("%ld\n", addr_cnt ? second_addr : addr_last);
+ break;
+ case '!':
+ if (addr_cnt > 0) {
+ errmsg = "unexpected address";
+ return ERR;
+ } else if ((sflags = get_shell_command()) < 0)
+ return ERR;
+ GET_COMMAND_SUFFIX();
+ if (sflags) printf("%s\n", shcmd + 1);
+ fflush(stdout);
+ system(shcmd + 1);
+ if (!scripted) printf("!\n");
+ break;
+ case '\n':
+ if (check_addr_range(first_addr = 1, current_addr + (posixly_correct ? !isglobal : 1)) < 0
+ || display_lines(second_addr, second_addr, 0) < 0)
+ return ERR;
+ break;
+ default:
+ errmsg = "unknown command";
+ return ERR;
+ }
+ return gflag;
+}
+
+
+/* check_addr_range: return status of address range check */
+int
+check_addr_range(long n, long m)
+{
+ if (addr_cnt == 0) {
+ first_addr = n;
+ second_addr = m;
+ }
+ if (first_addr > second_addr || 1 > first_addr ||
+ second_addr > addr_last) {
+ errmsg = "invalid address";
+ return ERR;
+ }
+ return 0;
+}
+
+
+/* get_matching_node_addr: return the address of the next line matching a
+ pattern in a given direction. wrap around begin/end of editor buffer if
+ necessary */
+long
+get_matching_node_addr(pattern_t *pat, int dir)
+{
+ char *s;
+ long n = current_addr;
+ line_t *lp;
+
+ if (!pat) return ERR;
+ do {
+ if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) {
+ lp = get_addressed_line_node(n);
+ if ((s = get_sbuf_line(lp)) == NULL)
+ return ERR;
+ if (isbinary)
+ NUL_TO_NEWLINE(s, lp->len);
+ if (!regexec(pat, s, 0, NULL, 0))
+ return n;
+ }
+ } while (n != current_addr);
+ errmsg = "no match";
+ return ERR;
+}
+
+
+/* get_filename: return pointer to copy of filename in the command buffer */
+char *
+get_filename(void)
+{
+ static char *file = NULL;
+ static int filesz = 0;
+
+ int n;
+
+ if (*ibufp != '\n') {
+ SKIP_BLANKS();
+ if (*ibufp == '\n') {
+ errmsg = "invalid filename";
+ return NULL;
+ } else if ((ibufp = get_extended_line(&n, 1)) == NULL)
+ return NULL;
+ else if (*ibufp == '!') {
+ ibufp++;
+ if ((n = get_shell_command()) < 0)
+ return NULL;
+ if (n)
+ printf("%s\n", shcmd + 1);
+ return shcmd;
+ } else if (n > PATH_MAX) {
+ errmsg = "filename too long";
+ return NULL;
+ }
+ }
+ else if (posixly_correct && *old_filename == '\0') {
+ errmsg = "no current filename";
+ return NULL;
+ }
+ REALLOC(file, filesz, PATH_MAX + 1, NULL);
+ for (n = 0; *ibufp != '\n';)
+ file[n++] = *ibufp++;
+ file[n] = '\0';
+ return is_legal_filename(file) ? file : NULL;
+}
+
+
+/* get_shell_command: read a shell command from stdin; return substitution
+ status */
+int
+get_shell_command(void)
+{
+ static char *buf = NULL;
+ static int n = 0;
+
+ char *s; /* substitution char pointer */
+ int i = 0;
+ int j = 0;
+
+ if (red) {
+ errmsg = "shell access restricted";
+ return ERR;
+ } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL)
+ return ERR;
+ REALLOC(buf, n, j + 1, ERR);
+ buf[i++] = '!'; /* prefix command w/ bang */
+ while (*ibufp != '\n')
+ switch (*ibufp) {
+ default:
+ REALLOC(buf, n, i + 2, ERR);
+ buf[i++] = *ibufp;
+ if (*ibufp++ == '\\')
+ buf[i++] = *ibufp++;
+ break;
+ case '!':
+ if (s != ibufp) {
+ REALLOC(buf, n, i + 1, ERR);
+ buf[i++] = *ibufp++;
+ }
+ else if (shcmd == NULL || (!posixly_correct && *(shcmd + 1) == '\0'))
+ {
+ errmsg = "no previous command";
+ return ERR;
+ } else {
+ REALLOC(buf, n, i + shcmdi, ERR);
+ for (s = shcmd + 1; s < shcmd + shcmdi;)
+ buf[i++] = *s++;
+ s = ibufp++;
+ }
+ break;
+ case '%':
+ if (*old_filename == '\0') {
+ errmsg = "no current filename";
+ return ERR;
+ }
+ j = strlen(s = strip_escapes(old_filename));
+ REALLOC(buf, n, i + j, ERR);
+ while (j--)
+ buf[i++] = *s++;
+ s = ibufp++;
+ break;
+ }
+ REALLOC(shcmd, shcmdsz, i + 1, ERR);
+ memcpy(shcmd, buf, i);
+ shcmd[shcmdi = i] = '\0';
+ return *s == '!' || *s == '%';
+}
+
+
+/* append_lines: insert text from stdin to after line n; stop when either a
+ single period is read or EOF; return status */
+int
+append_lines(long n)
+{
+ int l;
+ const char *lp = ibuf;
+ const char *eot;
+ undo_t *up = NULL;
+
+ for (current_addr = n;;) {
+ if (!isglobal) {
+ if ((l = get_tty_line()) < 0)
+ return ERR;
+ else if (l == 0 || ibuf[l - 1] != '\n') {
+ clearerr(stdin);
+ return l ? EOF : 0;
+ }
+ lp = ibuf;
+ } else if (*(lp = ibufp) == '\0')
+ return 0;
+ else {
+ while (*ibufp++ != '\n')
+ ;
+ l = ibufp - lp;
+ }
+ if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
+ return 0;
+ }
+ eot = lp + l;
+ SPL1();
+ do {
+ if ((lp = put_sbuf_line(lp)) == NULL) {
+ SPL0();
+ return ERR;
+ } else if (up)
+ up->t = get_addressed_line_node(current_addr);
+ else if ((up = push_undo_stack(UADD, current_addr,
+ current_addr)) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ } while (lp != eot);
+ modified = 1;
+ SPL0();
+ }
+ /* NOTREACHED */
+}
+
+
+/* join_lines: replace a range of lines with the joined text of those lines */
+int
+join_lines(long from, long to)
+{
+ static char *buf = NULL;
+ static int n;
+
+ char *s;
+ int size = 0;
+ line_t *bp, *ep;
+
+ ep = get_addressed_line_node(INC_MOD(to, addr_last));
+ bp = get_addressed_line_node(from);
+ for (; bp != ep; bp = bp->q_forw) {
+ if ((s = get_sbuf_line(bp)) == NULL)
+ return ERR;
+ REALLOC(buf, n, size + bp->len, ERR);
+ memcpy(buf + size, s, bp->len);
+ size += bp->len;
+ }
+ REALLOC(buf, n, size + 2, ERR);
+ memcpy(buf + size, "\n", 2);
+ if (delete_lines(from, to) < 0)
+ return ERR;
+ current_addr = from - 1;
+ SPL1();
+ if (put_sbuf_line(buf) == NULL ||
+ push_undo_stack(UADD, current_addr, current_addr) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ modified = 1;
+ if (current_addr == 0)
+ current_addr = 1;
+ SPL0();
+ return 0;
+}
+
+
+/* move_lines: move a range of lines */
+int
+move_lines(long addr)
+{
+ line_t *b1, *a1, *b2, *a2;
+ long n = INC_MOD(second_addr, addr_last);
+ long p = first_addr - 1;
+ int done = (addr == first_addr - 1 || addr == second_addr);
+
+ SPL1();
+ if (done) {
+ a2 = get_addressed_line_node(n);
+ b2 = get_addressed_line_node(p);
+ current_addr = second_addr;
+ } else if (push_undo_stack(UMOV, p, n) == NULL ||
+ push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) {
+ SPL0();
+ return ERR;
+ } else {
+ a1 = get_addressed_line_node(n);
+ if (addr < first_addr) {
+ b1 = get_addressed_line_node(p);
+ b2 = get_addressed_line_node(addr);
+ /* this get_addressed_line_node last! */
+ } else {
+ b2 = get_addressed_line_node(addr);
+ b1 = get_addressed_line_node(p);
+ /* this get_addressed_line_node last! */
+ }
+ a2 = b2->q_forw;
+ REQUE(b2, b1->q_forw);
+ REQUE(a1->q_back, a2);
+ REQUE(b1, a1);
+ current_addr = addr + ((addr < first_addr) ?
+ second_addr - first_addr + 1 : 0);
+ }
+ if (isglobal)
+ unset_active_nodes(b2->q_forw, a2);
+ modified = 1;
+ SPL0();
+ return 0;
+}
+
+
+/* copy_lines: copy a range of lines; return status */
+int
+copy_lines(long addr)
+{
+ line_t *lp, *np = get_addressed_line_node(first_addr);
+ undo_t *up = NULL;
+ long n = second_addr - first_addr + 1;
+ long m = 0;
+
+ current_addr = addr;
+ if (first_addr <= addr && addr < second_addr) {
+ n = addr - first_addr + 1;
+ m = second_addr - addr;
+ }
+ for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1))
+ for (; n-- > 0; np = np->q_forw) {
+ SPL1();
+ if ((lp = dup_line_node(np)) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ add_line_node(lp);
+ if (up)
+ up->t = lp;
+ else if ((up = push_undo_stack(UADD, current_addr,
+ current_addr)) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ modified = 1;
+ SPL0();
+ }
+ return 0;
+}
+
+
+/* delete_lines: delete a range of lines */
+int
+delete_lines(long from, long to)
+{
+ line_t *n, *p;
+
+ SPL1();
+ if (push_undo_stack(UDEL, from, to) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ n = get_addressed_line_node(INC_MOD(to, addr_last));
+ p = get_addressed_line_node(from - 1);
+ /* this get_addressed_line_node last! */
+ if (isglobal)
+ unset_active_nodes(p->q_forw, n);
+ REQUE(p, n);
+ addr_last -= to - from + 1;
+ current_addr = from - 1;
+ modified = 1;
+ SPL0();
+ return 0;
+}
+
+
+/* display_lines: print a range of lines to stdout */
+int
+display_lines(long from, long to, int gflag)
+{
+ line_t *bp;
+ line_t *ep;
+ char *s;
+
+ if (!from) {
+ errmsg = "invalid address";
+ return ERR;
+ }
+ ep = get_addressed_line_node(INC_MOD(to, addr_last));
+ bp = get_addressed_line_node(from);
+ for (; bp != ep; bp = bp->q_forw) {
+ if ((s = get_sbuf_line(bp)) == NULL)
+ return ERR;
+ if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0)
+ return ERR;
+ }
+ return 0;
+}
+
+
+#define MAXMARK 26 /* max number of marks */
+
+line_t *mark[MAXMARK]; /* line markers */
+int markno; /* line marker count */
+
+/* mark_line_node: set a line node mark */
+int
+mark_line_node(line_t *lp, int n)
+{
+ if (!islower((unsigned char)n)) {
+ errmsg = "invalid mark character";
+ return ERR;
+ } else if (mark[n - 'a'] == NULL)
+ markno++;
+ mark[n - 'a'] = lp;
+ return 0;
+}
+
+
+/* get_marked_node_addr: return address of a marked line */
+long
+get_marked_node_addr(int n)
+{
+ if (!islower((unsigned char)n)) {
+ errmsg = "invalid mark character";
+ return ERR;
+ }
+ return get_line_node_addr(mark[n - 'a']);
+}
+
+/* Used by search and replace */
+void
+replace_marks(line_t *old, line_t *new)
+{
+ int i;
+ for (i=0; markno && i<MAXMARK; i++)
+ if (mark[i] == old)
+ mark[i] = new;
+}
+
+
+/* unmark_line_node: clear line node mark */
+void
+unmark_line_node(line_t *lp)
+{
+ int i;
+
+ for (i = 0; markno && i < MAXMARK; i++)
+ if (mark[i] == lp) {
+ mark[i] = NULL;
+ markno--;
+ }
+}
+
+
+/* dup_line_node: return a pointer to a copy of a line node */
+line_t *
+dup_line_node(line_t *lp)
+{
+ line_t *np;
+
+ if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ return NULL;
+ }
+ np->seek = lp->seek;
+ np->len = lp->len;
+ return np;
+}
+
+
+/* has_trailing_escape: return the parity of escapes preceding a character
+ in a string */
+int
+has_trailing_escape(char *s, char *t)
+{
+ return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1);
+}
+
+
+/* strip_escapes: return copy of escaped string of at most length PATH_MAX */
+char *
+strip_escapes(char *s)
+{
+ static char *file = NULL;
+ static int filesz = 0;
+
+ int i = 0;
+
+ REALLOC(file, filesz, PATH_MAX, NULL);
+ while (i < filesz - 1 /* Worry about a possible trailing escape */
+ && (file[i++] = (*s == '\\') ? *++s : *s))
+ s++;
+ return file;
+}
+
+
+void
+signal_hup(int signo)
+{
+ if (mutex)
+ sigflags |= (1 << (signo - 1));
+ else
+ handle_hup(signo);
+}
+
+
+void
+signal_int(int signo)
+{
+ if (mutex)
+ sigflags |= (1 << (signo - 1));
+ else
+ handle_int(signo);
+}
+
+
+void
+handle_hup(int signo)
+{
+ char *hup = NULL; /* hup filename */
+ char *s;
+ char ed_hup[] = "ed.hup";
+ int n;
+
+ if (!sigactive)
+ quit(1);
+ sigflags &= ~(1 << (signo - 1));
+ if (addr_last && write_file(ed_hup, "w", 1, addr_last) < 0 &&
+ (s = getenv("HOME")) != NULL &&
+ (n = strlen(s)) + 8 <= PATH_MAX && /* "ed.hup" + '/' */
+ (hup = (char *) malloc(n + 10)) != NULL) {
+ strcpy(hup, s);
+ if (hup[n - 1] != '/')
+ hup[n] = '/', hup[n+1] = '\0';
+ strcat(hup, "ed.hup");
+ write_file(hup, "w", 1, addr_last);
+ }
+ quit(2);
+}
+
+
+void
+handle_int(int signo)
+{
+ if (!sigactive)
+ quit(1);
+ sigflags &= ~(1 << (signo - 1));
+#ifdef _POSIX_SOURCE
+ siglongjmp(env, -1);
+#else
+ longjmp(env, -1);
+#endif
+}
+
+
+int cols = 72; /* wrap column */
+
+void
+handle_winch(int signo)
+{
+ int save_errno = errno;
+
+ struct winsize ws; /* window size structure */
+
+ sigflags &= ~(1 << (signo - 1));
+ if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
+ if (ws.ws_row > 2) rows = ws.ws_row - 2;
+ if (ws.ws_col > 8) cols = ws.ws_col - 8;
+ }
+ errno = save_errno;
+}
+
+
+/* is_legal_filename: return a legal filename */
+int
+is_legal_filename(char *s)
+{
+ if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
+ errmsg = "shell access restricted";
+ return 0;
+ }
+ return 1;
+}
diff --git a/text_cmds/ed/re.c b/text_cmds/ed/re.c
new file mode 100644
index 0000000..43f67ae
--- /dev/null
+++ b/text_cmds/ed/re.c
@@ -0,0 +1,132 @@
+/* re.c: This file contains the regular expression interface routines for
+ the ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/re.c,v 1.20 2003/07/20 10:24:09 ru Exp $");
+
+#include "ed.h"
+
+
+extern int patlock;
+
+const char *errmsg = "";
+
+/* get_compiled_pattern: return pointer to compiled pattern from command
+ buffer */
+pattern_t *
+get_compiled_pattern(void)
+{
+ static pattern_t *expr = NULL;
+ static char error[1024];
+
+ char *exprs;
+ char delimiter;
+ int n;
+
+ if ((delimiter = *ibufp) == ' ') {
+ errmsg = "invalid pattern delimiter";
+ return NULL;
+ } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) {
+ if (!expr)
+ errmsg = "no previous pattern";
+ return expr;
+ } else if ((exprs = extract_pattern(delimiter)) == NULL)
+ return NULL;
+ /* buffer alloc'd && not reserved */
+ if (expr && !patlock)
+ regfree(expr);
+ else if ((expr = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ return NULL;
+ }
+ patlock = 0;
+ if ((n = regcomp(expr, exprs, 0))) {
+ regerror(n, expr, error, sizeof error);
+ errmsg = error;
+ free(expr);
+ return expr = NULL;
+ }
+ return expr;
+}
+
+
+/* extract_pattern: copy a pattern string from the command buffer; return
+ pointer to the copy */
+char *
+extract_pattern(int delimiter)
+{
+ static char *lhbuf = NULL; /* buffer */
+ static int lhbufsz = 0; /* buffer size */
+
+ char *nd;
+ int len;
+
+ for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++)
+ switch (*nd) {
+ default:
+ break;
+ case '[':
+ if ((nd = parse_char_class(++nd)) == NULL) {
+ errmsg = "unbalanced brackets ([])";
+ return NULL;
+ }
+ break;
+ case '\\':
+ if (*++nd == '\n') {
+ errmsg = "trailing backslash (\\)";
+ return NULL;
+ }
+ break;
+ }
+ len = nd - ibufp;
+ REALLOC(lhbuf, lhbufsz, len + 1, NULL);
+ memcpy(lhbuf, ibufp, len);
+ lhbuf[len] = '\0';
+ ibufp = nd;
+ return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf;
+}
+
+
+/* parse_char_class: expand a POSIX character class */
+char *
+parse_char_class(char *s)
+{
+ int c, d;
+
+ if (*s == '^')
+ s++;
+ if (*s == ']')
+ s++;
+ for (; *s != ']' && *s != '\n'; s++)
+ if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '='))
+ for (s++, c = *++s; *s != ']' || c != d; s++)
+ if ((c = *s) == '\n')
+ return NULL;
+ return (*s == ']') ? s : NULL;
+}
diff --git a/text_cmds/ed/red.1 b/text_cmds/ed/red.1
new file mode 100644
index 0000000..708251a
--- /dev/null
+++ b/text_cmds/ed/red.1
@@ -0,0 +1 @@
+.so man1/ed.1
diff --git a/text_cmds/ed/sub.c b/text_cmds/ed/sub.c
new file mode 100644
index 0000000..8d89a03
--- /dev/null
+++ b/text_cmds/ed/sub.c
@@ -0,0 +1,256 @@
+/* sub.c: This file contains the substitution routines for the ed
+ line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/sub.c,v 1.15 2002/06/30 05:13:53 obrien Exp $");
+
+#include "ed.h"
+
+
+char *rhbuf; /* rhs substitution buffer */
+int rhbufsz; /* rhs substitution buffer size */
+int rhbufi; /* rhs substitution buffer index */
+
+/* extract_subst_tail: extract substitution tail from the command buffer */
+int
+extract_subst_tail(int *flagp, long *np)
+{
+ char delimiter;
+
+ *flagp = *np = 0;
+ if ((delimiter = *ibufp) == '\n') {
+ rhbufi = 0;
+ *flagp = GPR;
+ return 0;
+ } else if (extract_subst_template() == NULL)
+ return ERR;
+ else if (*ibufp == '\n') {
+ *flagp = GPR;
+ return 0;
+ } else if (*ibufp == delimiter)
+ ibufp++;
+ if ('1' <= *ibufp && *ibufp <= '9') {
+ STRTOL(*np, ibufp);
+ return 0;
+ } else if (*ibufp == 'g') {
+ ibufp++;
+ *flagp = GSG;
+ return 0;
+ }
+ return 0;
+}
+
+
+/* extract_subst_template: return pointer to copy of substitution template
+ in the command buffer */
+char *
+extract_subst_template(void)
+{
+ int n = 0;
+ int i = 0;
+ char c;
+ char delimiter = *ibufp++;
+
+ if (*ibufp == '%' && *(ibufp + 1) == delimiter) {
+ ibufp++;
+ if (!rhbuf)
+ errmsg = "no previous substitution";
+ return rhbuf;
+ }
+ while (*ibufp != delimiter) {
+ REALLOC(rhbuf, rhbufsz, i + 2, NULL);
+ if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
+ i--, ibufp--;
+ break;
+ } else if (c != '\\')
+ ;
+ else if ((rhbuf[i++] = *ibufp++) != '\n')
+ ;
+ else if (!isglobal) {
+ while ((n = get_tty_line()) == 0 ||
+ (n > 0 && ibuf[n - 1] != '\n'))
+ clearerr(stdin);
+ if (n < 0)
+ return NULL;
+ }
+ }
+ REALLOC(rhbuf, rhbufsz, i + 1, NULL);
+ rhbuf[rhbufi = i] = '\0';
+ return rhbuf;
+}
+
+
+char *rbuf; /* substitute_matching_text buffer */
+int rbufsz; /* substitute_matching_text buffer size */
+
+/* search_and_replace: for each line in a range, change text matching a pattern
+ according to a substitution template; return status */
+int
+search_and_replace(pattern_t *pat, int gflag, int kth)
+{
+ undo_t *up;
+ const char *txt;
+ const char *eot;
+ long lc;
+ long xa = current_addr;
+ int nsubs = 0;
+ line_t *lp;
+ int len;
+
+ current_addr = first_addr - 1;
+ for (lc = 0; lc <= second_addr - first_addr; lc++) {
+ lp = get_addressed_line_node(++current_addr);
+ if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0)
+ return ERR;
+ else if (len) {
+ up = NULL;
+ if (delete_lines(current_addr, current_addr) < 0)
+ return ERR;
+ txt = rbuf;
+ eot = rbuf + len;
+ SPL1();
+ do {
+ if ((txt = put_sbuf_line(txt)) == NULL) {
+ SPL0();
+ return ERR;
+ } else if (up)
+ up->t = get_addressed_line_node(current_addr);
+ else if ((up = push_undo_stack(UADD,
+ current_addr, current_addr)) == NULL) {
+ SPL0();
+ return ERR;
+ }
+ } while (txt != eot);
+ SPL0();
+ nsubs++;
+ /* 3751351 */
+ replace_marks(lp, get_addressed_line_node(current_addr));
+ xa = current_addr;
+ }
+ }
+ current_addr = xa;
+ if (nsubs == 0 && !(gflag & GLB)) {
+ errmsg = "no match";
+ return ERR;
+ } else if ((gflag & (GPR | GLS | GNP)) &&
+ display_lines(current_addr, current_addr, gflag) < 0)
+ return ERR;
+ return 0;
+}
+
+
+/* substitute_matching_text: replace text matched by a pattern according to
+ a substitution template; return pointer to the modified text */
+int
+substitute_matching_text(pattern_t *pat, line_t *lp, int gflag, int kth)
+{
+ int off = 0;
+ int changed = 0;
+ int matchno = 0;
+ int i = 0;
+ regmatch_t rm[SE_MAX];
+ char *txt;
+ char *eot;
+
+ if ((txt = get_sbuf_line(lp)) == NULL)
+ return ERR;
+ if (isbinary)
+ NUL_TO_NEWLINE(txt, lp->len);
+ eot = txt + lp->len;
+ if (!regexec(pat, txt, SE_MAX, rm, 0)) {
+ do {
+ if (!kth || kth == ++matchno) {
+ changed++;
+ i = rm[0].rm_so;
+ REALLOC(rbuf, rbufsz, off + i, ERR);
+ if (isbinary)
+ NEWLINE_TO_NUL(txt, rm[0].rm_eo);
+ memcpy(rbuf + off, txt, i);
+ off += i;
+ if ((off = apply_subst_template(txt, rm, off,
+ pat->re_nsub)) < 0)
+ return ERR;
+ } else {
+ i = rm[0].rm_eo;
+ REALLOC(rbuf, rbufsz, off + i, ERR);
+ if (isbinary)
+ NEWLINE_TO_NUL(txt, i);
+ memcpy(rbuf + off, txt, i);
+ off += i;
+ }
+ txt += rm[0].rm_eo;
+ } while (*txt &&
+ (!changed || ((gflag & GSG) && rm[0].rm_eo)) &&
+ !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
+ i = eot - txt;
+ REALLOC(rbuf, rbufsz, off + i + 2, ERR);
+ if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
+ errmsg = "infinite substitution loop";
+ return ERR;
+ }
+ if (isbinary)
+ NEWLINE_TO_NUL(txt, i);
+ memcpy(rbuf + off, txt, i);
+ memcpy(rbuf + off + i, "\n", 2);
+ }
+ return changed ? off + i + 1 : 0;
+}
+
+
+/* apply_subst_template: modify text according to a substitution template;
+ return offset to end of modified text */
+int
+apply_subst_template(const char *boln, regmatch_t *rm, int off, int re_nsub)
+{
+ int j = 0;
+ int k = 0;
+ int n;
+ char *sub = rhbuf;
+
+ for (; sub - rhbuf < rhbufi; sub++)
+ if (*sub == '&') {
+ j = rm[0].rm_so;
+ k = rm[0].rm_eo;
+ REALLOC(rbuf, rbufsz, off + k - j, ERR);
+ while (j < k)
+ rbuf[off++] = boln[j++];
+ } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' &&
+ (n = *sub - '0') <= re_nsub) {
+ j = rm[n].rm_so;
+ k = rm[n].rm_eo;
+ REALLOC(rbuf, rbufsz, off + k - j, ERR);
+ while (j < k)
+ rbuf[off++] = boln[j++];
+ } else {
+ REALLOC(rbuf, rbufsz, off + 1, ERR);
+ rbuf[off++] = *sub;
+ }
+ REALLOC(rbuf, rbufsz, off + 1, ERR);
+ rbuf[off] = '\0';
+ return off;
+}
diff --git a/text_cmds/ed/test/=.err b/text_cmds/ed/test/=.err
new file mode 100644
index 0000000..6a60559
--- /dev/null
+++ b/text_cmds/ed/test/=.err
@@ -0,0 +1 @@
+1,$=
diff --git a/text_cmds/ed/test/README b/text_cmds/ed/test/README
new file mode 100644
index 0000000..e360c4f
--- /dev/null
+++ b/text_cmds/ed/test/README
@@ -0,0 +1,32 @@
+# $FreeBSD: src/bin/ed/test/README,v 1.7 1999/08/27 23:14:17 peter Exp $
+
+The files in this directory with suffixes `.t', `.d', `.r' and `.err' are
+used for testing ed. To run the tests, set the ED variable in the Makefile
+for the path name of the program to be tested (e.g., /bin/ed), and type
+`make'. The tests do not exhaustively verify POSIX compliance nor do
+they verify correct 8-bit or long line support.
+
+The test file suffixes have the following meanings:
+.t Template - a list of ed commands from which an ed script is
+ constructed
+.d Data - read by an ed script
+.r Result - the expected output after processing data via an ed
+ script.
+.err Error - invalid ed commands that should generate an error
+
+The output of the tests is written to the two files err.o and scripts.o.
+At the end of the tests, these files are grep'ed for error messages,
+which look like:
+ *** The script u.ed exited abnormally ***
+or:
+ *** Output u.o of script u.ed is incorrect ***
+
+The POSIX requirement that an address range not be used where at most
+a single address is expected has been relaxed in this version of ed.
+Therefore, the following scripts which test for compliance with this
+POSIX rule exit abnormally:
+=-err.ed
+a1-err.ed
+i1-err.ed
+k1-err.ed
+r1-err.ed
diff --git a/text_cmds/ed/test/TODO b/text_cmds/ed/test/TODO
new file mode 100644
index 0000000..7a4b74f
--- /dev/null
+++ b/text_cmds/ed/test/TODO
@@ -0,0 +1,15 @@
+Some missing tests:
+0) g/./s^@^@ - okay: NULs in commands
+1) g/./s/^@/ - okay: NULs in patterns
+2) a
+ hello^V^Jworld
+ . - okay: embedded newlines in insert mode
+3) ed "" - error: invalid filename
+4) red .. - error: restricted
+5) red / - error: restricted
+5) red !xx - error: restricted
+6) ed -x - verify: 8-bit clean
+7) ed - verify: long-line support
+8) ed - verify: interactive/help mode
+9) G/pat/ - verify: global interactive command
+10) V/pat/ - verify: global interactive command
diff --git a/text_cmds/ed/test/a.d b/text_cmds/ed/test/a.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/a.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/a.r b/text_cmds/ed/test/a.r
new file mode 100644
index 0000000..26257bd
--- /dev/null
+++ b/text_cmds/ed/test/a.r
@@ -0,0 +1,8 @@
+hello world
+line 1
+hello world!
+line 2
+line 3
+line 4
+line5
+hello world!!
diff --git a/text_cmds/ed/test/a.t b/text_cmds/ed/test/a.t
new file mode 100644
index 0000000..ac98c40
--- /dev/null
+++ b/text_cmds/ed/test/a.t
@@ -0,0 +1,9 @@
+0a
+hello world
+.
+2a
+hello world!
+.
+$a
+hello world!!
+.
diff --git a/text_cmds/ed/test/a1.err b/text_cmds/ed/test/a1.err
new file mode 100644
index 0000000..e80815f
--- /dev/null
+++ b/text_cmds/ed/test/a1.err
@@ -0,0 +1,3 @@
+1,$a
+hello world
+.
diff --git a/text_cmds/ed/test/a2.err b/text_cmds/ed/test/a2.err
new file mode 100644
index 0000000..ec4b00b
--- /dev/null
+++ b/text_cmds/ed/test/a2.err
@@ -0,0 +1,3 @@
+aa
+hello world
+.
diff --git a/text_cmds/ed/test/addr.d b/text_cmds/ed/test/addr.d
new file mode 100644
index 0000000..8f7ba1b
--- /dev/null
+++ b/text_cmds/ed/test/addr.d
@@ -0,0 +1,9 @@
+line 1
+line 2
+line 3
+line 4
+line5
+1ine6
+line7
+line8
+line9
diff --git a/text_cmds/ed/test/addr.r b/text_cmds/ed/test/addr.r
new file mode 100644
index 0000000..04caf17
--- /dev/null
+++ b/text_cmds/ed/test/addr.r
@@ -0,0 +1,2 @@
+line 2
+line9
diff --git a/text_cmds/ed/test/addr.t b/text_cmds/ed/test/addr.t
new file mode 100644
index 0000000..750b224
--- /dev/null
+++ b/text_cmds/ed/test/addr.t
@@ -0,0 +1,5 @@
+1 d
+1 1 d
+1,2,d
+1;+ + ,d
+1,2;., + 2d
diff --git a/text_cmds/ed/test/addr1.err b/text_cmds/ed/test/addr1.err
new file mode 100644
index 0000000..29d6383
--- /dev/null
+++ b/text_cmds/ed/test/addr1.err
@@ -0,0 +1 @@
+100
diff --git a/text_cmds/ed/test/addr2.err b/text_cmds/ed/test/addr2.err
new file mode 100644
index 0000000..e96acb9
--- /dev/null
+++ b/text_cmds/ed/test/addr2.err
@@ -0,0 +1 @@
+-100
diff --git a/text_cmds/ed/test/ascii.d.uu b/text_cmds/ed/test/ascii.d.uu
new file mode 100644
index 0000000..0b0a73c
--- /dev/null
+++ b/text_cmds/ed/test/ascii.d.uu
@@ -0,0 +1,9 @@
+begin 644 ascii.d
+M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL
+M+2XO,#$R,S0U-C<X.3H[/#T^/T!!0D-$149'2$E*2TQ-3D]045)35%565UA9
+M6EM<75Y?8&%B8V1E9F=H:6IK;&UN;W!Q<G-T=79W>'EZ>WQ]?G^`@8*#A(6&
+MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S
+MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R<K+S,W.S]#1TM/4U=;7V-G:V]S=WM_@
+?X>+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4
+`
+end
diff --git a/text_cmds/ed/test/ascii.r.uu b/text_cmds/ed/test/ascii.r.uu
new file mode 100644
index 0000000..9ca88b4
--- /dev/null
+++ b/text_cmds/ed/test/ascii.r.uu
@@ -0,0 +1,9 @@
+begin 644 ascii.r
+M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL
+M+2XO,#$R,S0U-C<X.3H[/#T^/T!!0D-$149'2$E*2TQ-3D]045)35%565UA9
+M6EM<75Y?8&%B8V1E9F=H:6IK;&UN;W!Q<G-T=79W>'EZ>WQ]?G^`@8*#A(6&
+MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S
+MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R<K+S,W.S]#1TM/4U=;7V-G:V]S=WM_@
+?X>+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4
+`
+end
diff --git a/text_cmds/ed/test/ascii.t b/text_cmds/ed/test/ascii.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/ascii.t
diff --git a/text_cmds/ed/test/bang1.d b/text_cmds/ed/test/bang1.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/bang1.d
diff --git a/text_cmds/ed/test/bang1.err b/text_cmds/ed/test/bang1.err
new file mode 100644
index 0000000..630af90
--- /dev/null
+++ b/text_cmds/ed/test/bang1.err
@@ -0,0 +1 @@
+.!date
diff --git a/text_cmds/ed/test/bang1.r b/text_cmds/ed/test/bang1.r
new file mode 100644
index 0000000..dcf02b2
--- /dev/null
+++ b/text_cmds/ed/test/bang1.r
@@ -0,0 +1 @@
+okay
diff --git a/text_cmds/ed/test/bang1.t b/text_cmds/ed/test/bang1.t
new file mode 100644
index 0000000..d7b1fea
--- /dev/null
+++ b/text_cmds/ed/test/bang1.t
@@ -0,0 +1,5 @@
+!read one
+hello, world
+a
+okay
+.
diff --git a/text_cmds/ed/test/bang2.err b/text_cmds/ed/test/bang2.err
new file mode 100644
index 0000000..79d8956
--- /dev/null
+++ b/text_cmds/ed/test/bang2.err
@@ -0,0 +1 @@
+!!
diff --git a/text_cmds/ed/test/c.d b/text_cmds/ed/test/c.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/c.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/c.r b/text_cmds/ed/test/c.r
new file mode 100644
index 0000000..0fb3e4f
--- /dev/null
+++ b/text_cmds/ed/test/c.r
@@ -0,0 +1,4 @@
+at the top
+between top/middle
+in the middle
+at the bottom
diff --git a/text_cmds/ed/test/c.t b/text_cmds/ed/test/c.t
new file mode 100644
index 0000000..ebdd536
--- /dev/null
+++ b/text_cmds/ed/test/c.t
@@ -0,0 +1,12 @@
+1c
+at the top
+.
+4c
+in the middle
+.
+$c
+at the bottom
+.
+2,3c
+between top/middle
+.
diff --git a/text_cmds/ed/test/c1.err b/text_cmds/ed/test/c1.err
new file mode 100644
index 0000000..658ec38
--- /dev/null
+++ b/text_cmds/ed/test/c1.err
@@ -0,0 +1,3 @@
+cc
+hello world
+.
diff --git a/text_cmds/ed/test/c2.err b/text_cmds/ed/test/c2.err
new file mode 100644
index 0000000..24b3227
--- /dev/null
+++ b/text_cmds/ed/test/c2.err
@@ -0,0 +1,3 @@
+0c
+hello world
+.
diff --git a/text_cmds/ed/test/ckscripts.sh b/text_cmds/ed/test/ckscripts.sh
new file mode 100755
index 0000000..2c57449
--- /dev/null
+++ b/text_cmds/ed/test/ckscripts.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -
+# This script runs the .ed scripts generated by mkscripts.sh
+# and compares their output against the .r files, which contain
+# the correct output
+#
+# $FreeBSD: src/bin/ed/test/ckscripts.sh,v 1.6 1999/08/27 23:14:18 peter Exp $
+
+PATH="/bin:/usr/bin:/usr/local/bin/:."
+ED=$1
+[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; }
+
+# Run the *.red scripts first, since these don't generate output;
+# they exit with non-zero status
+for i in *.red; do
+ echo $i
+ if $i; then
+ echo "*** The script $i exited abnormally ***"
+ fi
+done >errs.o 2>&1
+
+# Run the remainding scripts; they exit with zero status
+for i in *.ed; do
+# base=`expr $i : '\([^.]*\)'`
+# base=`echo $i | sed 's/\..*//'`
+ base=`$ED - \!"echo $i" <<-EOF
+ s/\..*
+ EOF`
+ if $base.ed; then
+ if cmp -s $base.o $base.r; then :; else
+ echo "*** Output $base.o of script $i is incorrect ***"
+ fi
+ else
+ echo "*** The script $i exited abnormally ***"
+ fi
+done >scripts.o 2>&1
+
+grep -h '\*\*\*' errs.o scripts.o
diff --git a/text_cmds/ed/test/d.d b/text_cmds/ed/test/d.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/d.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/d.err b/text_cmds/ed/test/d.err
new file mode 100644
index 0000000..f03f694
--- /dev/null
+++ b/text_cmds/ed/test/d.err
@@ -0,0 +1 @@
+dd
diff --git a/text_cmds/ed/test/d.r b/text_cmds/ed/test/d.r
new file mode 100644
index 0000000..b7e242c
--- /dev/null
+++ b/text_cmds/ed/test/d.r
@@ -0,0 +1 @@
+line 2
diff --git a/text_cmds/ed/test/d.t b/text_cmds/ed/test/d.t
new file mode 100644
index 0000000..c7c473f
--- /dev/null
+++ b/text_cmds/ed/test/d.t
@@ -0,0 +1,3 @@
+1d
+2;+1d
+$d
diff --git a/text_cmds/ed/test/e1.d b/text_cmds/ed/test/e1.d
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/text_cmds/ed/test/e1.d
@@ -0,0 +1 @@
+hello world
diff --git a/text_cmds/ed/test/e1.err b/text_cmds/ed/test/e1.err
new file mode 100644
index 0000000..827cc29
--- /dev/null
+++ b/text_cmds/ed/test/e1.err
@@ -0,0 +1 @@
+ee e1.err
diff --git a/text_cmds/ed/test/e1.r b/text_cmds/ed/test/e1.r
new file mode 100644
index 0000000..e656728
--- /dev/null
+++ b/text_cmds/ed/test/e1.r
@@ -0,0 +1 @@
+E e1.t
diff --git a/text_cmds/ed/test/e1.t b/text_cmds/ed/test/e1.t
new file mode 100644
index 0000000..e656728
--- /dev/null
+++ b/text_cmds/ed/test/e1.t
@@ -0,0 +1 @@
+E e1.t
diff --git a/text_cmds/ed/test/e2.d b/text_cmds/ed/test/e2.d
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e2.d
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e2.err b/text_cmds/ed/test/e2.err
new file mode 100644
index 0000000..779a64b
--- /dev/null
+++ b/text_cmds/ed/test/e2.err
@@ -0,0 +1 @@
+.e e2.err
diff --git a/text_cmds/ed/test/e2.r b/text_cmds/ed/test/e2.r
new file mode 100644
index 0000000..59ebf11
--- /dev/null
+++ b/text_cmds/ed/test/e2.r
@@ -0,0 +1 @@
+hello world-
diff --git a/text_cmds/ed/test/e2.t b/text_cmds/ed/test/e2.t
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e2.t
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e3.d b/text_cmds/ed/test/e3.d
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e3.d
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e3.err b/text_cmds/ed/test/e3.err
new file mode 100644
index 0000000..80a7fdc
--- /dev/null
+++ b/text_cmds/ed/test/e3.err
@@ -0,0 +1 @@
+ee.err
diff --git a/text_cmds/ed/test/e3.r b/text_cmds/ed/test/e3.r
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e3.r
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e3.t b/text_cmds/ed/test/e3.t
new file mode 100644
index 0000000..1c50726
--- /dev/null
+++ b/text_cmds/ed/test/e3.t
@@ -0,0 +1 @@
+E
diff --git a/text_cmds/ed/test/e4.d b/text_cmds/ed/test/e4.d
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e4.d
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e4.r b/text_cmds/ed/test/e4.r
new file mode 100644
index 0000000..aa44630
--- /dev/null
+++ b/text_cmds/ed/test/e4.r
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/text_cmds/ed/test/e4.t b/text_cmds/ed/test/e4.t
new file mode 100644
index 0000000..d905d9d
--- /dev/null
+++ b/text_cmds/ed/test/e4.t
@@ -0,0 +1 @@
+e
diff --git a/text_cmds/ed/test/f1.err b/text_cmds/ed/test/f1.err
new file mode 100644
index 0000000..e60975a
--- /dev/null
+++ b/text_cmds/ed/test/f1.err
@@ -0,0 +1 @@
+.f f1.err
diff --git a/text_cmds/ed/test/f2.err b/text_cmds/ed/test/f2.err
new file mode 100644
index 0000000..26d1c5e
--- /dev/null
+++ b/text_cmds/ed/test/f2.err
@@ -0,0 +1 @@
+ff1.err
diff --git a/text_cmds/ed/test/g1.d b/text_cmds/ed/test/g1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/g1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/g1.err b/text_cmds/ed/test/g1.err
new file mode 100644
index 0000000..f95ea22
--- /dev/null
+++ b/text_cmds/ed/test/g1.err
@@ -0,0 +1 @@
+g/./s //x/
diff --git a/text_cmds/ed/test/g1.r b/text_cmds/ed/test/g1.r
new file mode 100644
index 0000000..578a44b
--- /dev/null
+++ b/text_cmds/ed/test/g1.r
@@ -0,0 +1,15 @@
+line5
+help! world
+order
+line 4
+help! world
+order
+line 3
+help! world
+order
+line 2
+help! world
+order
+line 1
+help! world
+order
diff --git a/text_cmds/ed/test/g1.t b/text_cmds/ed/test/g1.t
new file mode 100644
index 0000000..2d0b54f
--- /dev/null
+++ b/text_cmds/ed/test/g1.t
@@ -0,0 +1,6 @@
+g/./m0
+g/./s/$/\
+hello world
+g/hello /s/lo/p!/\
+a\
+order
diff --git a/text_cmds/ed/test/g2.d b/text_cmds/ed/test/g2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/g2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/g2.err b/text_cmds/ed/test/g2.err
new file mode 100644
index 0000000..0ff6a5a
--- /dev/null
+++ b/text_cmds/ed/test/g2.err
@@ -0,0 +1 @@
+g//s/./x/
diff --git a/text_cmds/ed/test/g2.r b/text_cmds/ed/test/g2.r
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/text_cmds/ed/test/g2.r
@@ -0,0 +1 @@
+hello world
diff --git a/text_cmds/ed/test/g2.t b/text_cmds/ed/test/g2.t
new file mode 100644
index 0000000..831ee83
--- /dev/null
+++ b/text_cmds/ed/test/g2.t
@@ -0,0 +1,2 @@
+g/[2-4]/-1,+1c\
+hello world
diff --git a/text_cmds/ed/test/g3.d b/text_cmds/ed/test/g3.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/g3.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/g3.err b/text_cmds/ed/test/g3.err
new file mode 100644
index 0000000..01058d8
--- /dev/null
+++ b/text_cmds/ed/test/g3.err
@@ -0,0 +1 @@
+g
diff --git a/text_cmds/ed/test/g3.r b/text_cmds/ed/test/g3.r
new file mode 100644
index 0000000..cc6fbdd
--- /dev/null
+++ b/text_cmds/ed/test/g3.r
@@ -0,0 +1,5 @@
+linc 3
+xine 1
+xine 2
+xinc 4
+xinc5
diff --git a/text_cmds/ed/test/g3.t b/text_cmds/ed/test/g3.t
new file mode 100644
index 0000000..2d052a6
--- /dev/null
+++ b/text_cmds/ed/test/g3.t
@@ -0,0 +1,4 @@
+g/./s//x/\
+3m0
+g/./s/e/c/\
+2,3m1
diff --git a/text_cmds/ed/test/g4.d b/text_cmds/ed/test/g4.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/g4.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/g4.r b/text_cmds/ed/test/g4.r
new file mode 100644
index 0000000..350882d
--- /dev/null
+++ b/text_cmds/ed/test/g4.r
@@ -0,0 +1,7 @@
+hello
+zine 1
+line 2
+line 3
+line 4
+line5
+world
diff --git a/text_cmds/ed/test/g4.t b/text_cmds/ed/test/g4.t
new file mode 100644
index 0000000..ec61816
--- /dev/null
+++ b/text_cmds/ed/test/g4.t
@@ -0,0 +1,13 @@
+g/./s/./x/\
+u\
+s/./y/\
+u\
+s/./z/\
+u
+u
+0a
+hello
+.
+$a
+world
+.
diff --git a/text_cmds/ed/test/g5.d b/text_cmds/ed/test/g5.d
new file mode 100644
index 0000000..a92d664
--- /dev/null
+++ b/text_cmds/ed/test/g5.d
@@ -0,0 +1,3 @@
+line 1
+line 2
+line 3
diff --git a/text_cmds/ed/test/g5.r b/text_cmds/ed/test/g5.r
new file mode 100644
index 0000000..15a2675
--- /dev/null
+++ b/text_cmds/ed/test/g5.r
@@ -0,0 +1,9 @@
+line 1
+line 2
+line 3
+line 2
+line 3
+line 1
+line 3
+line 1
+line 2
diff --git a/text_cmds/ed/test/g5.t b/text_cmds/ed/test/g5.t
new file mode 100644
index 0000000..e213481
--- /dev/null
+++ b/text_cmds/ed/test/g5.t
@@ -0,0 +1,2 @@
+g/./1,3t$\
+1d
diff --git a/text_cmds/ed/test/h.err b/text_cmds/ed/test/h.err
new file mode 100644
index 0000000..a71e506
--- /dev/null
+++ b/text_cmds/ed/test/h.err
@@ -0,0 +1 @@
+.h
diff --git a/text_cmds/ed/test/i.d b/text_cmds/ed/test/i.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/i.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/i.r b/text_cmds/ed/test/i.r
new file mode 100644
index 0000000..5f27af0
--- /dev/null
+++ b/text_cmds/ed/test/i.r
@@ -0,0 +1,8 @@
+hello world
+hello world!
+line 1
+line 2
+line 3
+line 4
+hello world!!
+line5
diff --git a/text_cmds/ed/test/i.t b/text_cmds/ed/test/i.t
new file mode 100644
index 0000000..d1d9805
--- /dev/null
+++ b/text_cmds/ed/test/i.t
@@ -0,0 +1,9 @@
+1i
+hello world
+.
+2i
+hello world!
+.
+$i
+hello world!!
+.
diff --git a/text_cmds/ed/test/i1.err b/text_cmds/ed/test/i1.err
new file mode 100644
index 0000000..aaddede
--- /dev/null
+++ b/text_cmds/ed/test/i1.err
@@ -0,0 +1,3 @@
+1,$i
+hello world
+.
diff --git a/text_cmds/ed/test/i2.err b/text_cmds/ed/test/i2.err
new file mode 100644
index 0000000..b63f5ac
--- /dev/null
+++ b/text_cmds/ed/test/i2.err
@@ -0,0 +1,3 @@
+ii
+hello world
+.
diff --git a/text_cmds/ed/test/i3.err b/text_cmds/ed/test/i3.err
new file mode 100644
index 0000000..6d200c8
--- /dev/null
+++ b/text_cmds/ed/test/i3.err
@@ -0,0 +1,3 @@
+0i
+hello world
+.
diff --git a/text_cmds/ed/test/j.d b/text_cmds/ed/test/j.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/j.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/j.r b/text_cmds/ed/test/j.r
new file mode 100644
index 0000000..66f36a8
--- /dev/null
+++ b/text_cmds/ed/test/j.r
@@ -0,0 +1,4 @@
+line 1
+line 2line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/j.t b/text_cmds/ed/test/j.t
new file mode 100644
index 0000000..9b5d28d
--- /dev/null
+++ b/text_cmds/ed/test/j.t
@@ -0,0 +1,2 @@
+1,1j
+2,3j
diff --git a/text_cmds/ed/test/k.d b/text_cmds/ed/test/k.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/k.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/k.r b/text_cmds/ed/test/k.r
new file mode 100644
index 0000000..eeb38db
--- /dev/null
+++ b/text_cmds/ed/test/k.r
@@ -0,0 +1,5 @@
+line 3
+hello world
+line 4
+line5
+line 2
diff --git a/text_cmds/ed/test/k.t b/text_cmds/ed/test/k.t
new file mode 100644
index 0000000..53d588d
--- /dev/null
+++ b/text_cmds/ed/test/k.t
@@ -0,0 +1,10 @@
+2ka
+1d
+'am$
+1ka
+0a
+hello world
+.
+'ad
+u
+'am0
diff --git a/text_cmds/ed/test/k1.err b/text_cmds/ed/test/k1.err
new file mode 100644
index 0000000..eba1f3d
--- /dev/null
+++ b/text_cmds/ed/test/k1.err
@@ -0,0 +1 @@
+1,$ka
diff --git a/text_cmds/ed/test/k2.err b/text_cmds/ed/test/k2.err
new file mode 100644
index 0000000..b34a18d
--- /dev/null
+++ b/text_cmds/ed/test/k2.err
@@ -0,0 +1 @@
+kA
diff --git a/text_cmds/ed/test/k3.err b/text_cmds/ed/test/k3.err
new file mode 100644
index 0000000..70190c4
--- /dev/null
+++ b/text_cmds/ed/test/k3.err
@@ -0,0 +1 @@
+0ka
diff --git a/text_cmds/ed/test/k4.err b/text_cmds/ed/test/k4.err
new file mode 100644
index 0000000..3457642
--- /dev/null
+++ b/text_cmds/ed/test/k4.err
@@ -0,0 +1,6 @@
+a
+hello
+.
+.ka
+'ad
+'ap
diff --git a/text_cmds/ed/test/l.d b/text_cmds/ed/test/l.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/l.d
diff --git a/text_cmds/ed/test/l.r b/text_cmds/ed/test/l.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/l.r
diff --git a/text_cmds/ed/test/l.t b/text_cmds/ed/test/l.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/l.t
diff --git a/text_cmds/ed/test/m.d b/text_cmds/ed/test/m.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/m.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/m.err b/text_cmds/ed/test/m.err
new file mode 100644
index 0000000..3aec4c3
--- /dev/null
+++ b/text_cmds/ed/test/m.err
@@ -0,0 +1,4 @@
+a
+hello world
+.
+1,$m1
diff --git a/text_cmds/ed/test/m.r b/text_cmds/ed/test/m.r
new file mode 100644
index 0000000..186cf54
--- /dev/null
+++ b/text_cmds/ed/test/m.r
@@ -0,0 +1,5 @@
+line5
+line 1
+line 2
+line 3
+line 4
diff --git a/text_cmds/ed/test/m.t b/text_cmds/ed/test/m.t
new file mode 100644
index 0000000..c39c088
--- /dev/null
+++ b/text_cmds/ed/test/m.t
@@ -0,0 +1,7 @@
+1,2m$
+1,2m$
+1,2m$
+$m0
+$m0
+2,3m1
+2,3m3
diff --git a/text_cmds/ed/test/mkscripts.sh b/text_cmds/ed/test/mkscripts.sh
new file mode 100755
index 0000000..2d1a06f
--- /dev/null
+++ b/text_cmds/ed/test/mkscripts.sh
@@ -0,0 +1,75 @@
+#!/bin/sh -
+# This script generates ed test scripts (.ed) from .t files
+#
+# $FreeBSD: src/bin/ed/test/mkscripts.sh,v 1.6 1999/08/27 23:14:20 peter Exp $
+
+PATH="/bin:/usr/bin:/usr/local/bin/:."
+ED=$1
+[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; }
+
+for i in *.t; do
+# base=${i%.*}
+# base=`echo $i | sed 's/\..*//'`
+# base=`expr $i : '\([^.]*\)'`
+# (
+# echo "#!/bin/sh -"
+# echo "$ED - <<\EOT"
+# echo "r $base.d"
+# cat $i
+# echo "w $base.o"
+# echo EOT
+# ) >$base.ed
+# chmod +x $base.ed
+# The following is pretty ugly way of doing the above, and not appropriate
+# use of ed but the point is that it can be done...
+ base=`$ED - \!"echo $i" <<-EOF
+ s/\..*
+ EOF`
+ $ED - <<-EOF
+ a
+ #!/bin/sh -
+ $ED - <<\EOT
+ H
+ r $base.d
+ w $base.o
+ EOT
+ .
+ -2r $i
+ w $base.ed
+ !chmod +x $base.ed
+ EOF
+done
+
+for i in *.err; do
+# base=${i%.*}
+# base=`echo $i | sed 's/\..*//'`
+# base=`expr $i : '\([^.]*\)'`
+# (
+# echo "#!/bin/sh -"
+# echo "$ED - <<\EOT"
+# echo H
+# echo "r $base.err"
+# cat $i
+# echo "w $base.o"
+# echo EOT
+# ) >$base-err.ed
+# chmod +x $base-err.ed
+# The following is pretty ugly way of doing the above, and not appropriate
+# use of ed but the point is that it can be done...
+ base=`$ED - \!"echo $i" <<-EOF
+ s/\..*
+ EOF`
+ $ED - <<-EOF
+ a
+ #!/bin/sh -
+ $ED - <<\EOT
+ H
+ r $base.err
+ w $base.o
+ EOT
+ .
+ -2r $i
+ w ${base}.red
+ !chmod +x ${base}.red
+ EOF
+done
diff --git a/text_cmds/ed/test/n.d b/text_cmds/ed/test/n.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/n.d
diff --git a/text_cmds/ed/test/n.r b/text_cmds/ed/test/n.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/n.r
diff --git a/text_cmds/ed/test/n.t b/text_cmds/ed/test/n.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/n.t
diff --git a/text_cmds/ed/test/nl.err b/text_cmds/ed/test/nl.err
new file mode 100644
index 0000000..8949a85
--- /dev/null
+++ b/text_cmds/ed/test/nl.err
@@ -0,0 +1 @@
+,1
diff --git a/text_cmds/ed/test/nl1.d b/text_cmds/ed/test/nl1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/nl1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/nl1.r b/text_cmds/ed/test/nl1.r
new file mode 100644
index 0000000..9d8854c
--- /dev/null
+++ b/text_cmds/ed/test/nl1.r
@@ -0,0 +1,8 @@
+
+
+hello world
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/nl1.t b/text_cmds/ed/test/nl1.t
new file mode 100644
index 0000000..ea192e9
--- /dev/null
+++ b/text_cmds/ed/test/nl1.t
@@ -0,0 +1,8 @@
+1
+
+
+0a
+
+
+hello world
+.
diff --git a/text_cmds/ed/test/nl2.d b/text_cmds/ed/test/nl2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/nl2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/nl2.r b/text_cmds/ed/test/nl2.r
new file mode 100644
index 0000000..fe99e41
--- /dev/null
+++ b/text_cmds/ed/test/nl2.r
@@ -0,0 +1,6 @@
+line 1
+line 2
+line 3
+line 4
+line5
+hello world
diff --git a/text_cmds/ed/test/nl2.t b/text_cmds/ed/test/nl2.t
new file mode 100644
index 0000000..73fd27b
--- /dev/null
+++ b/text_cmds/ed/test/nl2.t
@@ -0,0 +1,4 @@
+a
+hello world
+.
+0;/./
diff --git a/text_cmds/ed/test/p.d b/text_cmds/ed/test/p.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/p.d
diff --git a/text_cmds/ed/test/p.r b/text_cmds/ed/test/p.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/p.r
diff --git a/text_cmds/ed/test/p.t b/text_cmds/ed/test/p.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/p.t
diff --git a/text_cmds/ed/test/q.d b/text_cmds/ed/test/q.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/q.d
diff --git a/text_cmds/ed/test/q.r b/text_cmds/ed/test/q.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/q.r
diff --git a/text_cmds/ed/test/q.t b/text_cmds/ed/test/q.t
new file mode 100644
index 0000000..123a2c8
--- /dev/null
+++ b/text_cmds/ed/test/q.t
@@ -0,0 +1,5 @@
+w q.o
+a
+hello
+.
+q
diff --git a/text_cmds/ed/test/q1.err b/text_cmds/ed/test/q1.err
new file mode 100644
index 0000000..0a7e178
--- /dev/null
+++ b/text_cmds/ed/test/q1.err
@@ -0,0 +1 @@
+.q
diff --git a/text_cmds/ed/test/r1.d b/text_cmds/ed/test/r1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/r1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/r1.err b/text_cmds/ed/test/r1.err
new file mode 100644
index 0000000..269aa7c
--- /dev/null
+++ b/text_cmds/ed/test/r1.err
@@ -0,0 +1 @@
+1,$r r1.err
diff --git a/text_cmds/ed/test/r1.r b/text_cmds/ed/test/r1.r
new file mode 100644
index 0000000..a3ff506
--- /dev/null
+++ b/text_cmds/ed/test/r1.r
@@ -0,0 +1,7 @@
+line 1
+hello world
+line 2
+line 3
+line 4
+line5
+hello world
diff --git a/text_cmds/ed/test/r1.t b/text_cmds/ed/test/r1.t
new file mode 100644
index 0000000..d787a92
--- /dev/null
+++ b/text_cmds/ed/test/r1.t
@@ -0,0 +1,3 @@
+1;r !echo hello world
+1
+r !echo hello world
diff --git a/text_cmds/ed/test/r2.d b/text_cmds/ed/test/r2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/r2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/r2.err b/text_cmds/ed/test/r2.err
new file mode 100644
index 0000000..1c44fa3
--- /dev/null
+++ b/text_cmds/ed/test/r2.err
@@ -0,0 +1 @@
+r a-good-book
diff --git a/text_cmds/ed/test/r2.r b/text_cmds/ed/test/r2.r
new file mode 100644
index 0000000..ac152ba
--- /dev/null
+++ b/text_cmds/ed/test/r2.r
@@ -0,0 +1,10 @@
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/r2.t b/text_cmds/ed/test/r2.t
new file mode 100644
index 0000000..4286f42
--- /dev/null
+++ b/text_cmds/ed/test/r2.t
@@ -0,0 +1 @@
+r
diff --git a/text_cmds/ed/test/r3.d b/text_cmds/ed/test/r3.d
new file mode 100644
index 0000000..593eec6
--- /dev/null
+++ b/text_cmds/ed/test/r3.d
@@ -0,0 +1 @@
+r r3.t
diff --git a/text_cmds/ed/test/r3.r b/text_cmds/ed/test/r3.r
new file mode 100644
index 0000000..86d5f90
--- /dev/null
+++ b/text_cmds/ed/test/r3.r
@@ -0,0 +1,2 @@
+r r3.t
+r r3.t
diff --git a/text_cmds/ed/test/r3.t b/text_cmds/ed/test/r3.t
new file mode 100644
index 0000000..593eec6
--- /dev/null
+++ b/text_cmds/ed/test/r3.t
@@ -0,0 +1 @@
+r r3.t
diff --git a/text_cmds/ed/test/s1.d b/text_cmds/ed/test/s1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/s1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/s1.err b/text_cmds/ed/test/s1.err
new file mode 100644
index 0000000..d7ca0cf
--- /dev/null
+++ b/text_cmds/ed/test/s1.err
@@ -0,0 +1 @@
+s . x
diff --git a/text_cmds/ed/test/s1.r b/text_cmds/ed/test/s1.r
new file mode 100644
index 0000000..4eb0980
--- /dev/null
+++ b/text_cmds/ed/test/s1.r
@@ -0,0 +1,5 @@
+liene 1
+(liene) (2)
+(liene) (3)
+liene (4)
+(()liene5)
diff --git a/text_cmds/ed/test/s1.t b/text_cmds/ed/test/s1.t
new file mode 100644
index 0000000..b0028bb
--- /dev/null
+++ b/text_cmds/ed/test/s1.t
@@ -0,0 +1,6 @@
+s/\([^ ][^ ]*\)/(\1)/g
+2s
+/3/s
+/\(4\)/sr
+/\(.\)/srg
+%s/i/&e/
diff --git a/text_cmds/ed/test/s10.err b/text_cmds/ed/test/s10.err
new file mode 100644
index 0000000..0d8d83d
--- /dev/null
+++ b/text_cmds/ed/test/s10.err
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[.]/x/
diff --git a/text_cmds/ed/test/s2.d b/text_cmds/ed/test/s2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/s2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/s2.err b/text_cmds/ed/test/s2.err
new file mode 100644
index 0000000..b5c851d
--- /dev/null
+++ b/text_cmds/ed/test/s2.err
@@ -0,0 +1,4 @@
+a
+a
+.
+s/x*/a/g
diff --git a/text_cmds/ed/test/s2.r b/text_cmds/ed/test/s2.r
new file mode 100644
index 0000000..ca305c8
--- /dev/null
+++ b/text_cmds/ed/test/s2.r
@@ -0,0 +1,5 @@
+li(n)e 1
+i(n)e 200
+li(n)e 3
+li(n)e 4
+li(n)e500
diff --git a/text_cmds/ed/test/s2.t b/text_cmds/ed/test/s2.t
new file mode 100644
index 0000000..f365849
--- /dev/null
+++ b/text_cmds/ed/test/s2.t
@@ -0,0 +1,4 @@
+,s/./(&)/3
+s/$/00
+2s//%/g
+s/^l
diff --git a/text_cmds/ed/test/s3.d b/text_cmds/ed/test/s3.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/text_cmds/ed/test/s3.d
diff --git a/text_cmds/ed/test/s3.err b/text_cmds/ed/test/s3.err
new file mode 100644
index 0000000..d68c7d0
--- /dev/null
+++ b/text_cmds/ed/test/s3.err
@@ -0,0 +1 @@
+s/[xyx/a/
diff --git a/text_cmds/ed/test/s3.r b/text_cmds/ed/test/s3.r
new file mode 100644
index 0000000..d6cada2
--- /dev/null
+++ b/text_cmds/ed/test/s3.r
@@ -0,0 +1 @@
+hello world
diff --git a/text_cmds/ed/test/s3.t b/text_cmds/ed/test/s3.t
new file mode 100644
index 0000000..fbf8803
--- /dev/null
+++ b/text_cmds/ed/test/s3.t
@@ -0,0 +1,6 @@
+a
+hello/[]world
+.
+s/[/]/ /
+s/[[:digit:][]/ /
+s/[]]/ /
diff --git a/text_cmds/ed/test/s4.err b/text_cmds/ed/test/s4.err
new file mode 100644
index 0000000..35b609f
--- /dev/null
+++ b/text_cmds/ed/test/s4.err
@@ -0,0 +1 @@
+s/\a\b\c/xyz/
diff --git a/text_cmds/ed/test/s5.err b/text_cmds/ed/test/s5.err
new file mode 100644
index 0000000..89104c5
--- /dev/null
+++ b/text_cmds/ed/test/s5.err
@@ -0,0 +1 @@
+s//xyz/
diff --git a/text_cmds/ed/test/s6.err b/text_cmds/ed/test/s6.err
new file mode 100644
index 0000000..b478595
--- /dev/null
+++ b/text_cmds/ed/test/s6.err
@@ -0,0 +1 @@
+s
diff --git a/text_cmds/ed/test/s7.err b/text_cmds/ed/test/s7.err
new file mode 100644
index 0000000..30ba4fd
--- /dev/null
+++ b/text_cmds/ed/test/s7.err
@@ -0,0 +1,5 @@
+a
+hello world
+.
+/./
+sr
diff --git a/text_cmds/ed/test/s8.err b/text_cmds/ed/test/s8.err
new file mode 100644
index 0000000..5665767
--- /dev/null
+++ b/text_cmds/ed/test/s8.err
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[=]/x/
diff --git a/text_cmds/ed/test/s9.err b/text_cmds/ed/test/s9.err
new file mode 100644
index 0000000..1ff16dd
--- /dev/null
+++ b/text_cmds/ed/test/s9.err
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[:]/x/
diff --git a/text_cmds/ed/test/t.d b/text_cmds/ed/test/t.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/t.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t.r b/text_cmds/ed/test/t.r
new file mode 100644
index 0000000..2b28547
--- /dev/null
+++ b/text_cmds/ed/test/t.r
@@ -0,0 +1,16 @@
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t1.d b/text_cmds/ed/test/t1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/t1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t1.err b/text_cmds/ed/test/t1.err
new file mode 100644
index 0000000..c49c556
--- /dev/null
+++ b/text_cmds/ed/test/t1.err
@@ -0,0 +1 @@
+tt
diff --git a/text_cmds/ed/test/t1.r b/text_cmds/ed/test/t1.r
new file mode 100644
index 0000000..2b28547
--- /dev/null
+++ b/text_cmds/ed/test/t1.r
@@ -0,0 +1,16 @@
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t1.t b/text_cmds/ed/test/t1.t
new file mode 100644
index 0000000..6b66163
--- /dev/null
+++ b/text_cmds/ed/test/t1.t
@@ -0,0 +1,3 @@
+1t0
+2,3t2
+,t$
diff --git a/text_cmds/ed/test/t2.d b/text_cmds/ed/test/t2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/t2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t2.err b/text_cmds/ed/test/t2.err
new file mode 100644
index 0000000..c202051
--- /dev/null
+++ b/text_cmds/ed/test/t2.err
@@ -0,0 +1 @@
+t0;-1
diff --git a/text_cmds/ed/test/t2.r b/text_cmds/ed/test/t2.r
new file mode 100644
index 0000000..0c75ff5
--- /dev/null
+++ b/text_cmds/ed/test/t2.r
@@ -0,0 +1,6 @@
+line 1
+line5
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/t2.t b/text_cmds/ed/test/t2.t
new file mode 100644
index 0000000..5175abd
--- /dev/null
+++ b/text_cmds/ed/test/t2.t
@@ -0,0 +1 @@
+t0;/./
diff --git a/text_cmds/ed/test/u.d b/text_cmds/ed/test/u.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/u.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/u.err b/text_cmds/ed/test/u.err
new file mode 100644
index 0000000..caa1ba1
--- /dev/null
+++ b/text_cmds/ed/test/u.err
@@ -0,0 +1 @@
+.u
diff --git a/text_cmds/ed/test/u.r b/text_cmds/ed/test/u.r
new file mode 100644
index 0000000..ad558d8
--- /dev/null
+++ b/text_cmds/ed/test/u.r
@@ -0,0 +1,9 @@
+line 1
+hello
+hello world!!
+line 2
+line 3
+line 4
+line5
+hello
+hello world!!
diff --git a/text_cmds/ed/test/u.t b/text_cmds/ed/test/u.t
new file mode 100644
index 0000000..131cb6e
--- /dev/null
+++ b/text_cmds/ed/test/u.t
@@ -0,0 +1,31 @@
+1;r u.t
+u
+a
+hello
+world
+.
+g/./s//x/\
+a\
+hello\
+world
+u
+u
+u
+a
+hello world!
+.
+u
+1,$d
+u
+2,3d
+u
+c
+hello world!!
+.
+u
+u
+-1;.,+1j
+u
+u
+u
+.,+1t$
diff --git a/text_cmds/ed/test/v.d b/text_cmds/ed/test/v.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/v.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/v.r b/text_cmds/ed/test/v.r
new file mode 100644
index 0000000..714db63
--- /dev/null
+++ b/text_cmds/ed/test/v.r
@@ -0,0 +1,11 @@
+line5
+order
+hello world
+line 1
+order
+line 2
+order
+line 3
+order
+line 4
+order
diff --git a/text_cmds/ed/test/v.t b/text_cmds/ed/test/v.t
new file mode 100644
index 0000000..608a77f
--- /dev/null
+++ b/text_cmds/ed/test/v.t
@@ -0,0 +1,6 @@
+v/[ ]/m0
+v/[ ]/s/$/\
+hello world
+v/hello /s/lo/p!/\
+a\
+order
diff --git a/text_cmds/ed/test/w.d b/text_cmds/ed/test/w.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/text_cmds/ed/test/w.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/w.r b/text_cmds/ed/test/w.r
new file mode 100644
index 0000000..ac152ba
--- /dev/null
+++ b/text_cmds/ed/test/w.r
@@ -0,0 +1,10 @@
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/text_cmds/ed/test/w.t b/text_cmds/ed/test/w.t
new file mode 100644
index 0000000..c2e18bd
--- /dev/null
+++ b/text_cmds/ed/test/w.t
@@ -0,0 +1,2 @@
+w !cat >\!.z
+r \!.z
diff --git a/text_cmds/ed/test/w1.err b/text_cmds/ed/test/w1.err
new file mode 100644
index 0000000..e2c8a60
--- /dev/null
+++ b/text_cmds/ed/test/w1.err
@@ -0,0 +1 @@
+w /to/some/far-away/place
diff --git a/text_cmds/ed/test/w2.err b/text_cmds/ed/test/w2.err
new file mode 100644
index 0000000..9daf89c
--- /dev/null
+++ b/text_cmds/ed/test/w2.err
@@ -0,0 +1 @@
+ww.o
diff --git a/text_cmds/ed/test/w3.err b/text_cmds/ed/test/w3.err
new file mode 100644
index 0000000..39bbf4c
--- /dev/null
+++ b/text_cmds/ed/test/w3.err
@@ -0,0 +1 @@
+wqp w.o
diff --git a/text_cmds/ed/test/x.err b/text_cmds/ed/test/x.err
new file mode 100644
index 0000000..0953f01
--- /dev/null
+++ b/text_cmds/ed/test/x.err
@@ -0,0 +1 @@
+.x
diff --git a/text_cmds/ed/test/z.err b/text_cmds/ed/test/z.err
new file mode 100644
index 0000000..6a51a2d
--- /dev/null
+++ b/text_cmds/ed/test/z.err
@@ -0,0 +1,2 @@
+z
+z
diff --git a/text_cmds/ed/undo.c b/text_cmds/ed/undo.c
new file mode 100644
index 0000000..2837495
--- /dev/null
+++ b/text_cmds/ed/undo.c
@@ -0,0 +1,150 @@
+/* undo.c: This file contains the undo routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ed/undo.c,v 1.12 2002/06/30 05:13:53 obrien Exp $");
+
+#include "ed.h"
+
+
+#define USIZE 100 /* undo stack size */
+undo_t *ustack = NULL; /* undo stack */
+long usize = 0; /* stack size variable */
+long u_p = 0; /* undo stack pointer */
+
+/* push_undo_stack: return pointer to initialized undo node */
+undo_t *
+push_undo_stack(int type, long from, long to)
+{
+ undo_t *t;
+
+#if defined(sun) || defined(NO_REALLOC_NULL)
+ if (ustack == NULL &&
+ (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ return NULL;
+ }
+#endif
+ t = ustack;
+ if (u_p < usize ||
+ (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) {
+ ustack = t;
+ ustack[u_p].type = type;
+ ustack[u_p].t = get_addressed_line_node(to);
+ ustack[u_p].h = get_addressed_line_node(from);
+ return ustack + u_p++;
+ }
+ /* out of memory - release undo stack */
+ fprintf(stderr, "%s\n", strerror(errno));
+ errmsg = "out of memory";
+ clear_undo_stack();
+ free(ustack);
+ ustack = NULL;
+ usize = 0;
+ return NULL;
+}
+
+
+/* USWAP: swap undo nodes */
+#define USWAP(x,y) { \
+ undo_t utmp; \
+ utmp = x, x = y, y = utmp; \
+}
+
+
+long u_current_addr = -1; /* if >= 0, undo enabled */
+long u_addr_last = -1; /* if >= 0, undo enabled */
+
+/* pop_undo_stack: undo last change to the editor buffer */
+int
+pop_undo_stack(void)
+{
+ long n;
+ long o_current_addr = current_addr;
+ long o_addr_last = addr_last;
+
+ if (u_current_addr == -1 || u_addr_last == -1) {
+ errmsg = "nothing to undo";
+ return ERR;
+ } else if (u_p)
+ modified = 1;
+ get_addressed_line_node(0); /* this get_addressed_line_node last! */
+ SPL1();
+ for (n = u_p; n-- > 0;) {
+ switch(ustack[n].type) {
+ case UADD:
+ REQUE(ustack[n].h->q_back, ustack[n].t->q_forw);
+ break;
+ case UDEL:
+ REQUE(ustack[n].h->q_back, ustack[n].h);
+ REQUE(ustack[n].t, ustack[n].t->q_forw);
+ break;
+ case UMOV:
+ case VMOV:
+ REQUE(ustack[n - 1].h, ustack[n].h->q_forw);
+ REQUE(ustack[n].t->q_back, ustack[n - 1].t);
+ REQUE(ustack[n].h, ustack[n].t);
+ n--;
+ break;
+ default:
+ /*NOTREACHED*/
+ ;
+ }
+ ustack[n].type ^= 1;
+ }
+ /* reverse undo stack order */
+ for (n = u_p; n-- > (u_p + 1)/ 2;)
+ USWAP(ustack[n], ustack[u_p - 1 - n]);
+ if (isglobal)
+ clear_active_list();
+ current_addr = u_current_addr, u_current_addr = o_current_addr;
+ addr_last = u_addr_last, u_addr_last = o_addr_last;
+ SPL0();
+ return 0;
+}
+
+
+/* clear_undo_stack: clear the undo stack */
+void
+clear_undo_stack(void)
+{
+ line_t *lp, *ep, *tl;
+
+ while (u_p--)
+ if (ustack[u_p].type == UDEL) {
+ ep = ustack[u_p].t->q_forw;
+ for (lp = ustack[u_p].h; lp != ep; lp = tl) {
+ unmark_line_node(lp);
+ tl = lp->q_forw;
+ free(lp);
+ }
+ }
+ u_p = 0;
+ u_current_addr = current_addr;
+ u_addr_last = addr_last;
+}
diff --git a/text_cmds/ee/Changes b/text_cmds/ee/Changes
new file mode 100644
index 0000000..0f2c8ab
--- /dev/null
+++ b/text_cmds/ee/Changes
@@ -0,0 +1,40 @@
+version 1.5.0 (2/16/2009)
+- added display of line number, column, and lines from top to separator line
+ for info window
+- minor changes to reduce number of warnings when using -pedantic option
+
+version 1.4.7 (2/10/2009)
+- changed how strings are terminated from the old usage of NULL to the current
+ use of character zero, '\0'
+- changed the licensing since the Artistic License is now considered
+ restrictive
+
+version 1.4.6
+- modified new_curse.c to handle different subdirectory naming in terminfo
+ directory; first noted on Mac OS 10.2
+
+version 1.4.5a (12/23/2001)
+- modified get_options to be cleaner for arg handling
+
+version 1.4.5 (12/15/2001)
+- made changes to check usage of arguments provided so that if a file is
+ specified options are no longer accepted (that is, they are treated as file
+ names)
+- changed to use ee_version.h to allow changing version number without need
+ to change ee.c directly
+
+version 1.4.4 (8/17/2001)
+- added code to check if the parent process has died, and if so to exit
+ gracefully
+
+version 1.4.3 (6/25/2001)
+- modified create.make and new_curse.c to allow defining TERMCAP file
+ location (since some distributions move the file)
+- source directory now has version number attached to directory name
+
+version 1.4.2 (1/19/2001)
+- change to create.make script to add unistd.h to files to search for
+ select() declaration
+- change to new_curse.c for proper raw mode operation
+
+
diff --git a/text_cmds/ee/Makefile b/text_cmds/ee/Makefile
new file mode 100644
index 0000000..a6525ea
--- /dev/null
+++ b/text_cmds/ee/Makefile
@@ -0,0 +1,29 @@
+# This is the make file for ee, the "easy editor".
+#
+# A file called 'make.local' will be generated which will contain information
+# specific to the local system, such as if it is a BSD or System V based
+# version of UNIX, whether or not it has catgets, or select.
+#
+# The "install" target ("make install") will copy the ee binary to
+# the /usr/local/bin directory on the local system. The man page (ee.1)
+# will be copied into the /usr/local/man/man1 directory.
+#
+# The "clean" target ("make clean") will remove the ee and new_curse.o
+# object files, and the ee binary.
+#
+
+all : localmake buildee
+
+buildee :
+ make -f make.local
+
+localmake:
+ @./create.make
+
+install :
+ cp ee /usr/local/bin/ee
+ cp ee.1 /usr/local/man/man1/ee.1
+
+clean :
+ rm -f ee.o new_curse.o ee
+
diff --git a/text_cmds/ee/README.ee b/text_cmds/ee/README.ee
new file mode 100644
index 0000000..bbb932f
--- /dev/null
+++ b/text_cmds/ee/README.ee
@@ -0,0 +1,119 @@
+Copyright (c) 2009, Hugh Mahon
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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 COPYRIGHT HOLDERS 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
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+The editor 'ee' (easy editor) is intended to be a simple, easy to use
+terminal-based screen oriented editor that requires no instruction to
+use. Its primary use would be for people who are new to computers, or who
+use computers only for things like e-mail.
+
+ee's simplified interface is highlighted by the use of pop-up menus which
+make it possible for users to carry out tasks without the need to
+remember commands. An information window at the top of the screen shows
+the user the operations available with control-keys.
+
+ee allows users to use full eight-bit characters. If the host system has
+the capabilities, ee can use message catalogs, which would allow users to
+translate the message catalog into other languages which use eight-bit
+characters. See the file ee.i18n.guide for more details.
+
+ee relies on the virtual memory abilities of the platform it is running on
+and does not have its own memory management capabilities.
+
+I am releasing ee because I hate to see new users and non-computer types
+get frustrated by vi, and would like to see more intuitive interfaces for
+basic tools (both character-based and graphical) become more pervasive.
+Terminal capabilities and communication speeds have evolved considerably
+since the time in which vi's interface was created, allowing much more
+intuitive interfaces to be used. Since character-based I/O won't be
+completely replaced by graphical user interfaces for at least a few more
+years, I'd like to do what I can to make using computers with less
+glamorous interfaces as easy to use as possible. If terminal interfaces
+are still used in ten years, I hope neophytes won't still be stuck with
+only vi.
+
+For a text editor to be easy to use requires a certain set of abilities. In
+order for ee to work, a terminal must have the ability to position the cursor
+on the screen, and should have arrow keys that send unique sequences
+(multiple characters, the first character is an "escape", octal code
+'\033'). All of this information needs to be in a database called "terminfo"
+(System V implementations) or "termcap" (usually used for BSD systems). In
+case the arrow keys do not transmit unique sequences, motion operations are
+mapped to control keys as well, but this at least partially defeats the
+purpose. The curses package is used to handle the I/O which deals with the
+terminal's capabilities.
+
+While ee is based on curses, I have included here the source code to
+new_curse, a subset of curses developed for use with ee. 'curses' often
+will have a defect that reduces the usefulness of the editor relying upon
+it.
+
+The file new_curse.c contains a subset of 'curses', a package for
+applications to use to handle screen output. Unfortunately, curses
+varies from system to system, so I developed new_curse to provide
+consistent behavior across systems. It works on both SystemV and BSD
+systems, and while it can sometimes be slower than other curses packages,
+it will get the information on the screen painted correctly more often
+than vendor supplied curses. Unless problems occur during the building
+of ee, it is recommended that you use new_curse rather than the curses
+supplied with your system.
+
+If you experience problems with data being displayed improperly, check
+your terminal configuration, especially if you're using a terminal
+emulator, and make sure that you are using the right terminfo entry
+before rummaging through code. Terminfo entries often contain
+inaccuracies, or incomplete information, or may not totally match the
+terminal or emulator the terminal information is being used with.
+Complaints that ee isn't working quite right often end up being something
+else (like the terminal emulator being used).
+
+Both ee and new_curse were developed using K&R C (also known as "classic
+C"), but it can also be compiled with ANSI C. You should be able to
+build ee by simply typing "make". A make file which takes into account
+the characteristics of your system will be created, and then ee will be
+built. If there are problems encountered, you will be notified about
+them.
+
+ee is the result of several conflicting design goals. While I know that it
+solves the problems of some users, I also have no doubt that some will decry
+its lack of more features. I will settle for knowing that ee does fulfill
+the needs of a minority (but still large number) of users. The goals of ee
+are:
+
+ 1. To be so easy to use as to require no instruction.
+ 2. To be easy to compile and, if necessary, port to new platforms
+ by people with relatively little knowledge of C and UNIX.
+ 3. To have a minimum number of files to be dealt with, for compile
+ and installation.
+ 4. To have enough functionality to be useful to a large number of
+ people.
+
+Hugh Mahon |___|
+hugh4242@yahoo.com | |
+ |\ /|
+ | \/ |
+
diff --git a/text_cmds/ee/create.make b/text_cmds/ee/create.make
new file mode 100755
index 0000000..5d6ec30
--- /dev/null
+++ b/text_cmds/ee/create.make
@@ -0,0 +1,292 @@
+#!/bin/sh
+
+#
+# This script will determine if the system is a System V or BSD based
+# UNIX system and create a makefile for ee appropriate for the system.
+#
+# $Header: /home/hugh/sources/old_ae/RCS/create.make,v 1.13 2002/09/23 04:18:13 hugh Exp $
+#
+
+#set -x
+
+name_string="`uname`"
+
+# test for existence of termcap (exists on both BSD and SysV systems)
+
+if [ -f /etc/termcap -o -f /usr/share/lib/termcap -o -f /usr/share/misc/termcap ]
+then
+ if [ -f /usr/share/lib/termcap ]
+ then
+ termcap_exists="-DTERMCAP=\"\\\"/usr/share/lib/termcap\\\"\""
+ elif [ -f /usr/share/misc/termcap ]
+ then
+ termcap_exists="-DTERMCAP=\"\\\"/usr/share/misc/termcap\\\"\""
+ elif [ -f /etc/termcap ]
+ then
+ termcap_exists="-DTERMCAP=\"\\\"/etc/termcap\\\"\""
+ fi
+else
+ termcap_exists=""
+fi
+
+# test for terminfo directory (exists on SysV systems)
+
+if [ -d /usr/lib/terminfo -o -d /usr/share/lib/terminfo -o -d /usr/share/terminfo ]
+then
+ terminfo_exists=""
+else
+ terminfo_exists="-DCAP"
+fi
+
+# test for existence of termio header (on SysV systems)
+
+if [ -f /usr/include/termio.h ]
+then
+ termio="-DSYS5"
+else
+ termio=""
+fi
+
+# test for sgtty header (on BSD systems)
+
+if [ -f /usr/include/sgtty.h ]
+then
+ sgtty="TRUE"
+else
+ sgtty=""
+fi
+
+# look for select call in headers, make sure headers exist
+
+HEADER_FILES=""
+
+if [ -f /usr/include/sys/time.h ]
+then
+ HEADER_FILES="/usr/include/sys/time.h "
+fi
+
+if [ -f /usr/include/sys/types.h ]
+then
+ HEADER_FILES="$HEADER_FILES /usr/include/sys/types.h"
+fi
+
+# check for unistd.h
+
+if [ -f /usr/include/unistd.h ]
+then
+ HAS_UNISTD=-DHAS_UNISTD
+ HEADER_FILES="$HEADER_FILES /usr/include/unistd.h"
+else
+ HAS_UNISTD=""
+fi
+
+if [ -n "$HEADER_FILES" ]
+then
+ string="`grep select $HEADER_FILES`"
+ if [ -n "$string" ]
+ then
+ BSD_SELECT="-DBSD_SELECT"
+ else
+ BSD_SELECT=""
+ fi
+fi
+
+# check for existence of select.h (on AIX)
+
+if [ -f /usr/include/sys/select.h ]
+then
+ select_hdr="-DSLCT_HDR"
+else
+ select_hdr=""
+fi
+
+# check for stdlib.h
+
+if [ -f /usr/include/stdlib.h ]
+then
+ HAS_STDLIB=-DHAS_STDLIB
+else
+ HAS_STDLIB=""
+fi
+
+# check for stdarg.h
+
+if [ -f /usr/include/stdarg.h ]
+then
+ HAS_STDARG=-DHAS_STDARG
+else
+ HAS_STDARG=""
+fi
+
+# check for ctype.h
+
+if [ -f /usr/include/ctype.h ]
+then
+ HAS_CTYPE=-DHAS_CTYPE
+else
+ HAS_CTYPE=""
+fi
+
+# check for sys/ioctl.h
+
+if [ -f /usr/include/sys/ioctl.h ]
+then
+ HAS_SYS_IOCTL=-DHAS_SYS_IOCTL
+else
+ HAS_SYS_IOCTL=""
+fi
+
+# check for sys/wait.h
+
+if [ -f /usr/include/sys/wait.h ]
+then
+ HAS_SYS_WAIT=-DHAS_SYS_WAIT
+else
+ HAS_SYS_WAIT=""
+fi
+
+# check for localization headers
+
+if [ -f /usr/include/locale.h -a -f /usr/include/nl_types.h ]
+then
+ catgets=""
+else
+ catgets="-DNO_CATGETS"
+fi
+
+# make decisions about use of new_curse.c (use of new_curse is recommended
+# rather than local curses)
+
+if [ -n "$terminfo_exists" -a -z "$termcap_exists" ]
+then
+ echo "Neither terminfo or termcap are on this system! "
+ if [ -f /usr/include/curses.h ]
+ then
+ echo "Relying on local curses implementation."
+ else
+ cat <<-EOF
+ Don't know where to find curses, you'll need to modify
+ source code to be able to build!
+
+ Modify the file make.default and build ee by typing:
+
+ make -f make.default
+
+ EOF
+
+ exit 1
+ fi
+
+ TARGET="curses"
+ curses=""
+else
+ curses="-DNCURSE"
+ TARGET="ee"
+fi
+
+if [ -z "$termio" -a -z "$sgtty" ]
+then
+ echo "Neither termio.h or sgtty.h are on this system! "
+ if [ -f /usr/include/curses.h ]
+ then
+ echo "Relying on local curses implementation."
+ else
+ cat <<-EOF
+ Don't know where to find curses, you'll need to modify
+ source code to be able to build!
+
+ Modify the file make.default and build ee by typing:
+
+ make -f make.default
+
+ EOF
+
+ exit 1
+ fi
+
+ TARGET="curses"
+ curses=""
+fi
+
+# check if this is a SunOS system
+
+if [ -d /usr/5include ]
+then
+ five_include="-I/usr/5include"
+else
+ five_include=""
+fi
+
+if [ -d /usr/5lib ]
+then
+ five_lib="-L/usr/5lib"
+else
+ five_lib=""
+fi
+
+
+if [ "$name_string" = "Darwin" ]
+then
+ if [ -n "$CFLAGS" ]
+ then
+ other_cflags="${CFLAGS} -DNO_CATGETS"
+ else
+ other_cflags="-DNO_CATGETS"
+ fi
+else
+
+ if [ -n "$CFLAGS" ]
+ then
+ if [ -z "`echo $CFLAGS | grep '[-]g'`" ]
+ then
+ other_cflags="${CFLAGS} -s"
+ else
+ other_cflags="${CFLAGS}"
+ fi
+ else
+ other_cflags="-s"
+ fi
+fi
+
+# time to write the makefile
+
+echo "Generating make.local"
+
+if [ -f make.local ]
+then
+ mv make.local make.lcl.old
+fi
+
+echo "DEFINES = $termio $terminfo_exists $BSD_SELECT $catgets $select $curses " > make.local
+echo "" >> make.local
+echo "CFLAGS = $HAS_UNISTD $HAS_STDARG $HAS_STDLIB $HAS_CTYPE $HAS_SYS_IOCTL $HAS_SYS_WAIT $five_lib $five_include $select_hdr $other_cflags $termcap_exists" >> make.local
+echo "" >> make.local
+echo "" >> make.local
+echo "all : $TARGET" >> make.local
+
+cat >> make.local << EOF
+
+curses : ee.c
+ cc ee.c -o ee \$(CFLAGS) -lcurses
+
+ee : ee.o new_curse.o
+ cc -o ee ee.o new_curse.o \$(CFLAGS)
+
+ee.o : ee.c new_curse.h
+ cc -c ee.c \$(DEFINES) \$(CFLAGS)
+
+new_curse.o : new_curse.c new_curse.h
+ cc new_curse.c -c \$(DEFINES) \$(CFLAGS)
+
+EOF
+
+if [ -f make.lcl.old ]
+then
+ diffs="`cmp make.lcl.old make.local`"
+ if [ -n "${diffs}" ]
+ then
+ rm -f ee.o new_curse.o ee
+ fi
+ rm -f make.lcl.old
+fi
+
diff --git a/text_cmds/ee/ee.1 b/text_cmds/ee/ee.1
new file mode 100644
index 0000000..d6558a1
--- /dev/null
+++ b/text_cmds/ee/ee.1
@@ -0,0 +1,543 @@
+.\"
+.\"
+.\" To format this reference page, use the command:
+.\"
+.\" nroff -man ee.1
+.\"
+.\" $Header: /home/hugh/sources/old_ae/RCS/ee.1,v 1.22 2001/12/16 04:49:27 hugh Exp $
+.\"
+.\"
+.TH ee 1 "" "" ""
+.SH NAME
+ee \- easy editor
+.SH SYNOPSIS
+.nf
+ee [-e] [-i] [-h] [+#] [\fIfile\fR ...]
+ree [-e] [-i] [-h] [+#] [\fIfile\fR ...]
+.ta
+.fi
+.ad b
+.SH DESCRIPTION
+The command
+.I ee
+is a simple screen oriented text editor. It is always in text insertion
+mode unless there is a prompt at the bottom of the terminal, or a
+menu present (in a box in the middle of the terminal). The command
+.I ree
+is the same as
+.I ee,
+but restricted to editing the named
+file (no file operations, or shell escapes are allowed).
+.PP
+An editor with similar user-friendly qualities but more features is available
+and is called
+.I aee.
+.PP
+For
+.I ee
+to work properly, the environment variable
+.SM TERM
+must be set to indicate the type of terminal being used. For
+example, for an
+.SM HP 700/92
+terminal, the
+.SM TERM
+variable should be set to "70092". See your System Administrator if
+you need more information.
+.\"
+.\" options
+.\"
+.SS Options
+The following options are available from the command line:
+.PP
+.TP 4
+.B -e
+Turns off expansion of tab character to spaces.
+.TP
+.B -i
+Turns off display of information window at top of terminal.
+.TP
+.B -h
+Turns off highlighting of borders of windows and menus (improves
+performance on some terminals).
+.TP
+.B +#
+Moves the cursor to line '#' at startup.
+.br
+.\"
+.\" control keys
+.\"
+.SS "Control keys"
+To do anything other than insert text, the user must use the control
+keys (the
+.B Control
+key, represented by a "^", pressed in conjunction with an
+alphabetic key, e.g., ^a) and function keys available on the keyboard
+(such as
+.BR "Next Page" ", " "Prev Page" ,
+arrow keys, etc.).
+.PP
+Since not all terminals have function keys,
+.I ee
+has the basic cursor movement functions assigned to control keys as
+well as more intuitive keys on the keyboard when available. For
+instance, to move the cursor up, the user can use the up arrow key,
+or
+.BR ^u .
+.RS 4
+.nf
+.ta 1.4i
+.sp
+^a Prompt for the decimal value of a character to insert.
+^b Move to the bottom of the text.
+^c Get the prompt for a command.
+^d Move the cursor down.
+^e Prompt for the string to search for.
+^f Undelete the last deleted character.
+^g Move to the beginning of the line.
+^h Backspace.
+^i Tab.
+^j Insert a newline.
+^k Delete the character the cursor is sitting on.
+^l Move the cursor left.
+^m Insert a newline.
+^n Move to the next page.
+^o Move to the end of the line.
+^p Move to the previous page.
+^r Move the cursor to the right.
+^t Move to the top of the text.
+^u Move the cursor up.
+^v Undelete the last deleted word.
+^w Delete the word beginning at the cursor position.
+^x Search.
+^y Delete from the cursor position to the end of line.
+^z Undelete the last deleted line.
+^[ (ESC) Pop up menu.
+.ta
+.fi
+.RE
+.sp
+.SS "EMACS keys mode"
+.PP
+Since many shells provide an Emacs mode (for cursor movement and other editing
+operations), some bindings that may be more useful for people familiar with
+those bindings have been provided. These are accessible via the
+.B settings
+menu, or via the initialization file (see below). The mappings are as follows:
+.RS
+.nf
+.ta 1.4i
+^a Move to the beginning of the line.
+^b Back 1 character.
+^c Command prompt.
+^d Delete character the cursor is sitting on.
+^e End of line.
+^f Forward 1 character.
+^g Go back 1 page.
+^h Backspace.
+^i Tab.
+^j Undelete last deleted character.
+^k Delete line.
+^l Undelete last deleted line.
+^m Insert a newline.
+^n Move to the next line.
+^o Prompt for the decimal value of a character to insert.
+^p Previous line.
+^r Restore last deleted word.
+^t Move to the top of the text.
+^u Move to the bottom of the text.
+^v Move to the next page.
+^w Delete the word beginning at the cursor position.
+^y Prompt for the string to search for.
+^z Next word.
+^[ (ESC) Pop up menu.
+.ta
+.fi
+.RE
+.sp
+.\"
+.\" function keys
+.\"
+.SS "Function Keys"
+.RS 4
+.IP "\fBNext Page\fR"
+Move to the next page.
+.IP "\fBPrev Page\fR"
+Move to the previous page.
+.IP "\fBDelete Char\fR"
+Delete the character the cursor is on.
+.IP "\fBDelete Line\fR"
+Delete from the cursor to the end of line.
+.IP "\fBInsert line\fR"
+Insert a newline at the cursor position.
+.IP "\fBArrow keys\fR"
+Move the cursor in the direction indicated.
+.RE
+.\"
+.\" commands
+.\"
+.SS Commands
+.PP
+Some operations require more information than a single keystroke can
+provide. For the most basic operations, there is a menu that can be
+obtained by pressing the
+.SM \fBESC\fR
+key. The same operations, and more can be performed by obtaining the
+command prompt (^c) and typing in one of the commands below.
+.RS 4
+.IP "!\fBcmd\fR"
+Execute \fBcmd\fR in a shell.
+.IP "\fB0-9\fR"
+Move to the line indicated.
+.IP "\fBcase\fR"
+Make searches case sensitive.
+.IP "\fBcharacter\fR"
+Display the ascii value of the character at the cursor.
+.IP "\fBexit\fR"
+Save the edited text, and leave the editor.
+.IP "\fBexpand\fR"
+Expand tabs to spaces.
+.IP "\fBfile\fR"
+Print the name of the file.
+.IP "\fBhelp\fR"
+Display help screen.
+.IP "\fBline\fR"
+Display the current line number.
+.IP "\fBnocase\fR
+Make searches insensitive to case (the default).
+.IP "\fBnoexpand\fR"
+Don't expand tab to spaces when the TAB key is pressed.
+.IP "\fBquit\fR"
+Leave the editor without saving changes.
+.IP "\fBread\fR \fIfile\fR"
+Read the named \fIfile\fR.
+.IP "\fBwrite\fR \fIfile\fR"
+Write the text to the named \fIfile\fR.
+.RE
+.\"
+.\" menu operations
+.\"
+.SS "Menu Operations"
+.PP
+Pop-up menus can be obtained by pressing the
+.B escape
+key (or
+.B ^[
+if no
+.B escape
+key is present). When in the menu, the escape key can be
+used to leave the menu without performing any operations. Use the up and
+down arrow keys, or
+.B ^u
+for moving up and
+.B ^d
+for moving down to move to the desired items in the menu, then press
+.B return
+to perform the indicated task.
+.PP
+To the left of each menu item is a letter, which if the corresponding
+letter is pressed on the keyboard selects that menu entry.
+.PP
+The main menu in \fIee\fR is as follows:
+.RS 4
+.IP "\fBleave editor\fR"
+If changes have been made, the user will get a menu prompting whether or
+not the changes should be saved.
+.IP "\fBhelp\fR"
+Displays a help screen, with all of the keyboard operations and commands.
+.IP "\fBfile operations\fR"
+Pops up a menu for selecting whether to read a file, write to a file, or
+save the current contents of the editor, as well as send the contents of
+the editor to a print command (see the section \fBInitializing ee from a
+file\fR).
+.IP "\fBredraw screen\fR"
+Provides a means to repaint the screen if the screen has been corrupted.
+.IP "\fBsettings\fR"
+Shows the current values of the operating modes, and right margin. By
+pressing return when the cursor is on a particular item, the value can be
+changed. To leave this menu, press the \fBescape\fR key. (See \fBModes\fR
+below.)
+.IP "\fBsearch\fR"
+.br
+Pops up a menu in which the user may choose to enter a string to search
+for, or search for a string already entered.
+.IP "\fBmiscellaneous\fR"
+Pops up a menu that allows the user to format the current paragraph,
+execute a shell command, or check the spelling of the text in the editor.
+.RE
+.\"
+.\" paragraph formatting
+.\"
+.SS "Paragraph Formatting"
+.PP
+Paragraphs are defined for \fIee\fR by a block of text bounded by:
+.sp
+.RS 8
+.IP \(bu
+Begin or end of file.
+.IP \(bu
+Line with no characters, or only spaces and/or tabs.
+.IP \(bu
+Line starting with a period ('.') or right angle bracket ('>').
+.RE
+.PP
+A paragraph may be formatted two ways: explicitly by choosing the
+\fBformat paragraph\fR menu item, or by setting \fIee\fR to automatically
+format paragraphs. The automatic mode may be set via a menu, or via the
+initialization file.
+.PP
+There are three states for text operation in \fIee\fR: free-form, margins,
+and automatic formatting.
+.PP
+"Free-form" is best used for things like programming. There are no
+restrictions on the length of lines, and no formatting takes place.
+.PP
+"Margins" allows the user to type in text without having to worry about going
+beyond the right margin (the right margin may be set in the \fBsettings\fR
+menu, the default is for the margin to be the right edge of the
+terminal). This is the mode that allows the \fBformat paragraph\fR menu
+item to work.
+.PP
+"Automatic formatting" provides word-processor-like behavior. The user
+may type in text, while \fIee\fR will make sure the entire paragraph fits
+within the width of the terminal every time the user inserts a space after
+typing or deleting text. Margin observation must also be enabled in order for
+automatic formatting to occur.
+.\"
+.\" modes
+.\"
+.SS Modes
+.PP
+Although ee is a 'modeless' editor (it is in text insertion mode all the
+time), there are modes in some of the things it does. These include:
+.RS 4
+.IP "\fBtab expansion\fR"
+Tabs may be inserted as a single tab character, or replaced with spaces.
+.IP "\fBcase sensitivity\fR"
+The search operation can be sensitive to whether characters are upper- or
+lower-case, or ignore case completely.
+.IP "\fBmargins observed\fR"
+Lines can either be truncated at the right margin, or extend on forever.
+.IP "\fBauto paragraph formatting\fR"
+While typing in text, the editor can try to keep it looking reasonably well
+within the width of the screen.
+.IP "\fBeightbit characters\fR"
+Toggles whether eight bit characters are displayed as their value in angle
+brackets (e.g. "<220>") or as a character.
+.IP "\fBinfo window\fR"
+A window showing the keyboard operations that can be performed can be
+displayed or not.
+.IP "\fBemacs keys\fR"
+Control keys may be given bindings similar to emacs, or not.
+.IP "\fB16 bit characters\fR"
+Toggles whether sixteen bit characters are handled as one 16-bit quantity or
+two 8-bit quantities. This works primarily with the Chinese Big 5 code set.
+.RE
+.PP
+You may set these modes via the initialization file (see below), or with a
+menu (see above).
+.\"
+.\" spell checking
+.\"
+.SS "Spell Checking"
+.PP
+There are two ways to have the spelling in the text checked from \fIee\fR.
+One is by the traditional \fIspell\fR(1) command, the other is with the
+optional \fIispell\fR(1) command.
+.PP
+Using \fIspell\fR, the words that are not recognized will be placed at the top
+of the file. For the \fIispell\fR option, the file is written to disk,
+then \fIispell\fR run on the file, and the file read back in once
+\fIispell\fR has completed making changes to the file.
+.\"
+.\" printing
+.\"
+.SS "Printing the contents of the editor"
+.PP
+The user may select a menu item which prints the contents of the editor.
+.I ee
+pipes the text in the editor to the command specified by the
+initialization command
+.B printcommand
+(see the section
+.B Initializing ee from a file
+below). The default is to send the contents to "lp".
+.PP
+Whatever the user assigns to
+.B printcommand
+must take input from
+standard input. See your system administrator for more details.
+.\"
+.\" shell operations
+.\"
+.SS "Shell operations"
+.PP
+Shell commands can be executed from within
+.I ee
+by selecting the
+.B shell command
+item in the
+.B miscellaneous
+menu, or by placing an exclamation mark ("!") before the command to
+execute at the
+.B command:
+prompt. Additionally, the user may direct the contents of the edit buffer
+out to a shell operation (via a pipe) by using the left angle bracket
+(">"), followed by a "!" and the shell command to execute. The output of
+a shell operation can also be directed into the edit buffer by using a
+right angle bracket ("<") before the exclamation mark. These can even be
+used together to send output to a shell operation and read back the
+results into the editor. So, if the editor contained a list of words
+to be sorted, they could be sorted by typing the following at the command
+prompt:
+.RS 4
+.sp
+><!sort
+.sp
+.RE
+This would send the contents of the editor to be piped into the
+.I sort
+utility and the result would be placed into the edit buffer at the current
+cursor location. The old information would have to be deleted by the user.
+.\"
+.\" initializing ee from a file
+.\"
+.SS "Initializing ee from a file"
+.PP
+Since different users have different preferences, \fIee\fR allows some
+slight configurability. There are three possible locations for an
+initialization file for ee: the file \fI/usr/share/misc/init.ee\fR, the
+file \fI.init.ee\fR in the user's home directory, or the file \fI.init.ee\fR
+in the current directory (if different from the home
+directory). This allows system administrators to set some preferences for
+the users on a system-wide basis (for example, the \fBprint\fR command),
+and the user to customize settings for particular directories (like one
+for correspondence, and a different directory for programming).
+.PP
+The file \fI\/usr/share/misc/init.ee\fR is read first, then
+\fI$HOME/.init.ee\fR, then \fI.init.ee\fR, with the settings specified by the
+most recent file read taking precedence.
+.PP
+The following items may be entered in the initialization file:
+.RS 4
+.IP \fBcase\fR
+Sets searches to be case sensitive.
+.IP \fBnocase\fR
+Sets searches to be insensitive to case (default).
+.IP \fBexpand\fR
+Causes \fIee\fR to expand tabs to spaces (default).
+.IP \fBnoexpand\fR
+Causes \fIee\fR to insert tabs as a single character.
+.IP \fBinfo\fR
+A small information window is displayed at the top of the terminal
+(default).
+.IP \fBnoinfo\fR
+Turns off the display of the information window.
+.IP \fBmargins\fR
+Causes \fIee\fR to truncate lines at the right margin when the
+cursor passes beyond the right margin as set by the user
+while text is being inserted
+(default).
+.IP \fBnomargins\fR
+Allows lines to extend beyond the right margin.
+.IP \fBautoformat\fR
+Causes \fIee\fR to automatically try to format the current paragraph while
+text insertion is occurring.
+.IP \fBnoautoformat\fR
+Turns off automatic paragraph formatting (default).
+.IP \fBprintcommand\fR
+Allows the setting of the print command (default: "lp").
+.IP \fBrightmargin\fR
+The user can select a value for the right margin (the first column on the
+screen is zero).
+.IP \fBhighlight\fR
+Turns on highlighting border of information window and menus (default).
+.IP \fBnohighlight\fR
+Turns off highlighting of border of information window and menus.
+.IP \fBeightbit\fR
+Turns on display of eight bit characters.
+.IP \fBnoeightbit\fR
+Turns off display of eight bit characters (they are displayed as their decimal
+value inside angle brackets, e.g., "<220>").
+.IP \fB16bit\fR
+Turns on handling of 16-bit characters.
+.IP \fBno16bit\fR
+Turns off handling of 16-bit characters.
+.IP \fBemacs\fR
+Turns on emacs key bindings.
+.IP \fBnoemacs\fR
+Turns off emacs key bindings.
+.RE
+.\"
+.\" save editor configuration
+.\"
+.SS "Save Editor Configuration"
+.PP
+When using this entry from the
+.B settings
+menu, the user may choose to save the current configuration of
+the editor (see \fBInitializing ee from a
+file\fR above) to a file named
+.I .init.ee
+in the current directory or the user's home directory. If a file named
+.I .init.ee
+already exists, it will be renamed
+.IR .init.ee.old .
+.\"
+.\" Caveats
+.\"
+.SH CAVEATS
+.PP
+THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. Neither
+Hewlett-Packard nor Hugh Mahon shall be liable
+for errors contained herein, nor for
+incidental or consequential damages in
+connection with the furnishing, performance or
+use of this material. Neither Hewlett-Packard
+nor Hugh Mahon assumes any responsibility for
+the use or reliability of this software or
+documentation. This software and
+documentation is totally UNSUPPORTED. There
+is no support contract available. Hewlett-Packard
+has done NO Quality Assurance on ANY
+of the program or documentation. You may find
+the quality of the materials inferior to
+supported materials.
+.PP
+Always make a copy of files that cannot be easily reproduced before
+editing. Save files early, and save often.
+.SS "International Code Set Support"
+.I ee
+supports single-byte character code sets (eight-bit clean), or the
+Chinese Big-5 code set. (Other multi-byte code sets may function, but the
+reason Big-5 works is that a two-byte character also takes up two columns on
+the screen.)
+.SH WARNINGS
+The automatic paragraph formatting operation
+may be too slow for slower systems.
+.SH FILES
+.PP
+.I /usr/share/misc/init.ee
+.br
+.I $HOME/.init.ee
+.br
+.I .init.ee
+.SH AUTHOR
+.PP
+The software
+.I ee
+was developed by Hugh Mahon.
+.PP
+This software and documentation contains
+proprietary information which is protected by
+copyright. All rights are reserved.
+.PP
+Copyright (c) 1990, 1991, 1992, 1993, 1995, 1996, 2001 Hugh Mahon.
+.SH "SEE ALSO"
+.PP
+termcap(4), terminfo(4), environ(5), spell(1), ispell(1), lp(1), aee(1)
+
diff --git a/text_cmds/ee/ee.c b/text_cmds/ee/ee.c
new file mode 100644
index 0000000..70a4407
--- /dev/null
+++ b/text_cmds/ee/ee.c
@@ -0,0 +1,5348 @@
+/*
+ | ee (easy editor)
+ |
+ | An easy to use, simple screen oriented editor.
+ |
+ | written by Hugh Mahon
+ |
+ |
+ | Copyright (c) 2009, Hugh Mahon
+ | All rights reserved.
+ |
+ | Redistribution and use in source and binary forms, with or without
+ | modification, are permitted provided that the following conditions
+ | are met:
+ |
+ | * Redistributions of source code must retain the above copyright
+ | notice, this list of conditions and the following disclaimer.
+ | * 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 COPYRIGHT HOLDERS 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
+ | COPYRIGHT OWNER 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.
+ |
+ | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ |
+ | This editor was purposely developed to be simple, both in
+ | interface and implementation. This editor was developed to
+ | address a specific audience: the user who is new to computers
+ | (especially UNIX).
+ |
+ | ee is not aimed at technical users; for that reason more
+ | complex features were intentionally left out. In addition,
+ | ee is intended to be compiled by people with little computer
+ | experience, which means that it needs to be small, relatively
+ | simple in implementation, and portable.
+ |
+ | This software and documentation contains
+ | proprietary information which is protected by
+ | copyright. All rights are reserved.
+ |
+ | $Header: /home/hugh/sources/old_ae/RCS/ee.c,v 1.104 2010/06/04 01:55:31 hugh Exp hugh $
+ |
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+char *ee_copyright_message =
+"Copyright (c) 1986, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 2009 Hugh Mahon ";
+
+#include "ee_version.h"
+
+char *version = "@(#) ee, version " EE_VERSION " $Revision: 1.104 $";
+
+#ifdef NCURSE
+#include "new_curse.h"
+#elif HAS_NCURSES
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+
+#include <ctype.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <locale.h>
+
+#ifdef HAS_SYS_WAIT
+#include <sys/wait.h>
+#endif
+
+#ifdef HAS_STDLIB
+#include <stdlib.h>
+#endif
+
+#ifdef HAS_STDARG
+#include <stdarg.h>
+#endif
+
+#ifdef HAS_UNISTD
+#include <unistd.h>
+#endif
+
+#ifndef NO_CATGETS
+#include <nl_types.h>
+
+nl_catd catalog;
+#else
+#define catgetlocal(a, b) (b)
+#endif /* NO_CATGETS */
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+#define TAB 9
+#define max(a, b) (a > b ? a : b)
+#define min(a, b) (a < b ? a : b)
+
+/*
+ | defines for type of data to show in info window
+ */
+
+#define CONTROL_KEYS 1
+#define COMMANDS 2
+
+struct text {
+ unsigned char *line; /* line of characters */
+ int line_number; /* line number */
+ int line_length; /* actual number of characters in the line */
+ int max_length; /* maximum number of characters the line handles */
+ struct text *next_line; /* next line of text */
+ struct text *prev_line; /* previous line of text */
+ };
+
+struct text *first_line; /* first line of current buffer */
+struct text *dlt_line; /* structure for info on deleted line */
+struct text *curr_line; /* current line cursor is on */
+struct text *tmp_line; /* temporary line pointer */
+struct text *srch_line; /* temporary pointer for search routine */
+
+struct files { /* structure to store names of files to be edited*/
+ unsigned char *name; /* name of file */
+ struct files *next_name;
+ };
+
+struct files *top_of_stack = NULL;
+
+int d_wrd_len; /* length of deleted word */
+int position; /* offset in bytes from begin of line */
+int scr_pos; /* horizontal position */
+int scr_vert; /* vertical position on screen */
+int scr_horz; /* horizontal position on screen */
+int absolute_lin; /* number of lines from top */
+int tmp_vert, tmp_horz;
+int input_file; /* indicate to read input file */
+int recv_file; /* indicate reading a file */
+int edit; /* continue executing while true */
+int gold; /* 'gold' function key pressed */
+int fildes; /* file descriptor */
+int case_sen; /* case sensitive search flag */
+int last_line; /* last line for text display */
+int last_col; /* last column for text display */
+int horiz_offset = 0; /* offset from left edge of text */
+int clear_com_win; /* flag to indicate com_win needs clearing */
+int text_changes = FALSE; /* indicate changes have been made to text */
+int get_fd; /* file descriptor for reading a file */
+int info_window = TRUE; /* flag to indicate if help window visible */
+int info_type = CONTROL_KEYS; /* flag to indicate type of info to display */
+int expand_tabs = TRUE; /* flag for expanding tabs */
+int right_margin = 0; /* the right margin */
+int observ_margins = TRUE; /* flag for whether margins are observed */
+int shell_fork;
+int temp_stdin; /* temporary storage for stdin */
+int temp_stdout; /* temp storage for stdout descriptor */
+int temp_stderr; /* temp storage for stderr descriptor */
+int pipe_out[2]; /* pipe file desc for output */
+int pipe_in[2]; /* pipe file descriptors for input */
+int out_pipe; /* flag that info is piped out */
+int in_pipe; /* flag that info is piped in */
+int formatted = FALSE; /* flag indicating paragraph formatted */
+int auto_format = FALSE; /* flag for auto_format mode */
+int restricted = FALSE; /* flag to indicate restricted mode */
+int nohighlight = FALSE; /* turns off highlighting */
+int eightbit = TRUE; /* eight bit character flag */
+int local_LINES = 0; /* copy of LINES, to detect when win resizes */
+int local_COLS = 0; /* copy of COLS, to detect when win resizes */
+int curses_initialized = FALSE; /* flag indicating if curses has been started*/
+int emacs_keys_mode = FALSE; /* mode for if emacs key binings are used */
+int ee_chinese = FALSE; /* allows handling of multi-byte characters */
+ /* by checking for high bit in a byte the */
+ /* code recognizes a two-byte character */
+ /* sequence */
+
+unsigned char *point; /* points to current position in line */
+unsigned char *srch_str; /* pointer for search string */
+unsigned char *u_srch_str; /* pointer to non-case sensitive search */
+unsigned char *srch_1; /* pointer to start of suspect string */
+unsigned char *srch_2; /* pointer to next character of string */
+unsigned char *srch_3;
+unsigned char *in_file_name = NULL; /* name of input file */
+char *tmp_file; /* temporary file name */
+unsigned char *d_char; /* deleted character */
+unsigned char *d_word; /* deleted word */
+unsigned char *d_line; /* deleted line */
+char in_string[513]; /* buffer for reading a file */
+unsigned char *print_command = (unsigned char *)"lpr"; /* string to use for the print command */
+unsigned char *start_at_line = NULL; /* move to this line at start of session*/
+int in; /* input character */
+
+FILE *temp_fp; /* temporary file pointer */
+FILE *bit_bucket; /* file pointer to /dev/null */
+
+char *table[] = {
+ "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "\t", "^J",
+ "^K", "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U",
+ "^V", "^W", "^X", "^Y", "^Z", "^[", "^\\", "^]", "^^", "^_"
+ };
+
+WINDOW *com_win;
+WINDOW *text_win;
+WINDOW *help_win;
+WINDOW *info_win;
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif
+
+
+/*
+ | The following structure allows menu items to be flexibly declared.
+ | The first item is the string describing the selection, the second
+ | is the address of the procedure to call when the item is selected,
+ | and the third is the argument for the procedure.
+ |
+ | For those systems with i18n, the string should be accompanied by a
+ | catalog number. The 'int *' should be replaced with 'void *' on
+ | systems with that type.
+ |
+ | The first menu item will be the title of the menu, with NULL
+ | parameters for the procedure and argument, followed by the menu items.
+ |
+ | If the procedure value is NULL, the menu item is displayed, but no
+ | procedure is called when the item is selected. The number of the
+ | item will be returned. If the third (argument) parameter is -1, no
+ | argument is given to the procedure when it is called.
+ */
+
+struct menu_entries {
+ char *item_string;
+ int (*procedure)P_((struct menu_entries *));
+ struct menu_entries *ptr_argument;
+ int (*iprocedure)P_((int));
+ void (*nprocedure)P_((void));
+ int argument;
+ };
+
+int main P_((int argc, char *argv[]));
+unsigned char *resiz_line P_((int factor, struct text *rline, int rpos));
+void insert P_((int character));
+void delete P_((int disp));
+void scanline P_((unsigned char *pos));
+int tabshift P_((int temp_int));
+int out_char P_((WINDOW *window, int character, int column));
+int len_char P_((int character, int column));
+void draw_line P_((int vertical, int horiz, unsigned char *ptr, int t_pos, int length));
+void insert_line P_((int disp));
+struct text *txtalloc P_((void));
+struct files *name_alloc P_((void));
+unsigned char *next_word P_((unsigned char *string));
+void prev_word P_((void));
+void control P_((void));
+void emacs_control P_((void));
+void bottom P_((void));
+void top P_((void));
+void nextline P_((void));
+void prevline P_((void));
+void left P_((int disp));
+void right P_((int disp));
+void find_pos P_((void));
+void up P_((void));
+void down P_((void));
+void function_key P_((void));
+void print_buffer P_((void));
+void command_prompt P_((void));
+void command P_((char *cmd_str1));
+int scan P_((char *line, int offset, int column));
+char *get_string P_((char *prompt, int advance));
+int compare P_((char *string1, char *string2, int sensitive));
+void goto_line P_((char *cmd_str));
+void midscreen P_((int line, unsigned char *pnt));
+void get_options P_((int numargs, char *arguments[]));
+void check_fp P_((void));
+void get_file P_((char *file_name));
+void get_line P_((int length, unsigned char *in_string, int *append));
+void draw_screen P_((void));
+void finish P_((void));
+int quit P_((int noverify));
+void edit_abort P_((int arg));
+void delete_text P_((void));
+int write_file P_((char *file_name, int warn_if_exists));
+int search P_((int display_message));
+void search_prompt P_((void));
+void del_char P_((void));
+void undel_char P_((void));
+void del_word P_((void));
+void undel_word P_((void));
+void del_line P_((void));
+void undel_line P_((void));
+void adv_word P_((void));
+void move_rel P_((int direction, int lines));
+void eol P_((void));
+void bol P_((void));
+void adv_line P_((void));
+void sh_command P_((char *string));
+void set_up_term P_((void));
+void resize_check P_((void));
+int menu_op P_((struct menu_entries *));
+void paint_menu P_((struct menu_entries menu_list[], int max_width, int max_height, int list_size, int top_offset, WINDOW *menu_win, int off_start, int vert_size));
+void help P_((void));
+void paint_info_win P_((void));
+void no_info_window P_((void));
+void create_info_window P_((void));
+int file_op P_((int arg));
+void shell_op P_((void));
+void leave_op P_((void));
+void redraw P_((void));
+int Blank_Line P_((struct text *test_line));
+void Format P_((void));
+void ee_init P_((void));
+void dump_ee_conf P_((void));
+void echo_string P_((char *string));
+void spell_op P_((void));
+void ispell_op P_((void));
+int first_word_len P_((struct text *test_line));
+void Auto_Format P_((void));
+void modes_op P_((void));
+char *is_in_string P_((char *string, char *substring));
+char *resolve_name P_((char *name));
+int restrict_mode P_((void));
+int unique_test P_((char *string, char *list[]));
+void strings_init P_((void));
+
+#undef P_
+/*
+ | allocate space here for the strings that will be in the menu
+ */
+
+struct menu_entries modes_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0}, /* title */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 1. tabs to spaces */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 2. case sensitive search*/
+ {"", NULL, NULL, NULL, NULL, -1}, /* 3. margins observed */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 4. auto-paragraph */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 5. eightbit characters*/
+ {"", NULL, NULL, NULL, NULL, -1}, /* 6. info window */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 7. emacs key bindings*/
+ {"", NULL, NULL, NULL, NULL, -1}, /* 8. right margin */
+ {"", NULL, NULL, NULL, NULL, -1}, /* 9. chinese text */
+ {"", NULL, NULL, NULL, dump_ee_conf, -1}, /* 10. save editor config */
+ {NULL, NULL, NULL, NULL, NULL, -1} /* terminator */
+ };
+
+char *mode_strings[11];
+
+#define NUM_MODES_ITEMS 10
+
+struct menu_entries config_dump_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries leave_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, finish, -1},
+ {"", NULL, NULL, quit, NULL, TRUE},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+#define READ_FILE 1
+#define WRITE_FILE 2
+#define SAVE_FILE 3
+
+struct menu_entries file_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, file_op, NULL, READ_FILE},
+ {"", NULL, NULL, file_op, NULL, WRITE_FILE},
+ {"", NULL, NULL, file_op, NULL, SAVE_FILE},
+ {"", NULL, NULL, NULL, print_buffer, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries search_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0},
+ {"", NULL, NULL, NULL, search_prompt, -1},
+ {"", NULL, NULL, search, NULL, TRUE},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries spell_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, spell_op, -1},
+ {"", NULL, NULL, NULL, ispell_op, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries misc_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, Format, -1},
+ {"", NULL, NULL, NULL, shell_op, -1},
+ {"", menu_op, spell_menu, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries main_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, leave_op, -1},
+ {"", NULL, NULL, NULL, help, -1},
+ {"", menu_op, file_menu, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, redraw, -1},
+ {"", NULL, NULL, NULL, modes_op, -1},
+ {"", menu_op, search_menu, NULL, NULL, -1},
+ {"", menu_op, misc_menu, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+char *help_text[23];
+char *control_keys[5];
+
+char *emacs_help_text[22];
+char *emacs_control_keys[5];
+
+char *command_strings[5];
+char *commands[32];
+char *init_strings[22];
+
+#define MENU_WARN 1
+
+#define max_alpha_char 36
+
+/*
+ | Declarations for strings for localization
+ */
+
+char *com_win_message; /* to be shown in com_win if no info window */
+char *no_file_string;
+char *ascii_code_str;
+char *printer_msg_str;
+char *command_str;
+char *file_write_prompt_str;
+char *file_read_prompt_str;
+char *char_str;
+char *unkn_cmd_str;
+char *non_unique_cmd_msg;
+char *line_num_str;
+char *line_len_str;
+char *current_file_str;
+char *usage0;
+char *usage1;
+char *usage2;
+char *usage3;
+char *usage4;
+char *file_is_dir_msg;
+char *new_file_msg;
+char *cant_open_msg;
+char *open_file_msg;
+char *file_read_fin_msg;
+char *reading_file_msg;
+char *read_only_msg;
+char *file_read_lines_msg;
+char *save_file_name_prompt;
+char *file_not_saved_msg;
+char *changes_made_prompt;
+char *yes_char;
+char *file_exists_prompt;
+char *create_file_fail_msg;
+char *writing_file_msg;
+char *file_written_msg;
+char *searching_msg;
+char *str_not_found_msg;
+char *search_prompt_str;
+char *exec_err_msg;
+char *continue_msg;
+char *menu_cancel_msg;
+char *menu_size_err_msg;
+char *press_any_key_msg;
+char *shell_prompt;
+char *formatting_msg;
+char *shell_echo_msg;
+char *spell_in_prog_msg;
+char *margin_prompt;
+char *restricted_msg;
+char *ON;
+char *OFF;
+char *HELP;
+char *WRITE;
+char *READ;
+char *LINE;
+char *FILE_str;
+char *CHARACTER;
+char *REDRAW;
+char *RESEQUENCE;
+char *AUTHOR;
+char *VERSION;
+char *CASE;
+char *NOCASE;
+char *EXPAND;
+char *NOEXPAND;
+char *Exit_string;
+char *QUIT_string;
+char *INFO;
+char *NOINFO;
+char *MARGINS;
+char *NOMARGINS;
+char *AUTOFORMAT;
+char *NOAUTOFORMAT;
+char *Echo;
+char *PRINTCOMMAND;
+char *RIGHTMARGIN;
+char *HIGHLIGHT;
+char *NOHIGHLIGHT;
+char *EIGHTBIT;
+char *NOEIGHTBIT;
+char *EMACS_string;
+char *NOEMACS_string;
+char *conf_dump_err_msg;
+char *conf_dump_success_msg;
+char *conf_not_saved_msg;
+char *ree_no_file_msg;
+char *cancel_string;
+char *menu_too_lrg_msg;
+char *more_above_str, *more_below_str;
+char *separator = "===============================================================================";
+
+char *chinese_cmd, *nochinese_cmd;
+
+#ifndef __STDC__
+#ifndef HAS_STDLIB
+extern char *malloc();
+extern char *realloc();
+extern char *getenv();
+FILE *fopen(); /* declaration for open function */
+#endif /* HAS_STDLIB */
+#endif /* __STDC__ */
+
+int
+main(argc, argv) /* beginning of main program */
+int argc;
+char *argv[];
+{
+ int counter;
+
+ for (counter = 1; counter < 24; counter++)
+ signal(counter, SIG_IGN);
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGINT, edit_abort);
+ d_char = malloc(3); /* provide a buffer for multi-byte chars */
+ d_word = malloc(150);
+ *d_word = '\0';
+ d_line = NULL;
+ dlt_line = txtalloc();
+ dlt_line->line = d_line;
+ dlt_line->line_length = 0;
+ curr_line = first_line = txtalloc();
+ curr_line->line = point = malloc(10);
+ curr_line->line_length = 1;
+ curr_line->max_length = 10;
+ curr_line->prev_line = NULL;
+ curr_line->next_line = NULL;
+ curr_line->line_number = 1;
+ srch_str = NULL;
+ u_srch_str = NULL;
+ position = 1;
+ scr_pos =0;
+ scr_vert = 0;
+ scr_horz = 0;
+ absolute_lin = 1;
+ bit_bucket = fopen("/dev/null", "w");
+ edit = TRUE;
+ gold = case_sen = FALSE;
+ shell_fork = TRUE;
+ strings_init();
+ ee_init();
+ if (argc > 0 )
+ get_options(argc, argv);
+ set_up_term();
+ if (right_margin == 0)
+ right_margin = COLS - 1;
+ if (top_of_stack == NULL)
+ {
+ if (restrict_mode())
+ {
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ wprintw(com_win, ree_no_file_msg);
+ wrefresh(com_win);
+ edit_abort(0);
+ }
+ wprintw(com_win, no_file_string);
+ wrefresh(com_win);
+ }
+ else
+ check_fp();
+
+ clear_com_win = TRUE;
+
+ counter = 0;
+
+ while(edit)
+ {
+ /*
+ | display line and column information
+ */
+ if (info_window)
+ {
+ if (!nohighlight)
+ wstandout(info_win);
+ wmove(info_win, 5, 0);
+ wprintw(info_win, separator);
+ wmove(info_win, 5, 5);
+ wprintw(info_win, "line %d col %d lines from top %d ",
+ curr_line->line_number, scr_horz, absolute_lin);
+ wstandend(info_win);
+ wrefresh(info_win);
+ }
+
+ wrefresh(text_win);
+ in = wgetch(text_win);
+ if (in == -1)
+ exit(0); /* without this exit ee will go into an
+ infinite loop if the network
+ session detaches */
+
+ resize_check();
+
+ if (clear_com_win)
+ {
+ clear_com_win = FALSE;
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ if (!info_window)
+ {
+ wprintw(com_win, "%s", com_win_message);
+ }
+ wrefresh(com_win);
+ }
+
+ if (in > 255)
+ function_key();
+ else if ((in == '\10') || (in == 127))
+ {
+ in = 8; /* make sure key is set to backspace */
+ delete(TRUE);
+ }
+ else if ((in > 31) || (in == 9))
+ insert(in);
+ else if ((in >= 0) && (in <= 31))
+ {
+ if (emacs_keys_mode)
+ emacs_control();
+ else
+ control();
+ }
+ }
+ return(0);
+}
+
+unsigned char *
+resiz_line(factor, rline, rpos) /* resize the line to length + factor*/
+int factor; /* resize factor */
+struct text *rline; /* position in line */
+int rpos;
+{
+ unsigned char *rpoint;
+ int resiz_var;
+
+ rline->max_length += factor;
+ rpoint = rline->line = realloc(rline->line, rline->max_length );
+ for (resiz_var = 1 ; (resiz_var < rpos) ; resiz_var++)
+ rpoint++;
+ return(rpoint);
+}
+
+void
+insert(character) /* insert character into line */
+int character; /* new character */
+{
+ int counter;
+ int value;
+ unsigned char *temp; /* temporary pointer */
+ unsigned char *temp2; /* temporary pointer */
+
+ if ((character == '\011') && (expand_tabs))
+ {
+ counter = len_char('\011', scr_horz);
+ for (; counter > 0; counter--)
+ insert(' ');
+ if (auto_format)
+ Auto_Format();
+ return;
+ }
+ text_changes = TRUE;
+ if ((curr_line->max_length - curr_line->line_length) < 5)
+ point = resiz_line(10, curr_line, position);
+ curr_line->line_length++;
+ temp = point;
+ counter = position;
+ while (counter < curr_line->line_length) /* find end of line */
+ {
+ counter++;
+ temp++;
+ }
+ temp++; /* increase length of line by one */
+ while (point < temp)
+ {
+ temp2=temp - 1;
+ *temp= *temp2; /* shift characters over by one */
+ temp--;
+ }
+ *point = character; /* insert new character */
+ wclrtoeol(text_win);
+ if (!isprint((unsigned char)character)) /* check for TAB character*/
+ {
+ scr_pos = scr_horz += out_char(text_win, character, scr_horz);
+ point++;
+ position++;
+ }
+ else
+ {
+ waddch(text_win, (unsigned char)character);
+ scr_pos = ++scr_horz;
+ point++;
+ position ++;
+ }
+
+ if ((observ_margins) && (right_margin < scr_pos))
+ {
+ counter = position;
+ while (scr_pos > right_margin)
+ prev_word();
+ if (scr_pos == 0)
+ {
+ while (position < counter)
+ right(TRUE);
+ }
+ else
+ {
+ counter -= position;
+ insert_line(TRUE);
+ for (value = 0; value < counter; value++)
+ right(TRUE);
+ }
+ }
+
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset += 8;
+ midscreen(scr_vert, point);
+ }
+
+ if ((auto_format) && (character == ' ') && (!formatted))
+ Auto_Format();
+ else if ((character != ' ') && (character != '\t'))
+ formatted = FALSE;
+
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+}
+
+void
+delete(disp) /* delete character */
+int disp;
+{
+ unsigned char *tp;
+ unsigned char *temp2;
+ struct text *temp_buff;
+ int temp_vert;
+ int temp_pos;
+ int del_width = 1;
+
+ if (point != curr_line->line) /* if not at beginning of line */
+ {
+ text_changes = TRUE;
+ temp2 = tp = point;
+ if ((ee_chinese) && (position >= 2) && (*(point - 2) > 127))
+ {
+ del_width = 2;
+ }
+ tp -= del_width;
+ point -= del_width;
+ position -= del_width;
+ temp_pos = position;
+ curr_line->line_length -= del_width;
+ if ((*tp < ' ') || (*tp >= 127)) /* check for TAB */
+ scanline(tp);
+ else
+ scr_horz -= del_width;
+ scr_pos = scr_horz;
+ if (in == 8)
+ {
+ if (del_width == 1)
+ *d_char = *point; /* save deleted character */
+ else
+ {
+ d_char[0] = *point;
+ d_char[1] = *(point + 1);
+ }
+ d_char[del_width] = '\0';
+ }
+ while (temp_pos <= curr_line->line_length)
+ {
+ temp_pos++;
+ *tp = *temp2;
+ tp++;
+ temp2++;
+ }
+ if ((scr_horz < horiz_offset) && (horiz_offset > 0))
+ {
+ horiz_offset -= 8;
+ midscreen(scr_vert, point);
+ }
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ text_changes = TRUE;
+ left(disp); /* go to previous line */
+ temp_buff = curr_line->next_line;
+ point = resiz_line(temp_buff->line_length, curr_line, position);
+ if (temp_buff->next_line != NULL)
+ temp_buff->next_line->prev_line = curr_line;
+ curr_line->next_line = temp_buff->next_line;
+ temp2 = temp_buff->line;
+ if (in == 8)
+ {
+ d_char[0] = '\n';
+ d_char[1] = '\0';
+ }
+ tp = point;
+ temp_pos = 1;
+ while (temp_pos < temp_buff->line_length)
+ {
+ curr_line->line_length++;
+ temp_pos++;
+ *tp = *temp2;
+ tp++;
+ temp2++;
+ }
+ *tp = '\0';
+ free(temp_buff->line);
+ free(temp_buff);
+ temp_buff = curr_line;
+ temp_vert = scr_vert;
+ scr_pos = scr_horz;
+ if (scr_vert < last_line)
+ {
+ wmove(text_win, scr_vert + 1, 0);
+ wdeleteln(text_win);
+ }
+ while ((temp_buff != NULL) && (temp_vert < last_line))
+ {
+ temp_buff = temp_buff->next_line;
+ temp_vert++;
+ }
+ if ((temp_vert == last_line) && (temp_buff != NULL))
+ {
+ tp = temp_buff->line;
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ draw_line(last_line, 0, tp, 1, temp_buff->line_length);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ }
+ }
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+ formatted = FALSE;
+}
+
+void
+scanline(pos) /* find the proper horizontal position for the pointer */
+unsigned char *pos;
+{
+ int temp;
+ unsigned char *ptr;
+
+ ptr = curr_line->line;
+ temp = 0;
+ while (ptr < pos)
+ {
+ if (*ptr <= 8)
+ temp += 2;
+ else if (*ptr == 9)
+ temp += tabshift(temp);
+ else if ((*ptr >= 10) && (*ptr <= 31))
+ temp += 2;
+ else if ((*ptr >= 32) && (*ptr < 127))
+ temp++;
+ else if (*ptr == 127)
+ temp += 2;
+ else if (!eightbit)
+ temp += 5;
+ else
+ temp++;
+ ptr++;
+ }
+ scr_horz = temp;
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset = (scr_horz - (scr_horz % 8)) - (COLS - 8);
+ midscreen(scr_vert, point);
+ }
+ else if (scr_horz < horiz_offset)
+ {
+ horiz_offset = max(0, (scr_horz - (scr_horz % 8)));
+ midscreen(scr_vert, point);
+ }
+}
+
+int
+tabshift(temp_int) /* give the number of spaces to shift */
+int temp_int;
+{
+ int leftover;
+
+ leftover = ((temp_int + 1) % 8);
+ if (leftover == 0)
+ return (1);
+ else
+ return (9 - leftover);
+}
+
+int
+out_char(window, character, column) /* output non-printing character */
+WINDOW *window;
+int character;
+int column;
+{
+ int i1, i2;
+ char *string;
+ char string2[8];
+
+ if (character == TAB)
+ {
+ i1 = tabshift(column);
+ for (i2 = 0;
+ (i2 < i1) && (((column+i2+1)-horiz_offset) < last_col); i2++)
+ {
+ waddch(window, ' ');
+ }
+ return(i1);
+ }
+ else if ((character >= '\0') && (character < ' '))
+ {
+ string = table[(int) character];
+ }
+ else if ((character < 0) || (character >= 127))
+ {
+ if (character == 127)
+ string = "^?";
+ else if (!eightbit)
+ {
+ sprintf(string2, "<%d>", (character < 0) ? (character + 256) : character);
+ string = string2;
+ }
+ else
+ {
+ waddch(window, (unsigned char)character );
+ return(1);
+ }
+ }
+ else
+ {
+ waddch(window, (unsigned char)character);
+ return(1);
+ }
+ for (i2 = 0; (string[i2] != '\0') && (((column+i2+1)-horiz_offset) < last_col); i2++)
+ waddch(window, (unsigned char)string[i2]);
+ return(strlen(string));
+}
+
+int
+len_char(character, column) /* return the length of the character */
+int character;
+int column; /* the column must be known to provide spacing for tabs */
+{
+ int length;
+
+ if (character == '\t')
+ length = tabshift(column);
+ else if ((character >= 0) && (character < 32))
+ length = 2;
+ else if ((character >= 32) && (character <= 126))
+ length = 1;
+ else if (character == 127)
+ length = 2;
+ else if (((character > 126) || (character < 0)) && (!eightbit))
+ length = 5;
+ else
+ length = 1;
+
+ return(length);
+}
+
+void
+draw_line(vertical, horiz, ptr, t_pos, length) /* redraw line from current position */
+int vertical; /* current vertical position on screen */
+int horiz; /* current horizontal position on screen */
+unsigned char *ptr; /* pointer to line */
+int t_pos; /* current position (offset in bytes) from bol */
+int length; /* length (in bytes) of line */
+{
+ int d; /* partial length of special or tab char to display */
+ unsigned char *temp; /* temporary pointer to position in line */
+ int abs_column; /* offset in screen units from begin of line */
+ int column; /* horizontal position on screen */
+ int row; /* vertical position on screen */
+ int posit; /* temporary position indicator within line */
+
+ abs_column = horiz;
+ column = horiz - horiz_offset;
+ row = vertical;
+ temp = ptr;
+ d = 0;
+ posit = t_pos;
+ if (column < 0)
+ {
+ wmove(text_win, row, 0);
+ wclrtoeol(text_win);
+ }
+ while (column < 0)
+ {
+ d = len_char(*temp, abs_column);
+ abs_column += d;
+ column += d;
+ posit++;
+ temp++;
+ }
+ wmove(text_win, row, column);
+ wclrtoeol(text_win);
+ while ((posit < length) && (column <= last_col))
+ {
+ if (!isprint(*temp))
+ {
+ column += len_char(*temp, abs_column);
+ abs_column += out_char(text_win, *temp, abs_column);
+ }
+ else
+ {
+ abs_column++;
+ column++;
+ waddch(text_win, *temp);
+ }
+ posit++;
+ temp++;
+ }
+ if (column < last_col)
+ wclrtoeol(text_win);
+ wmove(text_win, vertical, (horiz - horiz_offset));
+}
+
+void
+insert_line(disp) /* insert new line */
+int disp;
+{
+ int temp_pos;
+ int temp_pos2;
+ unsigned char *temp;
+ unsigned char *extra;
+ struct text *temp_nod;
+
+ text_changes = TRUE;
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wclrtoeol(text_win);
+ temp_nod= txtalloc();
+ temp_nod->line = extra= malloc(10);
+ temp_nod->line_length = 1;
+ temp_nod->max_length = 10;
+ temp_nod->line_number = curr_line->line_number + 1;
+ temp_nod->next_line = curr_line->next_line;
+ if (temp_nod->next_line != NULL)
+ temp_nod->next_line->prev_line = temp_nod;
+ temp_nod->prev_line = curr_line;
+ curr_line->next_line = temp_nod;
+ temp_pos2 = position;
+ temp = point;
+ if (temp_pos2 < curr_line->line_length)
+ {
+ temp_pos = 1;
+ while (temp_pos2 < curr_line->line_length)
+ {
+ if ((temp_nod->max_length - temp_nod->line_length)< 5)
+ extra = resiz_line(10, temp_nod, temp_pos);
+ temp_nod->line_length++;
+ temp_pos++;
+ temp_pos2++;
+ *extra= *temp;
+ extra++;
+ temp++;
+ }
+ temp=point;
+ *temp = '\0';
+ temp = resiz_line((1 - temp_nod->line_length), curr_line, position);
+ curr_line->line_length = 1 + temp - curr_line->line;
+ }
+ curr_line->line_length = position;
+ absolute_lin++;
+ curr_line = temp_nod;
+ *extra = '\0';
+ position = 1;
+ point= curr_line->line;
+ if (disp)
+ {
+ if (scr_vert < last_line)
+ {
+ scr_vert++;
+ wclrtoeol(text_win);
+ wmove(text_win, scr_vert, 0);
+ winsertln(text_win);
+ }
+ else
+ {
+ wmove(text_win, 0,0);
+ wdeleteln(text_win);
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ }
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ draw_line(scr_vert, scr_horz, point, position,
+ curr_line->line_length);
+ }
+}
+
+struct text *txtalloc() /* allocate space for line structure */
+{
+ return((struct text *) malloc(sizeof( struct text)));
+}
+
+struct files *name_alloc() /* allocate space for file name list node */
+{
+ return((struct files *) malloc(sizeof( struct files)));
+}
+
+unsigned char *next_word(string) /* move to next word in string */
+unsigned char *string;
+{
+ while ((*string != '\0') && ((*string != 32) && (*string != 9)))
+ string++;
+ while ((*string != '\0') && ((*string == 32) || (*string == 9)))
+ string++;
+ return(string);
+}
+
+void
+prev_word() /* move to start of previous word in text */
+{
+ if (position != 1)
+ {
+ if ((position != 1) && ((point[-1] == ' ') || (point[-1] == '\t')))
+ { /* if at the start of a word */
+ while ((position != 1) && ((*point != ' ') && (*point != '\t')))
+ left(TRUE);
+ }
+ while ((position != 1) && ((*point == ' ') || (*point == '\t')))
+ left(TRUE);
+ while ((position != 1) && ((*point != ' ') && (*point != '\t')))
+ left(TRUE);
+ if ((position != 1) && ((*point == ' ') || (*point == '\t')))
+ right(TRUE);
+ }
+ else
+ left(TRUE);
+}
+
+void
+control() /* use control for commands */
+{
+ char *string;
+
+ if (in == 1) /* control a */
+ {
+ string = get_string(ascii_code_str, TRUE);
+ if (*string != '\0')
+ {
+ in = atoi(string);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ insert(in);
+ }
+ free(string);
+ }
+ else if (in == 2) /* control b */
+ bottom();
+ else if (in == 3) /* control c */
+ {
+ command_prompt();
+ }
+ else if (in == 4) /* control d */
+ down();
+ else if (in == 5) /* control e */
+ search_prompt();
+ else if (in == 6) /* control f */
+ undel_char();
+ else if (in == 7) /* control g */
+ bol();
+ else if (in == 8) /* control h */
+ delete(TRUE);
+ else if (in == 9) /* control i */
+ ;
+ else if (in == 10) /* control j */
+ insert_line(TRUE);
+ else if (in == 11) /* control k */
+ del_char();
+ else if (in == 12) /* control l */
+ left(TRUE);
+ else if (in == 13) /* control m */
+ insert_line(TRUE);
+ else if (in == 14) /* control n */
+ move_rel('d', max(5, (last_line - 5)));
+ else if (in == 15) /* control o */
+ eol();
+ else if (in == 16) /* control p */
+ move_rel('u', max(5, (last_line - 5)));
+ else if (in == 17) /* control q */
+ ;
+ else if (in == 18) /* control r */
+ right(TRUE);
+ else if (in == 19) /* control s */
+ ;
+ else if (in == 20) /* control t */
+ top();
+ else if (in == 21) /* control u */
+ up();
+ else if (in == 22) /* control v */
+ undel_word();
+ else if (in == 23) /* control w */
+ del_word();
+ else if (in == 24) /* control x */
+ search(TRUE);
+ else if (in == 25) /* control y */
+ del_line();
+ else if (in == 26) /* control z */
+ undel_line();
+ else if (in == 27) /* control [ (escape) */
+ {
+ menu_op(main_menu);
+ }
+}
+
+/*
+ | Emacs control-key bindings
+ */
+
+void
+emacs_control()
+{
+ char *string;
+
+ if (in == 1) /* control a */
+ bol();
+ else if (in == 2) /* control b */
+ left(TRUE);
+ else if (in == 3) /* control c */
+ {
+ command_prompt();
+ }
+ else if (in == 4) /* control d */
+ del_char();
+ else if (in == 5) /* control e */
+ eol();
+ else if (in == 6) /* control f */
+ right(TRUE);
+ else if (in == 7) /* control g */
+ move_rel('u', max(5, (last_line - 5)));
+ else if (in == 8) /* control h */
+ delete(TRUE);
+ else if (in == 9) /* control i */
+ ;
+ else if (in == 10) /* control j */
+ undel_char();
+ else if (in == 11) /* control k */
+ del_line();
+ else if (in == 12) /* control l */
+ undel_line();
+ else if (in == 13) /* control m */
+ insert_line(TRUE);
+ else if (in == 14) /* control n */
+ down();
+ else if (in == 15) /* control o */
+ {
+ string = get_string(ascii_code_str, TRUE);
+ if (*string != '\0')
+ {
+ in = atoi(string);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ insert(in);
+ }
+ free(string);
+ }
+ else if (in == 16) /* control p */
+ up();
+ else if (in == 17) /* control q */
+ ;
+ else if (in == 18) /* control r */
+ undel_word();
+ else if (in == 19) /* control s */
+ ;
+ else if (in == 20) /* control t */
+ top();
+ else if (in == 21) /* control u */
+ bottom();
+ else if (in == 22) /* control v */
+ move_rel('d', max(5, (last_line - 5)));
+ else if (in == 23) /* control w */
+ del_word();
+ else if (in == 24) /* control x */
+ search(TRUE);
+ else if (in == 25) /* control y */
+ search_prompt();
+ else if (in == 26) /* control z */
+ adv_word();
+ else if (in == 27) /* control [ (escape) */
+ {
+ menu_op(main_menu);
+ }
+}
+
+void
+bottom() /* go to bottom of file */
+{
+ while (curr_line->next_line != NULL)
+ {
+ curr_line = curr_line->next_line;
+ absolute_lin++;
+ }
+ point = curr_line->line;
+ if (horiz_offset)
+ horiz_offset = 0;
+ position = 1;
+ midscreen(last_line, point);
+ scr_pos = scr_horz;
+}
+
+void
+top() /* go to top of file */
+{
+ while (curr_line->prev_line != NULL)
+ {
+ curr_line = curr_line->prev_line;
+ absolute_lin--;
+ }
+ point = curr_line->line;
+ if (horiz_offset)
+ horiz_offset = 0;
+ position = 1;
+ midscreen(0, point);
+ scr_pos = scr_horz;
+}
+
+void
+nextline() /* move pointers to start of next line */
+{
+ curr_line = curr_line->next_line;
+ absolute_lin++;
+ point = curr_line->line;
+ position = 1;
+ if (scr_vert == last_line)
+ {
+ wmove(text_win, 0,0);
+ wdeleteln(text_win);
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ draw_line(last_line,0,point,1,curr_line->line_length);
+ }
+ else
+ scr_vert++;
+}
+
+void
+prevline() /* move pointers to start of previous line*/
+{
+ curr_line = curr_line->prev_line;
+ absolute_lin--;
+ point = curr_line->line;
+ position = 1;
+ if (scr_vert == 0)
+ {
+ winsertln(text_win);
+ draw_line(0,0,point,1,curr_line->line_length);
+ }
+ else
+ scr_vert--;
+ while (position < curr_line->line_length)
+ {
+ position++;
+ point++;
+ }
+}
+
+void
+left(disp) /* move left one character */
+int disp;
+{
+ if (point != curr_line->line) /* if not at begin of line */
+ {
+ if ((ee_chinese) && (position >= 2) && (*(point - 2) > 127))
+ {
+ point--;
+ position--;
+ }
+ point--;
+ position--;
+ scanline(point);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ scr_pos = scr_horz;
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ if (!disp)
+ {
+ absolute_lin--;
+ curr_line = curr_line->prev_line;
+ point = curr_line->line + curr_line->line_length;
+ position = curr_line->line_length;
+ return;
+ }
+ position = 1;
+ prevline();
+ scanline(point);
+ scr_pos = scr_horz;
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ }
+}
+
+void
+right(disp) /* move right one character */
+int disp;
+{
+ if (position < curr_line->line_length)
+ {
+ if ((ee_chinese) && (*point > 127) &&
+ ((curr_line->line_length - position) >= 2))
+ {
+ point++;
+ position++;
+ }
+ point++;
+ position++;
+ scanline(point);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ scr_pos = scr_horz;
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ if (!disp)
+ {
+ absolute_lin++;
+ curr_line = curr_line->next_line;
+ point = curr_line->line;
+ position = 1;
+ return;
+ }
+ nextline();
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ position = 1;
+ }
+}
+
+void
+find_pos() /* move to the same column as on other line */
+{
+ scr_horz = 0;
+ position = 1;
+ while ((scr_horz < scr_pos) && (position < curr_line->line_length))
+ {
+ if (*point == 9)
+ scr_horz += tabshift(scr_horz);
+ else if (*point < ' ')
+ scr_horz += 2;
+ else if ((ee_chinese) && (*point > 127) &&
+ ((curr_line->line_length - position) >= 2))
+ {
+ scr_horz += 2;
+ point++;
+ position++;
+ }
+ else
+ scr_horz++;
+ position++;
+ point++;
+ }
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset = (scr_horz - (scr_horz % 8)) - (COLS - 8);
+ midscreen(scr_vert, point);
+ }
+ else if (scr_horz < horiz_offset)
+ {
+ horiz_offset = max(0, (scr_horz - (scr_horz % 8)));
+ midscreen(scr_vert, point);
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+up() /* move up one line */
+{
+ if (curr_line->prev_line != NULL)
+ {
+ prevline();
+ point = curr_line->line;
+ find_pos();
+ }
+}
+
+void
+down() /* move down one line */
+{
+ if (curr_line->next_line != NULL)
+ {
+ nextline();
+ find_pos();
+ }
+}
+
+void
+function_key() /* process function key */
+{
+ if (in == KEY_LEFT)
+ left(TRUE);
+ else if (in == KEY_RIGHT)
+ right(TRUE);
+ else if (in == KEY_HOME)
+ bol();
+ else if (in == KEY_END)
+ eol();
+ else if (in == KEY_UP)
+ up();
+ else if (in == KEY_DOWN)
+ down();
+ else if (in == KEY_NPAGE)
+ move_rel('d', max( 5, (last_line - 5)));
+ else if (in == KEY_PPAGE)
+ move_rel('u', max(5, (last_line - 5)));
+ else if (in == KEY_DL)
+ del_line();
+ else if (in == KEY_DC)
+ del_char();
+ else if (in == KEY_BACKSPACE)
+ delete(TRUE);
+ else if (in == KEY_IL)
+ { /* insert a line before current line */
+ insert_line(TRUE);
+ left(TRUE);
+ }
+ else if (in == KEY_F(1))
+ gold = !gold;
+ else if (in == KEY_F(2))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ undel_line();
+ }
+ else
+ undel_char();
+ }
+ else if (in == KEY_F(3))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ undel_word();
+ }
+ else
+ del_word();
+ }
+ else if (in == KEY_F(4))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ paint_info_win();
+ midscreen(scr_vert, point);
+ }
+ else
+ adv_word();
+ }
+ else if (in == KEY_F(5))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ search_prompt();
+ }
+ else
+ search(TRUE);
+ }
+ else if (in == KEY_F(6))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ bottom();
+ }
+ else
+ top();
+ }
+ else if (in == KEY_F(7))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ eol();
+ }
+ else
+ bol();
+ }
+ else if (in == KEY_F(8))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ command_prompt();
+ }
+ else
+ adv_line();
+ }
+}
+
+void
+print_buffer()
+{
+ char buffer[256];
+
+ sprintf(buffer, ">!%s", print_command);
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, printer_msg_str, print_command);
+ wrefresh(com_win);
+ command(buffer);
+}
+
+void
+command_prompt()
+{
+ char *cmd_str;
+ int result;
+
+ info_type = COMMANDS;
+ paint_info_win();
+ cmd_str = get_string(command_str, TRUE);
+ if ((result = unique_test(cmd_str, commands)) != 1)
+ {
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ if (result == 0)
+ wprintw(com_win, unkn_cmd_str, cmd_str);
+ else
+ wprintw(com_win, non_unique_cmd_msg);
+
+ wrefresh(com_win);
+
+ info_type = CONTROL_KEYS;
+ paint_info_win();
+
+ if (cmd_str != NULL)
+ free(cmd_str);
+ return;
+ }
+ command(cmd_str);
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ info_type = CONTROL_KEYS;
+ paint_info_win();
+ if (cmd_str != NULL)
+ free(cmd_str);
+}
+
+void
+command(cmd_str1) /* process commands from keyboard */
+char *cmd_str1;
+{
+ char *cmd_str2 = NULL;
+ char *cmd_str = cmd_str1;
+
+ clear_com_win = TRUE;
+ if (compare(cmd_str, HELP, FALSE))
+ help();
+ else if (compare(cmd_str, WRITE, FALSE))
+ {
+ if (restrict_mode())
+ {
+ return;
+ }
+ cmd_str = next_word(cmd_str);
+ if (*cmd_str == '\0')
+ {
+ cmd_str = cmd_str2 = get_string(file_write_prompt_str, TRUE);
+ }
+ tmp_file = resolve_name(cmd_str);
+ write_file(tmp_file, 1);
+ if (tmp_file != cmd_str)
+ free(tmp_file);
+ }
+ else if (compare(cmd_str, READ, FALSE))
+ {
+ if (restrict_mode())
+ {
+ return;
+ }
+ cmd_str = next_word(cmd_str);
+ if (*cmd_str == '\0')
+ {
+ cmd_str = cmd_str2 = get_string(file_read_prompt_str, TRUE);
+ }
+ tmp_file = cmd_str;
+ recv_file = TRUE;
+ tmp_file = resolve_name(cmd_str);
+ check_fp();
+ if (tmp_file != cmd_str)
+ free(tmp_file);
+ }
+ else if (compare(cmd_str, LINE, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, line_num_str, curr_line->line_number);
+ wprintw(com_win, line_len_str, curr_line->line_length);
+ }
+ else if (compare(cmd_str, FILE_str, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (in_file_name == NULL)
+ wprintw(com_win, no_file_string);
+ else
+ wprintw(com_win, current_file_str, in_file_name);
+ }
+ else if ((*cmd_str >= '0') && (*cmd_str <= '9'))
+ goto_line(cmd_str);
+ else if (compare(cmd_str, CHARACTER, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, char_str, *point);
+ }
+ else if (compare(cmd_str, REDRAW, FALSE))
+ redraw();
+ else if (compare(cmd_str, RESEQUENCE, FALSE))
+ {
+ tmp_line = first_line->next_line;
+ while (tmp_line != NULL)
+ {
+ tmp_line->line_number = tmp_line->prev_line->line_number + 1;
+ tmp_line = tmp_line->next_line;
+ }
+ }
+ else if (compare(cmd_str, AUTHOR, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, "written by Hugh Mahon");
+ }
+ else if (compare(cmd_str, VERSION, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, "%s", version);
+ }
+ else if (compare(cmd_str, CASE, FALSE))
+ case_sen = TRUE;
+ else if (compare(cmd_str, NOCASE, FALSE))
+ case_sen = FALSE;
+ else if (compare(cmd_str, EXPAND, FALSE))
+ expand_tabs = TRUE;
+ else if (compare(cmd_str, NOEXPAND, FALSE))
+ expand_tabs = FALSE;
+ else if (compare(cmd_str, Exit_string, FALSE))
+ finish();
+ else if (compare(cmd_str, chinese_cmd, FALSE))
+ {
+ ee_chinese = TRUE;
+#ifdef NCURSE
+ nc_setattrib(A_NC_BIG5);
+#endif /* NCURSE */
+ }
+ else if (compare(cmd_str, nochinese_cmd, FALSE))
+ {
+ ee_chinese = FALSE;
+#ifdef NCURSE
+ nc_clearattrib(A_NC_BIG5);
+#endif /* NCURSE */
+ }
+ else if (compare(cmd_str, QUIT_string, FALSE))
+ quit(0);
+ else if (*cmd_str == '!')
+ {
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == 9))
+ cmd_str = next_word(cmd_str);
+ sh_command(cmd_str);
+ }
+ else if ((*cmd_str == '<') && (!in_pipe))
+ {
+ in_pipe = TRUE;
+ shell_fork = FALSE;
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == '\t'))
+ cmd_str = next_word(cmd_str);
+ command(cmd_str);
+ in_pipe = FALSE;
+ shell_fork = TRUE;
+ }
+ else if ((*cmd_str == '>') && (!out_pipe))
+ {
+ out_pipe = TRUE;
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == '\t'))
+ cmd_str = next_word(cmd_str);
+ command(cmd_str);
+ out_pipe = FALSE;
+ }
+ else
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, unkn_cmd_str, cmd_str);
+ }
+ if (cmd_str2 != NULL)
+ free(cmd_str2);
+}
+
+int
+scan(line, offset, column) /* determine horizontal position for get_string */
+char *line;
+int offset;
+int column;
+{
+ char *stemp;
+ int i;
+ int j;
+
+ stemp = line;
+ i = 0;
+ j = column;
+ while (i < offset)
+ {
+ i++;
+ j += len_char(*stemp, j);
+ stemp++;
+ }
+ return(j);
+}
+
+char *
+get_string(prompt, advance) /* read string from input on command line */
+char *prompt; /* string containing user prompt message */
+int advance; /* if true, skip leading spaces and tabs */
+{
+ char *string;
+ char *tmp_string;
+ char *nam_str;
+ char *g_point;
+ int tmp_int;
+ int g_horz, g_position, g_pos;
+ int esc_flag;
+
+ g_point = tmp_string = malloc(512);
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ waddstr(com_win, prompt);
+ wrefresh(com_win);
+ nam_str = tmp_string;
+ clear_com_win = TRUE;
+ g_horz = g_position = scan(prompt, strlen(prompt), 0);
+ g_pos = 0;
+ do
+ {
+ esc_flag = FALSE;
+ in = wgetch(com_win);
+ if (in == -1)
+ exit(0);
+ if (((in == 8) || (in == 127) || (in == KEY_BACKSPACE)) && (g_pos > 0))
+ {
+ tmp_int = g_horz;
+ g_pos--;
+ g_horz = scan(g_point, g_pos, g_position);
+ tmp_int = tmp_int - g_horz;
+ for (; 0 < tmp_int; tmp_int--)
+ {
+ if ((g_horz+tmp_int) < (last_col - 1))
+ {
+ waddch(com_win, '\010');
+ waddch(com_win, ' ');
+ waddch(com_win, '\010');
+ }
+ }
+ nam_str--;
+ }
+ else if ((in != 8) && (in != 127) && (in != '\n') && (in != '\r') && (in < 256))
+ {
+ if (in == '\026') /* control-v, accept next character verbatim */
+ { /* allows entry of ^m, ^j, and ^h */
+ esc_flag = TRUE;
+ in = wgetch(com_win);
+ if (in == -1)
+ exit(0);
+ }
+ *nam_str = in;
+ g_pos++;
+ if (!isprint((unsigned char)in) && (g_horz < (last_col - 1)))
+ g_horz += out_char(com_win, in, g_horz);
+ else
+ {
+ g_horz++;
+ if (g_horz < (last_col - 1))
+ waddch(com_win, (unsigned char)in);
+ }
+ nam_str++;
+ }
+ wrefresh(com_win);
+ if (esc_flag)
+ in = '\0';
+ } while ((in != '\n') && (in != '\r'));
+ *nam_str = '\0';
+ nam_str = tmp_string;
+ if (((*nam_str == ' ') || (*nam_str == 9)) && (advance))
+ nam_str = next_word(nam_str);
+ string = malloc(strlen(nam_str) + 1);
+ strcpy(string, nam_str);
+ free(tmp_string);
+ wrefresh(com_win);
+ return(string);
+}
+
+int
+compare(string1, string2, sensitive) /* compare two strings */
+char *string1;
+char *string2;
+int sensitive;
+{
+ char *strng1;
+ char *strng2;
+ int tmp;
+ int equal;
+
+ strng1 = string1;
+ strng2 = string2;
+ tmp = 0;
+ if ((strng1 == NULL) || (strng2 == NULL) || (*strng1 == '\0') || (*strng2 == '\0'))
+ return(FALSE);
+ equal = TRUE;
+ while (equal)
+ {
+ if (sensitive)
+ {
+ if (*strng1 != *strng2)
+ equal = FALSE;
+ }
+ else
+ {
+ if (toupper((unsigned char)*strng1) != toupper((unsigned char)*strng2))
+ equal = FALSE;
+ }
+ strng1++;
+ strng2++;
+ if ((*strng1 == '\0') || (*strng2 == '\0') || (*strng1 == ' ') || (*strng2 == ' '))
+ break;
+ tmp++;
+ }
+ return(equal);
+}
+
+void
+goto_line(cmd_str)
+char *cmd_str;
+{
+ int number;
+ int i;
+ char *ptr;
+ char direction = '\0';
+ struct text *t_line;
+
+ ptr = cmd_str;
+ i= 0;
+ while ((*ptr >='0') && (*ptr <= '9'))
+ {
+ i= i * 10 + (*ptr - '0');
+ ptr++;
+ }
+ number = i;
+ i = 0;
+ t_line = curr_line;
+ while ((t_line->line_number > number) && (t_line->prev_line != NULL))
+ {
+ i++;
+ t_line = t_line->prev_line;
+ direction = 'u';
+ }
+ while ((t_line->line_number < number) && (t_line->next_line != NULL))
+ {
+ i++;
+ direction = 'd';
+ t_line = t_line->next_line;
+ }
+ if ((i < 30) && (i > 0))
+ {
+ move_rel(direction, i);
+ }
+ else
+ {
+ if (direction != 'd')
+ {
+ absolute_lin += i;
+ }
+ else
+ {
+ absolute_lin -= i;
+ }
+ curr_line = t_line;
+ point = curr_line->line;
+ position = 1;
+ midscreen((last_line / 2), point);
+ scr_pos = scr_horz;
+ }
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, line_num_str, curr_line->line_number);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+midscreen(line, pnt) /* put current line in middle of screen */
+int line;
+unsigned char *pnt;
+{
+ struct text *mid_line;
+ int i;
+
+ line = min(line, last_line);
+ mid_line = curr_line;
+ for (i = 0; ((i < line) && (curr_line->prev_line != NULL)); i++)
+ curr_line = curr_line->prev_line;
+ scr_vert = scr_horz = 0;
+ wmove(text_win, 0, 0);
+ draw_screen();
+ scr_vert = i;
+ curr_line = mid_line;
+ scanline(pnt);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+get_options(numargs, arguments) /* get arguments from command line */
+int numargs;
+char *arguments[];
+{
+ char *buff;
+ int count;
+ struct files *temp_names = NULL;
+ char *name;
+ char *ptr;
+ int no_more_opts = FALSE;
+
+ /*
+ | see if editor was invoked as 'ree' (restricted mode)
+ */
+
+ if (!(name = strrchr(arguments[0], '/')))
+ name = arguments[0];
+ else
+ name++;
+ if (!strcmp(name, "ree"))
+ restricted = TRUE;
+
+ top_of_stack = NULL;
+ input_file = FALSE;
+ recv_file = FALSE;
+ count = 1;
+ while ((count < numargs)&& (!no_more_opts))
+ {
+ buff = arguments[count];
+ if (!strcmp("-i", buff))
+ {
+ info_window = FALSE;
+ }
+ else if (!strcmp("-e", buff))
+ {
+ expand_tabs = FALSE;
+ }
+ else if (!strcmp("-h", buff))
+ {
+ nohighlight = TRUE;
+ }
+ else if (!strcmp("-?", buff))
+ {
+ fprintf(stderr, usage0, arguments[0]);
+ fputs(usage1, stderr);
+ fputs(usage2, stderr);
+ fputs(usage3, stderr);
+ fputs(usage4, stderr);
+ exit(1);
+ }
+ else if ((*buff == '+') && (start_at_line == NULL))
+ {
+ buff++;
+ start_at_line = buff;
+ }
+ else if (!(strcmp("--", buff)))
+ no_more_opts = TRUE;
+ else
+ {
+ count--;
+ no_more_opts = TRUE;
+ }
+ count++;
+ }
+ while (count < numargs)
+ {
+ buff = arguments[count];
+ if (top_of_stack == NULL)
+ {
+ temp_names = top_of_stack = name_alloc();
+ }
+ else
+ {
+ temp_names->next_name = name_alloc();
+ temp_names = temp_names->next_name;
+ }
+ ptr = temp_names->name = malloc(strlen(buff) + 1);
+ while (*buff != '\0')
+ {
+ *ptr = *buff;
+ buff++;
+ ptr++;
+ }
+ *ptr = '\0';
+ temp_names->next_name = NULL;
+ input_file = TRUE;
+ recv_file = TRUE;
+ count++;
+ }
+}
+
+void
+check_fp() /* open or close files according to flags */
+{
+ int line_num;
+ int temp;
+ struct stat buf;
+
+ clear_com_win = TRUE;
+ tmp_vert = scr_vert;
+ tmp_horz = scr_horz;
+ tmp_line = curr_line;
+ if (input_file)
+ {
+ in_file_name = tmp_file = top_of_stack->name;
+ top_of_stack = top_of_stack->next_name;
+ }
+ temp = stat(tmp_file, &buf);
+ buf.st_mode &= ~07777;
+ if ((temp != -1) && (buf.st_mode != 0100000) && (buf.st_mode != 0))
+ {
+ wprintw(com_win, file_is_dir_msg, tmp_file);
+ wrefresh(com_win);
+ if (input_file)
+ {
+ quit(0);
+ return;
+ }
+ else
+ return;
+ }
+ if ((get_fd = open(tmp_file, O_RDONLY)) == -1)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (input_file)
+ wprintw(com_win, new_file_msg, tmp_file);
+ else
+ wprintw(com_win, cant_open_msg, tmp_file);
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wrefresh(text_win);
+ recv_file = FALSE;
+ input_file = FALSE;
+ return;
+ }
+ else
+ get_file(tmp_file);
+
+ recv_file = FALSE;
+ line_num = curr_line->line_number;
+ scr_vert = tmp_vert;
+ scr_horz = tmp_horz;
+ if (input_file)
+ curr_line= first_line;
+ else
+ curr_line = tmp_line;
+ point = curr_line->line;
+ draw_screen();
+ if (input_file)
+ {
+ input_file = FALSE;
+ if (start_at_line != NULL)
+ {
+ line_num = atoi(start_at_line) - 1;
+ move_rel('d', line_num);
+ line_num = 0;
+ start_at_line = NULL;
+ }
+ }
+ else
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ text_changes = TRUE;
+ if ((tmp_file != NULL) && (*tmp_file != '\0'))
+ wprintw(com_win, file_read_fin_msg, tmp_file);
+ }
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wrefresh(text_win);
+}
+
+void
+get_file(file_name) /* read specified file into current buffer */
+char *file_name;
+{
+ int can_read; /* file has at least one character */
+ int length; /* length of line read by read */
+ int append; /* should text be appended to current line */
+ struct text *temp_line;
+ char ro_flag = FALSE;
+
+ if (recv_file) /* if reading a file */
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, reading_file_msg, file_name);
+ if (access(file_name, 2)) /* check permission to write */
+ {
+ if ((errno == ENOTDIR) || (errno == EACCES) || (errno == EROFS) || (errno == ETXTBSY) || (errno == EFAULT))
+ {
+ wprintw(com_win, read_only_msg);
+ ro_flag = TRUE;
+ }
+ }
+ wrefresh(com_win);
+ }
+ if (curr_line->line_length > 1) /* if current line is not blank */
+ {
+ insert_line(FALSE);
+ left(FALSE);
+ append = FALSE;
+ }
+ else
+ append = TRUE;
+ can_read = FALSE; /* test if file has any characters */
+ while (((length = read(get_fd, in_string, 512)) != 0) && (length != -1))
+ {
+ can_read = TRUE; /* if set file has at least 1 character */
+ get_line(length, in_string, &append);
+ }
+ if ((can_read) && (curr_line->line_length == 1))
+ {
+ temp_line = curr_line->prev_line;
+ temp_line->next_line = curr_line->next_line;
+ if (temp_line->next_line != NULL)
+ temp_line->next_line->prev_line = temp_line;
+ if (curr_line->line != NULL)
+ free(curr_line->line);
+ free(curr_line);
+ curr_line = temp_line;
+ }
+ if (input_file) /* if this is the file to be edited display number of lines */
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, file_read_lines_msg, in_file_name, curr_line->line_number);
+ if (ro_flag)
+ wprintw(com_win, read_only_msg);
+ wrefresh(com_win);
+ }
+ else if (can_read) /* not input_file and file is non-zero size */
+ text_changes = TRUE;
+
+ if (recv_file) /* if reading a file */
+ {
+ in = EOF;
+ }
+}
+
+void
+get_line(length, in_string, append) /* read string and split into lines */
+int length; /* length of string read by read */
+unsigned char *in_string; /* string read by read */
+int *append; /* TRUE if must append more text to end of current line */
+{
+ unsigned char *str1;
+ unsigned char *str2;
+ int num; /* offset from start of string */
+ int char_count; /* length of new line (or added portion */
+ int temp_counter; /* temporary counter value */
+ struct text *tline; /* temporary pointer to new line */
+ int first_time; /* if TRUE, the first time through the loop */
+
+ str2 = in_string;
+ num = 0;
+ first_time = TRUE;
+ while (num < length)
+ {
+ if (!first_time)
+ {
+ if (num < length)
+ {
+ str2++;
+ num++;
+ }
+ }
+ else
+ first_time = FALSE;
+ str1 = str2;
+ char_count = 1;
+ /* find end of line */
+ while ((*str2 != '\n') && (num < length))
+ {
+ str2++;
+ num++;
+ char_count++;
+ }
+ if (!(*append)) /* if not append to current line, insert new one */
+ {
+ tline = txtalloc(); /* allocate data structure for next line */
+ tline->line_number = curr_line->line_number + 1;
+ tline->next_line = curr_line->next_line;
+ tline->prev_line = curr_line;
+ curr_line->next_line = tline;
+ if (tline->next_line != NULL)
+ tline->next_line->prev_line = tline;
+ curr_line = tline;
+ curr_line->line = point = (unsigned char *) malloc(char_count);
+ curr_line->line_length = char_count;
+ curr_line->max_length = char_count;
+ }
+ else
+ {
+ point = resiz_line(char_count, curr_line, curr_line->line_length);
+ curr_line->line_length += (char_count - 1);
+ }
+ for (temp_counter = 1; temp_counter < char_count; temp_counter++)
+ {
+ *point = *str1;
+ point++;
+ str1++;
+ }
+ *point = '\0';
+ *append = FALSE;
+ if ((num == length) && (*str2 != '\n'))
+ *append = TRUE;
+ }
+}
+
+void
+draw_screen() /* redraw the screen from current postion */
+{
+ struct text *temp_line;
+ unsigned char *line_out;
+ int temp_vert;
+
+ temp_line = curr_line;
+ temp_vert = scr_vert;
+ wclrtobot(text_win);
+ while ((temp_line != NULL) && (temp_vert <= last_line))
+ {
+ line_out = temp_line->line;
+ draw_line(temp_vert, 0, line_out, 1, temp_line->line_length);
+ temp_vert++;
+ temp_line = temp_line->next_line;
+ }
+ wmove(text_win, temp_vert, 0);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+finish() /* prepare to exit edit session */
+{
+ char *file_name = in_file_name;
+
+ /*
+ | changes made here should be reflected in the 'save'
+ | portion of file_op()
+ */
+
+ if ((file_name == NULL) || (*file_name == '\0'))
+ file_name = get_string(save_file_name_prompt, TRUE);
+
+ if ((file_name == NULL) || (*file_name == '\0'))
+ {
+ wmove(com_win, 0, 0);
+ wprintw(com_win, file_not_saved_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return;
+ }
+
+ tmp_file = resolve_name(file_name);
+ if (tmp_file != file_name)
+ {
+ free(file_name);
+ file_name = tmp_file;
+ }
+
+ if (write_file(file_name, 1))
+ {
+ text_changes = FALSE;
+ quit(0);
+ }
+}
+
+int
+quit(noverify) /* exit editor */
+int noverify;
+{
+ char *ans;
+
+ touchwin(text_win);
+ wrefresh(text_win);
+ if ((text_changes) && (!noverify))
+ {
+ ans = get_string(changes_made_prompt, TRUE);
+ if (toupper((unsigned char)*ans) == toupper((unsigned char)*yes_char))
+ text_changes = FALSE;
+ else
+ return(0);
+ free(ans);
+ }
+ if (top_of_stack == NULL)
+ {
+ if (info_window)
+ wrefresh(info_win);
+ wrefresh(com_win);
+ resetty();
+ endwin();
+ putchar('\n');
+ exit(0);
+ }
+ else
+ {
+ delete_text();
+ recv_file = TRUE;
+ input_file = TRUE;
+ check_fp();
+ }
+ return(0);
+}
+
+void
+edit_abort(arg)
+int arg;
+{
+ wrefresh(com_win);
+ resetty();
+ endwin();
+ putchar('\n');
+ exit(1);
+}
+
+void
+delete_text()
+{
+ while (curr_line->next_line != NULL)
+ curr_line = curr_line->next_line;
+ while (curr_line != first_line)
+ {
+ free(curr_line->line);
+ curr_line = curr_line->prev_line;
+ absolute_lin--;
+ free(curr_line->next_line);
+ }
+ curr_line->next_line = NULL;
+ *curr_line->line = '\0';
+ curr_line->line_length = 1;
+ curr_line->line_number = 1;
+ point = curr_line->line;
+ scr_pos = scr_vert = scr_horz = 0;
+ position = 1;
+}
+
+int
+write_file(file_name, warn_if_exists)
+char *file_name;
+int warn_if_exists;
+{
+ char cr;
+ char *tmp_point;
+ struct text *out_line;
+ int lines, charac;
+ int temp_pos;
+ int write_flag = TRUE;
+
+ charac = lines = 0;
+ if (warn_if_exists &&
+ ((in_file_name == NULL) || strcmp(in_file_name, file_name)))
+ {
+ if ((temp_fp = fopen(file_name, "r")))
+ {
+ tmp_point = get_string(file_exists_prompt, TRUE);
+ if (toupper((unsigned char)*tmp_point) == toupper((unsigned char)*yes_char))
+ write_flag = TRUE;
+ else
+ write_flag = FALSE;
+ fclose(temp_fp);
+ free(tmp_point);
+ }
+ }
+
+ clear_com_win = TRUE;
+
+ if (write_flag)
+ {
+ if ((temp_fp = fopen(file_name, "w")) == NULL)
+ {
+ clear_com_win = TRUE;
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, create_file_fail_msg, file_name);
+ wrefresh(com_win);
+ return(FALSE);
+ }
+ else
+ {
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, writing_file_msg, file_name);
+ wrefresh(com_win);
+ cr = '\n';
+ out_line = first_line;
+ while (out_line != NULL)
+ {
+ temp_pos = 1;
+ tmp_point= out_line->line;
+ while (temp_pos < out_line->line_length)
+ {
+ putc(*tmp_point, temp_fp);
+ tmp_point++;
+ temp_pos++;
+ }
+ charac += out_line->line_length;
+ out_line = out_line->next_line;
+ putc(cr, temp_fp);
+ lines++;
+ }
+ fclose(temp_fp);
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, file_written_msg, file_name, lines, charac);
+ wrefresh(com_win);
+ return(TRUE);
+ }
+ }
+ else
+ return(FALSE);
+}
+
+int
+search(display_message) /* search for string in srch_str */
+int display_message;
+{
+ int lines_moved;
+ int iter;
+ int found;
+
+ if ((srch_str == NULL) || (*srch_str == '\0'))
+ return(FALSE);
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, searching_msg);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ }
+ lines_moved = 0;
+ found = FALSE;
+ srch_line = curr_line;
+ srch_1 = point;
+ if (position < curr_line->line_length)
+ srch_1++;
+ iter = position + 1;
+ while ((!found) && (srch_line != NULL))
+ {
+ while ((iter < srch_line->line_length) && (!found))
+ {
+ srch_2 = srch_1;
+ if (case_sen) /* if case sensitive */
+ {
+ srch_3 = srch_str;
+ while ((*srch_2 == *srch_3) && (*srch_3 != '\0'))
+ {
+ found = TRUE;
+ srch_2++;
+ srch_3++;
+ } /* end while */
+ }
+ else /* if not case sensitive */
+ {
+ srch_3 = u_srch_str;
+ while ((toupper(*srch_2) == *srch_3) && (*srch_3 != '\0'))
+ {
+ found = TRUE;
+ srch_2++;
+ srch_3++;
+ }
+ } /* end else */
+ if (!((*srch_3 == '\0') && (found)))
+ {
+ found = FALSE;
+ if (iter < srch_line->line_length)
+ srch_1++;
+ iter++;
+ }
+ }
+ if (!found)
+ {
+ srch_line = srch_line->next_line;
+ if (srch_line != NULL)
+ srch_1 = srch_line->line;
+ iter = 1;
+ lines_moved++;
+ }
+ }
+ if (found)
+ {
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ }
+ if (lines_moved == 0)
+ {
+ while (position < iter)
+ right(TRUE);
+ }
+ else
+ {
+ if (lines_moved < 30)
+ {
+ move_rel('d', lines_moved);
+ while (position < iter)
+ right(TRUE);
+ }
+ else
+ {
+ absolute_lin += lines_moved;
+ curr_line = srch_line;
+ point = srch_1;
+ position = iter;
+ scanline(point);
+ scr_pos = scr_horz;
+ midscreen((last_line / 2), point);
+ }
+ }
+ }
+ else
+ {
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, str_not_found_msg, srch_str);
+ wrefresh(com_win);
+ }
+ wmove(text_win, scr_vert,(scr_horz - horiz_offset));
+ }
+ return(found);
+}
+
+void
+search_prompt() /* prompt and read search string (srch_str) */
+{
+ if (srch_str != NULL)
+ free(srch_str);
+ if ((u_srch_str != NULL) && (*u_srch_str != '\0'))
+ free(u_srch_str);
+ srch_str = get_string(search_prompt_str, FALSE);
+ gold = FALSE;
+ srch_3 = srch_str;
+ srch_1 = u_srch_str = malloc(strlen(srch_str) + 1);
+ while (*srch_3 != '\0')
+ {
+ *srch_1 = toupper(*srch_3);
+ srch_1++;
+ srch_3++;
+ }
+ *srch_1 = '\0';
+ search(TRUE);
+}
+
+void
+del_char() /* delete current character */
+{
+ in = 8; /* backspace */
+ if (position < curr_line->line_length) /* if not end of line */
+ {
+ if ((ee_chinese) && (*point > 127) &&
+ ((curr_line->line_length - position) >= 2))
+ {
+ point++;
+ position++;
+ }
+ position++;
+ point++;
+ scanline(point);
+ delete(TRUE);
+ }
+ else
+ {
+ right(TRUE);
+ delete(TRUE);
+ }
+}
+
+void
+undel_char() /* undelete last deleted character */
+{
+ if (d_char[0] == '\n') /* insert line if last del_char deleted eol */
+ insert_line(TRUE);
+ else
+ {
+ in = d_char[0];
+ insert(in);
+ if (d_char[1] != '\0')
+ {
+ in = d_char[1];
+ insert(in);
+ }
+ }
+}
+
+void
+del_word() /* delete word in front of cursor */
+{
+ int tposit;
+ int difference;
+ unsigned char *d_word2;
+ unsigned char *d_word3;
+ unsigned char tmp_char[3];
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = malloc(curr_line->line_length);
+ tmp_char[0] = d_char[0];
+ tmp_char[1] = d_char[1];
+ tmp_char[2] = d_char[2];
+ d_word3 = point;
+ d_word2 = d_word;
+ tposit = position;
+ while ((tposit < curr_line->line_length) &&
+ ((*d_word3 != ' ') && (*d_word3 != '\t')))
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ while ((tposit < curr_line->line_length) &&
+ ((*d_word3 == ' ') || (*d_word3 == '\t')))
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ *d_word2 = '\0';
+ d_wrd_len = difference = d_word2 - d_word;
+ d_word2 = point;
+ while (tposit < curr_line->line_length)
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ curr_line->line_length -= difference;
+ *d_word2 = '\0';
+ draw_line(scr_vert, scr_horz,point,position,curr_line->line_length);
+ d_char[0] = tmp_char[0];
+ d_char[1] = tmp_char[1];
+ d_char[2] = tmp_char[2];
+ text_changes = TRUE;
+ formatted = FALSE;
+}
+
+void
+undel_word() /* undelete last deleted word */
+{
+ int temp;
+ int tposit;
+ unsigned char *tmp_old_ptr;
+ unsigned char *tmp_space;
+ unsigned char *tmp_ptr;
+ unsigned char *d_word_ptr;
+
+ /*
+ | resize line to handle undeleted word
+ */
+ if ((curr_line->max_length - (curr_line->line_length + d_wrd_len)) < 5)
+ point = resiz_line(d_wrd_len, curr_line, position);
+ tmp_ptr = tmp_space = malloc(curr_line->line_length + d_wrd_len);
+ d_word_ptr = d_word;
+ temp = 1;
+ /*
+ | copy d_word contents into temp space
+ */
+ while (temp <= d_wrd_len)
+ {
+ temp++;
+ *tmp_ptr = *d_word_ptr;
+ tmp_ptr++;
+ d_word_ptr++;
+ }
+ tmp_old_ptr = point;
+ tposit = position;
+ /*
+ | copy contents of line from curent position to eol into
+ | temp space
+ */
+ while (tposit < curr_line->line_length)
+ {
+ temp++;
+ tposit++;
+ *tmp_ptr = *tmp_old_ptr;
+ tmp_ptr++;
+ tmp_old_ptr++;
+ }
+ curr_line->line_length += d_wrd_len;
+ tmp_old_ptr = point;
+ *tmp_ptr = '\0';
+ tmp_ptr = tmp_space;
+ tposit = 1;
+ /*
+ | now copy contents from temp space back to original line
+ */
+ while (tposit < temp)
+ {
+ tposit++;
+ *tmp_old_ptr = *tmp_ptr;
+ tmp_ptr++;
+ tmp_old_ptr++;
+ }
+ *tmp_old_ptr = '\0';
+ free(tmp_space);
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+}
+
+void
+del_line() /* delete from cursor to end of line */
+{
+ unsigned char *dl1;
+ unsigned char *dl2;
+ int tposit;
+
+ if (d_line != NULL)
+ free(d_line);
+ d_line = malloc(curr_line->line_length);
+ dl1 = d_line;
+ dl2 = point;
+ tposit = position;
+ while (tposit < curr_line->line_length)
+ {
+ *dl1 = *dl2;
+ dl1++;
+ dl2++;
+ tposit++;
+ }
+ dlt_line->line_length = 1 + tposit - position;
+ *dl1 = '\0';
+ *point = '\0';
+ curr_line->line_length = position;
+ wclrtoeol(text_win);
+ if (curr_line->next_line != NULL)
+ {
+ right(FALSE);
+ delete(FALSE);
+ }
+ text_changes = TRUE;
+}
+
+void
+undel_line() /* undelete last deleted line */
+{
+ unsigned char *ud1;
+ unsigned char *ud2;
+ int tposit;
+
+ if (dlt_line->line_length == 0)
+ return;
+
+ insert_line(TRUE);
+ left(TRUE);
+ point = resiz_line(dlt_line->line_length, curr_line, position);
+ curr_line->line_length += dlt_line->line_length - 1;
+ ud1 = point;
+ ud2 = d_line;
+ tposit = 1;
+ while (tposit < dlt_line->line_length)
+ {
+ tposit++;
+ *ud1 = *ud2;
+ ud1++;
+ ud2++;
+ }
+ *ud1 = '\0';
+ draw_line(scr_vert, scr_horz,point,position,curr_line->line_length);
+}
+
+void
+adv_word() /* advance to next word */
+{
+while ((position < curr_line->line_length) && ((*point != 32) && (*point != 9)))
+ right(TRUE);
+while ((position < curr_line->line_length) && ((*point == 32) || (*point == 9)))
+ right(TRUE);
+}
+
+void
+move_rel(direction, lines) /* move relative to current line */
+int direction;
+int lines;
+{
+ int i;
+ char *tmp;
+
+ if (direction == 'u')
+ {
+ scr_pos = 0;
+ while (position > 1)
+ left(TRUE);
+ for (i = 0; i < lines; i++)
+ {
+ up();
+ }
+ if ((last_line > 5) && ( scr_vert < 4))
+ {
+ tmp = point;
+ tmp_line = curr_line;
+ for (i= 0;(i<5)&&(curr_line->prev_line != NULL); i++)
+ {
+ up();
+ }
+ scr_vert = scr_vert + i;
+ curr_line = tmp_line;
+ absolute_lin += i;
+ point = tmp;
+ scanline(point);
+ }
+ }
+ else
+ {
+ if ((position != 1) && (curr_line->next_line != NULL))
+ {
+ nextline();
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ }
+ else
+ adv_line();
+ for (i = 1; i < lines; i++)
+ {
+ down();
+ }
+ if ((last_line > 10) && (scr_vert > (last_line - 5)))
+ {
+ tmp = point;
+ tmp_line = curr_line;
+ for (i=0; (i<5) && (curr_line->next_line != NULL); i++)
+ {
+ down();
+ }
+ absolute_lin -= i;
+ scr_vert = scr_vert - i;
+ curr_line = tmp_line;
+ point = tmp;
+ scanline(point);
+ }
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+eol() /* go to end of line */
+{
+ if (position < curr_line->line_length)
+ {
+ while (position < curr_line->line_length)
+ right(TRUE);
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ right(TRUE);
+ while (position < curr_line->line_length)
+ right(TRUE);
+ }
+}
+
+void
+bol() /* move to beginning of line */
+{
+ if (point != curr_line->line)
+ {
+ while (point != curr_line->line)
+ left(TRUE);
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ scr_pos = 0;
+ up();
+ }
+}
+
+void
+adv_line() /* advance to beginning of next line */
+{
+ if ((point != curr_line->line) || (scr_pos > 0))
+ {
+ while (position < curr_line->line_length)
+ right(TRUE);
+ right(TRUE);
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ scr_pos = 0;
+ down();
+ }
+}
+
+void
+from_top()
+{
+ struct text *tmpline = first_line;
+ int x = 1;
+
+ while ((tmpline != NULL) && (tmpline != curr_line))
+ {
+ x++;
+ tmpline = tmpline->next_line;
+ }
+ absolute_lin = x;
+}
+
+void
+sh_command(string) /* execute shell command */
+char *string; /* string containing user command */
+{
+ char *temp_point;
+ char *last_slash;
+ char *path; /* directory path to executable */
+ int parent; /* zero if child, child's pid if parent */
+ int value;
+ int return_val;
+ struct text *line_holder;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+
+ if (!(path = getenv("SHELL")))
+ path = "/bin/sh";
+ last_slash = temp_point = path;
+ while (*temp_point != '\0')
+ {
+ if (*temp_point == '/')
+ last_slash = ++temp_point;
+ else
+ temp_point++;
+ }
+
+ /*
+ | if in_pipe is true, then output of the shell operation will be
+ | read by the editor, and curses doesn't need to be turned off
+ */
+
+ if (!in_pipe)
+ {
+ keypad(com_win, FALSE);
+ keypad(text_win, FALSE);
+ echo();
+ nl();
+ noraw();
+ resetty();
+
+#ifndef NCURSE
+ endwin();
+#endif
+ }
+
+ if (in_pipe)
+ {
+ pipe(pipe_in); /* create a pipe */
+ parent = fork();
+ if (!parent) /* if the child */
+ {
+/*
+ | child process which will fork and exec shell command (if shell output is
+ | to be read by editor)
+ */
+ in_pipe = FALSE;
+/*
+ | redirect stdout to pipe
+ */
+ temp_stdout = dup(1);
+ close(1);
+ dup(pipe_in[1]);
+/*
+ | redirect stderr to pipe
+ */
+ temp_stderr = dup(2);
+ close(2);
+ dup(pipe_in[1]);
+ close(pipe_in[1]);
+ /*
+ | child will now continue down 'if (!in_pipe)'
+ | path below
+ */
+ }
+ else /* if the parent */
+ {
+/*
+ | prepare editor to read from the pipe
+ */
+ signal(SIGCHLD, SIG_IGN);
+ line_holder = curr_line;
+ tmp_vert = scr_vert;
+ close(pipe_in[1]);
+ get_fd = pipe_in[0];
+ get_file("");
+ close(pipe_in[0]);
+ scr_vert = tmp_vert;
+ scr_horz = scr_pos = 0;
+ position = 1;
+ curr_line = line_holder;
+ from_top();
+ point = curr_line->line;
+ out_pipe = FALSE;
+ signal(SIGCHLD, SIG_DFL);
+/*
+ | since flag "in_pipe" is still TRUE, the path which waits for the child
+ | process to die will be avoided.
+ | (the pipe is closed, no more output can be expected)
+ */
+ }
+ }
+ if (!in_pipe)
+ {
+ signal(SIGINT, SIG_IGN);
+ if (out_pipe)
+ {
+ pipe(pipe_out);
+ }
+/*
+ | fork process which will exec command
+ */
+ parent = fork();
+ if (!parent) /* if the child */
+ {
+ if (shell_fork)
+ putchar('\n');
+ if (out_pipe)
+ {
+/*
+ | prepare the child process (soon to exec a shell command) to read from the
+ | pipe (which will be output from the editor's buffer)
+ */
+ close(0);
+ dup(pipe_out[0]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ }
+ for (value = 1; value < 24; value++)
+ signal(value, SIG_DFL);
+ execl(path, last_slash, "-c", string, NULL);
+ fprintf(stderr, exec_err_msg, path);
+ exit(-1);
+ }
+ else /* if the parent */
+ {
+ if (out_pipe)
+ {
+/*
+ | output the contents of the buffer to the pipe (to be read by the
+ | process forked and exec'd above as stdin)
+ */
+ close(pipe_out[0]);
+ line_holder = first_line;
+ while (line_holder != NULL)
+ {
+ write(pipe_out[1], line_holder->line, (line_holder->line_length-1));
+ write(pipe_out[1], "\n", 1);
+ line_holder = line_holder->next_line;
+ }
+ close(pipe_out[1]);
+ out_pipe = FALSE;
+ }
+ do
+ {
+ return_val = wait((int *) 0);
+ }
+ while ((return_val != parent) && (return_val != -1));
+/*
+ | if this process is actually the child of the editor, exit. Here's how it
+ | works:
+ | The editor forks a process. If output must be sent to the command to be
+ | exec'd another process is forked, and that process (the child's child)
+ | will exec the command. In this case, "shell_fork" will be FALSE. If no
+ | output is to be performed to the shell command, "shell_fork" will be TRUE.
+ | If this is the editor process, shell_fork will be true, otherwise this is
+ | the child of the edit process.
+ */
+ if (!shell_fork)
+ exit(0);
+ }
+ signal(SIGINT, edit_abort);
+ }
+ if (shell_fork)
+ {
+ fputs(continue_msg, stdout);
+ fflush(stdout);
+ while ((in = getchar()) != '\n')
+ ;
+ }
+
+ if (!in_pipe)
+ {
+ fixterm();
+ noecho();
+ nonl();
+ raw();
+ keypad(text_win, TRUE);
+ keypad(com_win, TRUE);
+ if (info_window)
+ clearok(info_win, TRUE);
+ }
+
+ redraw();
+}
+
+void
+set_up_term() /* set up the terminal for operating with ae */
+{
+ if (!curses_initialized)
+ {
+ initscr();
+ savetty();
+ noecho();
+ raw();
+ nonl();
+ curses_initialized = TRUE;
+ }
+
+ if (((LINES > 15) && (COLS >= 80)) && info_window)
+ last_line = LINES - 8;
+ else
+ {
+ info_window = FALSE;
+ last_line = LINES - 2;
+ }
+
+ idlok(stdscr, TRUE);
+ com_win = newwin(1, COLS, (LINES - 1), 0);
+ keypad(com_win, TRUE);
+ idlok(com_win, TRUE);
+ wrefresh(com_win);
+ if (!info_window)
+ text_win = newwin((LINES - 1), COLS, 0, 0);
+ else
+ text_win = newwin((LINES - 7), COLS, 6, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ wrefresh(text_win);
+ help_win = newwin((LINES - 1), COLS, 0, 0);
+ keypad(help_win, TRUE);
+ idlok(help_win, TRUE);
+ if (info_window)
+ {
+ info_type = CONTROL_KEYS;
+ info_win = newwin(6, COLS, 0, 0);
+ werase(info_win);
+ paint_info_win();
+ }
+
+ last_col = COLS - 1;
+ local_LINES = LINES;
+ local_COLS = COLS;
+
+#ifdef NCURSE
+ if (ee_chinese)
+ nc_setattrib(A_NC_BIG5);
+#endif /* NCURSE */
+
+}
+
+void
+resize_check()
+{
+ if ((LINES == local_LINES) && (COLS == local_COLS))
+ return;
+
+ if (info_window)
+ delwin(info_win);
+ delwin(text_win);
+ delwin(com_win);
+ delwin(help_win);
+ set_up_term();
+ redraw();
+ wrefresh(text_win);
+}
+
+static char item_alpha[] = "abcdefghijklmnopqrstuvwxyz0123456789 ";
+
+int
+menu_op(menu_list)
+struct menu_entries menu_list[];
+{
+ WINDOW *temp_win;
+ int max_width, max_height;
+ int x_off, y_off;
+ int counter;
+ int length;
+ int input;
+ int temp;
+ int list_size;
+ int top_offset; /* offset from top where menu items start */
+ int vert_pos; /* vertical position */
+ int vert_size; /* vertical size for menu list item display */
+ int off_start = 1; /* offset from start of menu items to start display */
+
+
+ /*
+ | determine number and width of menu items
+ */
+
+ list_size = 1;
+ while (menu_list[list_size + 1].item_string != NULL)
+ list_size++;
+ max_width = 0;
+ for (counter = 0; counter <= list_size; counter++)
+ {
+ if ((length = strlen(menu_list[counter].item_string)) > max_width)
+ max_width = length;
+ }
+ max_width += 3;
+ max_width = max(max_width, strlen(menu_cancel_msg));
+ max_width = max(max_width, max(strlen(more_above_str), strlen(more_below_str)));
+ max_width += 6;
+
+ /*
+ | make sure that window is large enough to handle menu
+ | if not, print error message and return to calling function
+ */
+
+ if (max_width > COLS)
+ {
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ wprintw(com_win, menu_too_lrg_msg);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(0);
+ }
+
+ top_offset = 0;
+
+ if (list_size > LINES)
+ {
+ max_height = LINES;
+ if (max_height > 11)
+ vert_size = max_height - 8;
+ else
+ vert_size = max_height;
+ }
+ else
+ {
+ vert_size = list_size;
+ max_height = list_size;
+ }
+
+ if (LINES >= (vert_size + 8))
+ {
+ if (menu_list[0].argument != MENU_WARN)
+ max_height = vert_size + 8;
+ else
+ max_height = vert_size + 7;
+ top_offset = 4;
+ }
+ x_off = (COLS - max_width) / 2;
+ y_off = (LINES - max_height - 1) / 2;
+ temp_win = newwin(max_height, max_width, y_off, x_off);
+ keypad(temp_win, TRUE);
+
+ paint_menu(menu_list, max_width, max_height, list_size, top_offset, temp_win, off_start, vert_size);
+
+ counter = 1;
+ vert_pos = 0;
+ do
+ {
+ if (off_start > 2)
+ wmove(temp_win, (1 + counter + top_offset - off_start), 3);
+ else
+ wmove(temp_win, (counter + top_offset - off_start), 3);
+
+ wrefresh(temp_win);
+ in = wgetch(temp_win);
+ input = in;
+ if (input == -1)
+ exit(0);
+
+ if (isascii(input) && isalnum(input))
+ {
+ if (isalpha(input))
+ {
+ temp = 1 + tolower(input) - 'a';
+ }
+ else if (isdigit(input))
+ {
+ temp = (2 + 'z' - 'a') + (input - '0');
+ }
+
+ if (temp <= list_size)
+ {
+ input = '\n';
+ counter = temp;
+ }
+ }
+ else
+ {
+ switch (input)
+ {
+ case ' ': /* space */
+ case '\004': /* ^d, down */
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ counter++;
+ if (counter > list_size)
+ counter = 1;
+ break;
+ case '\010': /* ^h, backspace*/
+ case '\025': /* ^u, up */
+ case 127: /* ^?, delete */
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case KEY_UP:
+ counter--;
+ if (counter == 0)
+ counter = list_size;
+ break;
+ case '\033': /* escape key */
+ if (menu_list[0].argument != MENU_WARN)
+ counter = 0;
+ break;
+ case '\014': /* ^l */
+ case '\022': /* ^r, redraw */
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win,
+ off_start, vert_size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (((list_size - off_start) >= (vert_size - 1)) &&
+ (counter > (off_start + vert_size - 3)) &&
+ (off_start > 1))
+ {
+ if (counter == list_size)
+ off_start = (list_size - vert_size) + 2;
+ else
+ off_start++;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ else if ((list_size != vert_size) &&
+ (counter > (off_start + vert_size - 2)))
+ {
+ if (counter == list_size)
+ off_start = 2 + (list_size - vert_size);
+ else if (off_start == 1)
+ off_start = 3;
+ else
+ off_start++;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ else if (counter < off_start)
+ {
+ if (counter <= 2)
+ off_start = 1;
+ else
+ off_start = counter;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ }
+ while ((input != '\r') && (input != '\n') && (counter != 0));
+
+ werase(temp_win);
+ wrefresh(temp_win);
+ delwin(temp_win);
+
+ if ((menu_list[counter].procedure != NULL) ||
+ (menu_list[counter].iprocedure != NULL) ||
+ (menu_list[counter].nprocedure != NULL))
+ {
+ if (menu_list[counter].argument != -1)
+ (*menu_list[counter].iprocedure)(menu_list[counter].argument);
+ else if (menu_list[counter].ptr_argument != NULL)
+ (*menu_list[counter].procedure)(menu_list[counter].ptr_argument);
+ else
+ (*menu_list[counter].nprocedure)();
+ }
+
+ if (info_window)
+ paint_info_win();
+ redraw();
+
+ return(counter);
+}
+
+void
+paint_menu(menu_list, max_width, max_height, list_size, top_offset, menu_win,
+ off_start, vert_size)
+struct menu_entries menu_list[];
+int max_width, max_height, list_size, top_offset;
+WINDOW *menu_win;
+int off_start, vert_size;
+{
+ int counter, temp_int;
+
+ werase(menu_win);
+
+ /*
+ | output top and bottom portions of menu box only if window
+ | large enough
+ */
+
+ if (max_height > vert_size)
+ {
+ wmove(menu_win, 1, 1);
+ if (!nohighlight)
+ wstandout(menu_win);
+ waddch(menu_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(menu_win, '-');
+ waddch(menu_win, '+');
+
+ wmove(menu_win, (max_height - 2), 1);
+ waddch(menu_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(menu_win, '-');
+ waddch(menu_win, '+');
+ wstandend(menu_win);
+ wmove(menu_win, 2, 3);
+ waddstr(menu_win, menu_list[0].item_string);
+ wmove(menu_win, (max_height - 3), 3);
+ if (menu_list[0].argument != MENU_WARN)
+ waddstr(menu_win, menu_cancel_msg);
+ }
+ if (!nohighlight)
+ wstandout(menu_win);
+
+ for (counter = 0; counter < (vert_size + top_offset); counter++)
+ {
+ if (top_offset == 4)
+ {
+ temp_int = counter + 2;
+ }
+ else
+ temp_int = counter;
+
+ wmove(menu_win, temp_int, 1);
+ waddch(menu_win, '|');
+ wmove(menu_win, temp_int, (max_width - 2));
+ waddch(menu_win, '|');
+ }
+ wstandend(menu_win);
+
+ if (list_size > vert_size)
+ {
+ if (off_start >= 3)
+ {
+ temp_int = 1;
+ wmove(menu_win, top_offset, 3);
+ waddstr(menu_win, more_above_str);
+ }
+ else
+ temp_int = 0;
+
+ for (counter = off_start;
+ ((temp_int + counter - off_start) < (vert_size - 1));
+ counter++)
+ {
+ wmove(menu_win, (top_offset + temp_int +
+ (counter - off_start)), 3);
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ waddstr(menu_win, menu_list[counter].item_string);
+ }
+
+ wmove(menu_win, (top_offset + (vert_size - 1)), 3);
+
+ if (counter == list_size)
+ {
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ wprintw(menu_win, menu_list[counter].item_string);
+ }
+ else
+ wprintw(menu_win, more_below_str);
+ }
+ else
+ {
+ for (counter = 1; counter <= list_size; counter++)
+ {
+ wmove(menu_win, (top_offset + counter - 1), 3);
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ waddstr(menu_win, menu_list[counter].item_string);
+ }
+ }
+}
+
+void
+help()
+{
+ int counter;
+
+ werase(help_win);
+ clearok(help_win, TRUE);
+ for (counter = 0; counter < 22; counter++)
+ {
+ wmove(help_win, counter, 0);
+ waddstr(help_win, (emacs_keys_mode) ?
+ emacs_help_text[counter] : help_text[counter]);
+ }
+ wrefresh(help_win);
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ wprintw(com_win, press_any_key_msg);
+ wrefresh(com_win);
+ counter = wgetch(com_win);
+ if (counter == -1)
+ exit(0);
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ werase(help_win);
+ wrefresh(help_win);
+ wrefresh(com_win);
+ redraw();
+}
+
+void
+paint_info_win()
+{
+ int counter;
+
+ if (!info_window)
+ return;
+
+ werase(info_win);
+ for (counter = 0; counter < 5; counter++)
+ {
+ wmove(info_win, counter, 0);
+ wclrtoeol(info_win);
+ if (info_type == CONTROL_KEYS)
+ waddstr(info_win, (emacs_keys_mode) ?
+ emacs_control_keys[counter] : control_keys[counter]);
+ else if (info_type == COMMANDS)
+ waddstr(info_win, command_strings[counter]);
+ }
+ wmove(info_win, 5, 0);
+ if (!nohighlight)
+ wstandout(info_win);
+ waddstr(info_win, separator);
+ wstandend(info_win);
+ wrefresh(info_win);
+}
+
+void
+no_info_window()
+{
+ if (!info_window)
+ return;
+ delwin(info_win);
+ delwin(text_win);
+ info_window = FALSE;
+ last_line = LINES - 2;
+ text_win = newwin((LINES - 1), COLS, 0, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ clearok(text_win, TRUE);
+ midscreen(scr_vert, point);
+ wrefresh(text_win);
+ clear_com_win = TRUE;
+}
+
+void
+create_info_window()
+{
+ if (info_window)
+ return;
+ last_line = LINES - 8;
+ delwin(text_win);
+ text_win = newwin((LINES - 7), COLS, 6, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ werase(text_win);
+ info_window = TRUE;
+ info_win = newwin(6, COLS, 0, 0);
+ werase(info_win);
+ info_type = CONTROL_KEYS;
+ midscreen(min(scr_vert, last_line), point);
+ clearok(info_win, TRUE);
+ paint_info_win();
+ wrefresh(text_win);
+ clear_com_win = TRUE;
+}
+
+int
+file_op(arg)
+int arg;
+{
+ char *string;
+ int flag;
+
+ if (restrict_mode())
+ {
+ return(0);
+ }
+
+ if (arg == READ_FILE)
+ {
+ string = get_string(file_read_prompt_str, TRUE);
+ recv_file = TRUE;
+ tmp_file = resolve_name(string);
+ check_fp();
+ if (tmp_file != string)
+ free(tmp_file);
+ free(string);
+ }
+ else if (arg == WRITE_FILE)
+ {
+ string = get_string(file_write_prompt_str, TRUE);
+ tmp_file = resolve_name(string);
+ write_file(tmp_file, 1);
+ if (tmp_file != string)
+ free(tmp_file);
+ free(string);
+ }
+ else if (arg == SAVE_FILE)
+ {
+ /*
+ | changes made here should be reflected in finish()
+ */
+
+ if (in_file_name)
+ flag = TRUE;
+ else
+ flag = FALSE;
+
+ string = in_file_name;
+ if ((string == NULL) || (*string == '\0'))
+ string = get_string(save_file_name_prompt, TRUE);
+ if ((string == NULL) || (*string == '\0'))
+ {
+ wmove(com_win, 0, 0);
+ wprintw(com_win, file_not_saved_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(0);
+ }
+ if (!flag)
+ {
+ tmp_file = resolve_name(string);
+ if (tmp_file != string)
+ {
+ free(string);
+ string = tmp_file;
+ }
+ }
+ if (write_file(string, 1))
+ {
+ in_file_name = string;
+ text_changes = FALSE;
+ }
+ else if (!flag)
+ free(string);
+ }
+ return(0);
+}
+
+void
+shell_op()
+{
+ char *string;
+
+ if (((string = get_string(shell_prompt, TRUE)) != NULL) &&
+ (*string != '\0'))
+ {
+ sh_command(string);
+ free(string);
+ }
+}
+
+void
+leave_op()
+{
+ if (text_changes)
+ {
+ menu_op(leave_menu);
+ }
+ else
+ quit(TRUE);
+}
+
+void
+redraw()
+{
+ if (info_window)
+ {
+ clearok(info_win, TRUE);
+ paint_info_win();
+ }
+ else
+ clearok(text_win, TRUE);
+ midscreen(scr_vert, point);
+}
+
+/*
+ | The following routines will "format" a paragraph (as defined by a
+ | block of text with blank lines before and after the block).
+ */
+
+int
+Blank_Line(test_line) /* test if line has any non-space characters */
+struct text *test_line;
+{
+ unsigned char *line;
+ int length;
+
+ if (test_line == NULL)
+ return(TRUE);
+
+ length = 1;
+ line = test_line->line;
+
+ /*
+ | To handle troff/nroff documents, consider a line with a
+ | period ('.') in the first column to be blank. To handle mail
+ | messages with included text, consider a line with a '>' blank.
+ */
+
+ if ((*line == '.') || (*line == '>'))
+ return(TRUE);
+
+ while (((*line == ' ') || (*line == '\t')) && (length < test_line->line_length))
+ {
+ length++;
+ line++;
+ }
+ if (length != test_line->line_length)
+ return(FALSE);
+ else
+ return(TRUE);
+}
+
+void
+Format() /* format the paragraph according to set margins */
+{
+ int string_count;
+ int offset;
+ int temp_case;
+ int status;
+ int tmp_af;
+ int counter;
+ unsigned char *line;
+ unsigned char *tmp_srchstr;
+ unsigned char *temp1, *temp2;
+ unsigned char *temp_dword;
+ unsigned char temp_d_char[3];
+
+ temp_d_char[0] = d_char[0];
+ temp_d_char[1] = d_char[1];
+ temp_d_char[2] = d_char[2];
+
+/*
+ | if observ_margins is not set, or the current line is blank,
+ | do not format the current paragraph
+ */
+
+ if ((!observ_margins) || (Blank_Line(curr_line)))
+ return;
+
+/*
+ | save the currently set flags, and clear them
+ */
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | get current position in paragraph, so after formatting, the cursor
+ | will be in the same relative position
+ */
+
+ tmp_af = auto_format;
+ auto_format = FALSE;
+ offset = position;
+ if (position != 1)
+ prev_word();
+ temp_dword = d_word;
+ d_word = NULL;
+ temp_case = case_sen;
+ case_sen = TRUE;
+ tmp_srchstr = srch_str;
+ temp2 = srch_str = (unsigned char *) malloc(1 + curr_line->line_length - position);
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ offset -= position;
+ counter = position;
+ line = temp1 = point;
+ while ((*temp1 != '\0') && (*temp1 != ' ') && (*temp1 != '\t') && (counter < curr_line->line_length))
+ {
+ *temp2 = *temp1;
+ temp2++;
+ temp1++;
+ counter++;
+ }
+ *temp2 = '\0';
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+ string_count = 0;
+ status = TRUE;
+ while ((line != point) && (status))
+ {
+ status = search(FALSE);
+ string_count++;
+ }
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | now get back to the start of the paragraph to start formatting
+ */
+
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+ observ_margins = FALSE;
+
+/*
+ | Start going through lines, putting spaces at end of lines if they do
+ | not already exist. Append lines together to get one long line, and
+ | eliminate spacing at begin of lines.
+ */
+
+ while (!Blank_Line(curr_line->next_line))
+ {
+ eol();
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+ del_char();
+ if ((*point == ' ') || (*point == '\t'))
+ del_word();
+ }
+
+/*
+ | Now there is one long line. Eliminate extra spaces within the line
+ | after the first word (so as not to blow away any indenting the user
+ | may have put in).
+ */
+
+ bol();
+ adv_word();
+ while (position < curr_line->line_length)
+ {
+ if ((*point == ' ') && (*(point + 1) == ' '))
+ del_char();
+ else
+ right(TRUE);
+ }
+
+/*
+ | Now make sure there are two spaces after a '.'.
+ */
+
+ bol();
+ while (position < curr_line->line_length)
+ {
+ if ((*point == '.') && (*(point + 1) == ' '))
+ {
+ right(TRUE);
+ insert(' ');
+ insert(' ');
+ while (*point == ' ')
+ del_char();
+ }
+ right(TRUE);
+ }
+
+ observ_margins = TRUE;
+ bol();
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | create lines between margins
+ */
+
+ while (position < curr_line->line_length)
+ {
+ while ((scr_pos < right_margin) && (position < curr_line->line_length))
+ right(TRUE);
+ if (position < curr_line->line_length)
+ {
+ prev_word();
+ if (position == 1)
+ adv_word();
+ insert_line(TRUE);
+ }
+ }
+
+/*
+ | go back to begin of paragraph, put cursor back to original position
+ */
+
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+/*
+ | find word cursor was in
+ */
+
+ while ((status) && (string_count > 0))
+ {
+ search(FALSE);
+ string_count--;
+ }
+
+/*
+ | offset the cursor to where it was before from the start of the word
+ */
+
+ while (offset > 0)
+ {
+ offset--;
+ right(TRUE);
+ }
+
+/*
+ | reset flags and strings to what they were before formatting
+ */
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = temp_dword;
+ case_sen = temp_case;
+ free(srch_str);
+ srch_str = tmp_srchstr;
+ d_char[0] = temp_d_char[0];
+ d_char[1] = temp_d_char[1];
+ d_char[2] = temp_d_char[2];
+ auto_format = tmp_af;
+
+ midscreen(scr_vert, point);
+ werase(com_win);
+ wrefresh(com_win);
+}
+
+unsigned char *init_name[3] = {
+ "/usr/share/misc/init.ee",
+ NULL,
+ ".init.ee"
+ };
+
+void
+ee_init() /* check for init file and read it if it exists */
+{
+ FILE *init_file;
+ unsigned char *string;
+ unsigned char *str1;
+ unsigned char *str2;
+ char *home;
+ int counter;
+ int temp_int;
+
+ string = getenv("HOME");
+ if (string == NULL)
+ string = "/tmp";
+ str1 = home = malloc(strlen(string)+10);
+ strcpy(home, string);
+ strcat(home, "/.init.ee");
+ init_name[1] = home;
+ string = malloc(512);
+
+ for (counter = 0; counter < 3; counter++)
+ {
+ if (!(access(init_name[counter], 4)))
+ {
+ init_file = fopen(init_name[counter], "r");
+ while ((str2 = fgets(string, 512, init_file)) != NULL)
+ {
+ str1 = str2 = string;
+ while (*str2 != '\n')
+ str2++;
+ *str2 = '\0';
+
+ if (unique_test(string, init_strings) != 1)
+ continue;
+
+ if (compare(str1, CASE, FALSE))
+ case_sen = TRUE;
+ else if (compare(str1, NOCASE, FALSE))
+ case_sen = FALSE;
+ else if (compare(str1, EXPAND, FALSE))
+ expand_tabs = TRUE;
+ else if (compare(str1, NOEXPAND, FALSE))
+ expand_tabs = FALSE;
+ else if (compare(str1, INFO, FALSE))
+ info_window = TRUE;
+ else if (compare(str1, NOINFO, FALSE))
+ info_window = FALSE;
+ else if (compare(str1, MARGINS, FALSE))
+ observ_margins = TRUE;
+ else if (compare(str1, NOMARGINS, FALSE))
+ observ_margins = FALSE;
+ else if (compare(str1, AUTOFORMAT, FALSE))
+ {
+ auto_format = TRUE;
+ observ_margins = TRUE;
+ }
+ else if (compare(str1, NOAUTOFORMAT, FALSE))
+ auto_format = FALSE;
+ else if (compare(str1, Echo, FALSE))
+ {
+ str1 = next_word(str1);
+ if (*str1 != '\0')
+ echo_string(str1);
+ }
+ else if (compare(str1, PRINTCOMMAND, FALSE))
+ {
+ str1 = next_word(str1);
+ print_command = malloc(strlen(str1)+1);
+ strcpy(print_command, str1);
+ }
+ else if (compare(str1, RIGHTMARGIN, FALSE))
+ {
+ str1 = next_word(str1);
+ if ((*str1 >= '0') && (*str1 <= '9'))
+ {
+ temp_int = atoi(str1);
+ if (temp_int > 0)
+ right_margin = temp_int;
+ }
+ }
+ else if (compare(str1, HIGHLIGHT, FALSE))
+ nohighlight = FALSE;
+ else if (compare(str1, NOHIGHLIGHT, FALSE))
+ nohighlight = TRUE;
+ else if (compare(str1, EIGHTBIT, FALSE))
+ eightbit = TRUE;
+ else if (compare(str1, NOEIGHTBIT, FALSE))
+ {
+ eightbit = FALSE;
+ ee_chinese = FALSE;
+ }
+ else if (compare(str1, EMACS_string, FALSE))
+ emacs_keys_mode = TRUE;
+ else if (compare(str1, NOEMACS_string, FALSE))
+ emacs_keys_mode = FALSE;
+ else if (compare(str1, chinese_cmd, FALSE))
+ {
+ ee_chinese = TRUE;
+ eightbit = TRUE;
+ }
+ else if (compare(str1, nochinese_cmd, FALSE))
+ ee_chinese = FALSE;
+ }
+ fclose(init_file);
+ }
+ }
+ free(string);
+ free(home);
+
+ string = getenv("LANG");
+ if (string != NULL)
+ {
+ if (strcmp(string, "zh_TW.big5") == 0)
+ {
+ ee_chinese = TRUE;
+ eightbit = TRUE;
+ }
+ }
+}
+
+/*
+ | Save current configuration to .init.ee file in the current directory.
+ */
+
+void
+dump_ee_conf()
+{
+ FILE *init_file;
+ FILE *old_init_file = NULL;
+ char *file_name = ".init.ee";
+ char *home_dir = "~/.init.ee";
+ char buffer[512];
+ struct stat buf;
+ char *string;
+ int length;
+ int option = 0;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+
+ option = menu_op(config_dump_menu);
+
+ werase(com_win);
+ wmove(com_win, 0, 0);
+
+ if (option == 0)
+ {
+ wprintw(com_win, conf_not_saved_msg);
+ wrefresh(com_win);
+ return;
+ }
+ else if (option == 2)
+ file_name = resolve_name(home_dir);
+
+ /*
+ | If a .init.ee file exists, move it to .init.ee.old.
+ */
+
+ if (stat(file_name, &buf) != -1)
+ {
+ sprintf(buffer, "%s.old", file_name);
+ unlink(buffer);
+ link(file_name, buffer);
+ unlink(file_name);
+ old_init_file = fopen(buffer, "r");
+ }
+
+ init_file = fopen(file_name, "w");
+ if (init_file == NULL)
+ {
+ wprintw(com_win, conf_dump_err_msg);
+ wrefresh(com_win);
+ return;
+ }
+
+ if (old_init_file != NULL)
+ {
+ /*
+ | Copy non-configuration info into new .init.ee file.
+ */
+ while ((string = fgets(buffer, 512, old_init_file)) != NULL)
+ {
+ length = strlen(string);
+ string[length - 1] = '\0';
+
+ if (unique_test(string, init_strings) == 1)
+ {
+ if (compare(string, Echo, FALSE))
+ {
+ fprintf(init_file, "%s\n", string);
+ }
+ }
+ else
+ fprintf(init_file, "%s\n", string);
+ }
+
+ fclose(old_init_file);
+ }
+
+ fprintf(init_file, "%s\n", case_sen ? CASE : NOCASE);
+ fprintf(init_file, "%s\n", expand_tabs ? EXPAND : NOEXPAND);
+ fprintf(init_file, "%s\n", info_window ? INFO : NOINFO );
+ fprintf(init_file, "%s\n", observ_margins ? MARGINS : NOMARGINS );
+ fprintf(init_file, "%s\n", auto_format ? AUTOFORMAT : NOAUTOFORMAT );
+ fprintf(init_file, "%s %s\n", PRINTCOMMAND, print_command);
+ fprintf(init_file, "%s %d\n", RIGHTMARGIN, right_margin);
+ fprintf(init_file, "%s\n", nohighlight ? NOHIGHLIGHT : HIGHLIGHT );
+ fprintf(init_file, "%s\n", eightbit ? EIGHTBIT : NOEIGHTBIT );
+ fprintf(init_file, "%s\n", emacs_keys_mode ? EMACS_string : NOEMACS_string );
+ fprintf(init_file, "%s\n", ee_chinese ? chinese_cmd : nochinese_cmd );
+
+ fclose(init_file);
+
+ wprintw(com_win, conf_dump_success_msg, file_name);
+ wrefresh(com_win);
+
+ if ((option == 2) && (file_name != home_dir))
+ {
+ free(file_name);
+ }
+}
+
+void
+echo_string(string) /* echo the given string */
+char *string;
+{
+ char *temp;
+ int Counter;
+
+ temp = string;
+ while (*temp != '\0')
+ {
+ if (*temp == '\\')
+ {
+ temp++;
+ if (*temp == 'n')
+ putchar('\n');
+ else if (*temp == 't')
+ putchar('\t');
+ else if (*temp == 'b')
+ putchar('\b');
+ else if (*temp == 'r')
+ putchar('\r');
+ else if (*temp == 'f')
+ putchar('\f');
+ else if ((*temp == 'e') || (*temp == 'E'))
+ putchar('\033'); /* escape */
+ else if (*temp == '\\')
+ putchar('\\');
+ else if (*temp == '\'')
+ putchar('\'');
+ else if ((*temp >= '0') && (*temp <= '9'))
+ {
+ Counter = 0;
+ while ((*temp >= '0') && (*temp <= '9'))
+ {
+ Counter = (8 * Counter) + (*temp - '0');
+ temp++;
+ }
+ putchar(Counter);
+ temp--;
+ }
+ temp++;
+ }
+ else
+ {
+ putchar(*temp);
+ temp++;
+ }
+ }
+
+ fflush(stdout);
+}
+
+void
+spell_op() /* check spelling of words in the editor */
+{
+ if (restrict_mode())
+ {
+ return;
+ }
+ top(); /* go to top of file */
+ insert_line(FALSE); /* create two blank lines */
+ insert_line(FALSE);
+ top();
+ command(shell_echo_msg);
+ adv_line();
+ wmove(com_win, 0, 0);
+ wprintw(com_win, spell_in_prog_msg);
+ wrefresh(com_win);
+ command("<>!spell"); /* send contents of buffer to command 'spell'
+ and read the results back into the editor */
+}
+
+void
+ispell_op()
+{
+ char template[128], *name;
+ char string[256];
+ int fd;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+ (void)sprintf(template, "/tmp/ee.XXXXXXXX");
+ fd = mkstemp(template);
+ if (fd < 0) {
+ wmove(com_win, 0, 0);
+ wprintw(com_win, create_file_fail_msg, name);
+ wrefresh(com_win);
+ return;
+ }
+ close(fd);
+ if (write_file(name, 0))
+ {
+ sprintf(string, "ispell %s", name);
+ sh_command(string);
+ delete_text();
+ tmp_file = name;
+ recv_file = TRUE;
+ check_fp();
+ unlink(name);
+ }
+}
+
+int
+first_word_len(test_line)
+struct text *test_line;
+{
+ int counter;
+ unsigned char *pnt;
+
+ if (test_line == NULL)
+ return(0);
+
+ pnt = test_line->line;
+ if ((pnt == NULL) || (*pnt == '\0') ||
+ (*pnt == '.') || (*pnt == '>'))
+ return(0);
+
+ if ((*pnt == ' ') || (*pnt == '\t'))
+ {
+ pnt = next_word(pnt);
+ }
+
+ if (*pnt == '\0')
+ return(0);
+
+ counter = 0;
+ while ((*pnt != '\0') && ((*pnt != ' ') && (*pnt != '\t')))
+ {
+ pnt++;
+ counter++;
+ }
+ while ((*pnt != '\0') && ((*pnt == ' ') || (*pnt == '\t')))
+ {
+ pnt++;
+ counter++;
+ }
+ return(counter);
+}
+
+void
+Auto_Format() /* format the paragraph according to set margins */
+{
+ int string_count;
+ int offset;
+ int temp_case;
+ int word_len;
+ int temp_dwl;
+ int tmp_d_line_length;
+ int leave_loop = FALSE;
+ int status;
+ int counter;
+ char not_blank;
+ unsigned char *line;
+ unsigned char *tmp_srchstr;
+ unsigned char *temp1, *temp2;
+ unsigned char *temp_dword;
+ unsigned char temp_d_char[3];
+ unsigned char *tmp_d_line;
+
+
+ temp_d_char[0] = d_char[0];
+ temp_d_char[1] = d_char[1];
+ temp_d_char[2] = d_char[2];
+
+/*
+ | if observ_margins is not set, or the current line is blank,
+ | do not format the current paragraph
+ */
+
+ if ((!observ_margins) || (Blank_Line(curr_line)))
+ return;
+
+/*
+ | get current position in paragraph, so after formatting, the cursor
+ | will be in the same relative position
+ */
+
+ tmp_d_line = d_line;
+ tmp_d_line_length = dlt_line->line_length;
+ d_line = NULL;
+ auto_format = FALSE;
+ offset = position;
+ if ((position != 1) && ((*point == ' ') || (*point == '\t') || (position == curr_line->line_length) || (*point == '\0')))
+ prev_word();
+ temp_dword = d_word;
+ temp_dwl = d_wrd_len;
+ d_wrd_len = 0;
+ d_word = NULL;
+ temp_case = case_sen;
+ case_sen = TRUE;
+ tmp_srchstr = srch_str;
+ temp2 = srch_str = (unsigned char *) malloc(1 + curr_line->line_length - position);
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ offset -= position;
+ counter = position;
+ line = temp1 = point;
+ while ((*temp1 != '\0') && (*temp1 != ' ') && (*temp1 != '\t') && (counter < curr_line->line_length))
+ {
+ *temp2 = *temp1;
+ temp2++;
+ temp1++;
+ counter++;
+ }
+ *temp2 = '\0';
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+ string_count = 0;
+ status = TRUE;
+ while ((line != point) && (status))
+ {
+ status = search(FALSE);
+ string_count++;
+ }
+
+/*
+ | now get back to the start of the paragraph to start checking
+ */
+
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+/*
+ | Start going through lines, putting spaces at end of lines if they do
+ | not already exist. Check line length, and move words to the next line
+ | if they cross the margin. Then get words from the next line if they
+ | will fit in before the margin.
+ */
+
+ counter = 0;
+
+ while (!leave_loop)
+ {
+ if (position != curr_line->line_length)
+ eol();
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+
+ not_blank = FALSE;
+
+ /*
+ | fill line if first word on next line will fit
+ | in the line without crossing the margin
+ */
+
+ while ((curr_line->next_line != NULL) &&
+ ((word_len = first_word_len(curr_line->next_line)) > 0)
+ && ((scr_pos + word_len) < right_margin))
+ {
+ adv_line();
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ del_word();
+ if (position != 1)
+ bol();
+
+ /*
+ | We know this line was not blank before, so
+ | make sure that it doesn't have one of the
+ | leading characters that indicate the line
+ | should not be modified.
+ |
+ | We also know that this character should not
+ | be left as the first character of this line.
+ */
+
+ if ((Blank_Line(curr_line)) &&
+ (curr_line->line[0] != '.') &&
+ (curr_line->line[0] != '>'))
+ {
+ del_line();
+ not_blank = FALSE;
+ }
+ else
+ not_blank = TRUE;
+
+ /*
+ | go to end of previous line
+ */
+ left(TRUE);
+ undel_word();
+ eol();
+ /*
+ | make sure there's a space at the end of the line
+ */
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+ }
+
+ /*
+ | make sure line does not cross right margin
+ */
+
+ while (right_margin <= scr_pos)
+ {
+ prev_word();
+ if (position != 1)
+ {
+ del_word();
+ if (Blank_Line(curr_line->next_line))
+ insert_line(TRUE);
+ else
+ adv_line();
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ undel_word();
+ not_blank = TRUE;
+ if (position != 1)
+ bol();
+ left(TRUE);
+ }
+ }
+
+ if ((!Blank_Line(curr_line->next_line)) || (not_blank))
+ {
+ adv_line();
+ counter++;
+ }
+ else
+ leave_loop = TRUE;
+ }
+
+/*
+ | go back to begin of paragraph, put cursor back to original position
+ */
+
+ if (position != 1)
+ bol();
+ while ((counter-- > 0) || (!Blank_Line(curr_line->prev_line)))
+ bol();
+
+/*
+ | find word cursor was in
+ */
+
+ status = TRUE;
+ while ((status) && (string_count > 0))
+ {
+ status = search(FALSE);
+ string_count--;
+ }
+
+/*
+ | offset the cursor to where it was before from the start of the word
+ */
+
+ while (offset > 0)
+ {
+ offset--;
+ right(TRUE);
+ }
+
+ if ((string_count > 0) && (offset < 0))
+ {
+ while (offset < 0)
+ {
+ offset++;
+ left(TRUE);
+ }
+ }
+
+/*
+ | reset flags and strings to what they were before formatting
+ */
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = temp_dword;
+ d_wrd_len = temp_dwl;
+ case_sen = temp_case;
+ free(srch_str);
+ srch_str = tmp_srchstr;
+ d_char[0] = temp_d_char[0];
+ d_char[1] = temp_d_char[1];
+ d_char[2] = temp_d_char[2];
+ auto_format = TRUE;
+ dlt_line->line_length = tmp_d_line_length;
+ d_line = tmp_d_line;
+
+ formatted = TRUE;
+ midscreen(scr_vert, point);
+}
+
+void
+modes_op()
+{
+ int ret_value;
+ int counter;
+ char *string;
+
+ do
+ {
+ sprintf(modes_menu[1].item_string, "%s %s", mode_strings[1],
+ (expand_tabs ? ON : OFF));
+ sprintf(modes_menu[2].item_string, "%s %s", mode_strings[2],
+ (case_sen ? ON : OFF));
+ sprintf(modes_menu[3].item_string, "%s %s", mode_strings[3],
+ (observ_margins ? ON : OFF));
+ sprintf(modes_menu[4].item_string, "%s %s", mode_strings[4],
+ (auto_format ? ON : OFF));
+ sprintf(modes_menu[5].item_string, "%s %s", mode_strings[5],
+ (eightbit ? ON : OFF));
+ sprintf(modes_menu[6].item_string, "%s %s", mode_strings[6],
+ (info_window ? ON : OFF));
+ sprintf(modes_menu[7].item_string, "%s %s", mode_strings[7],
+ (emacs_keys_mode ? ON : OFF));
+ sprintf(modes_menu[8].item_string, "%s %d", mode_strings[8],
+ right_margin);
+ sprintf(modes_menu[9].item_string, "%s %s", mode_strings[9],
+ (ee_chinese ? ON : OFF));
+
+ ret_value = menu_op(modes_menu);
+
+ switch (ret_value)
+ {
+ case 1:
+ expand_tabs = !expand_tabs;
+ break;
+ case 2:
+ case_sen = !case_sen;
+ break;
+ case 3:
+ observ_margins = !observ_margins;
+ break;
+ case 4:
+ auto_format = !auto_format;
+ if (auto_format)
+ observ_margins = TRUE;
+ break;
+ case 5:
+ eightbit = !eightbit;
+ if (!eightbit)
+ ee_chinese = FALSE;
+#ifdef NCURSE
+ if (ee_chinese)
+ nc_setattrib(A_NC_BIG5);
+ else
+ nc_clearattrib(A_NC_BIG5);
+#endif /* NCURSE */
+
+ redraw();
+ wnoutrefresh(text_win);
+ break;
+ case 6:
+ if (info_window)
+ no_info_window();
+ else
+ create_info_window();
+ break;
+ case 7:
+ emacs_keys_mode = !emacs_keys_mode;
+ if (info_window)
+ paint_info_win();
+ break;
+ case 8:
+ string = get_string(margin_prompt, TRUE);
+ if (string != NULL)
+ {
+ counter = atoi(string);
+ if (counter > 0)
+ right_margin = counter;
+ free(string);
+ }
+ break;
+ case 9:
+ ee_chinese = !ee_chinese;
+ if (ee_chinese != FALSE)
+ eightbit = TRUE;
+#ifdef NCURSE
+ if (ee_chinese)
+ nc_setattrib(A_NC_BIG5);
+ else
+ nc_clearattrib(A_NC_BIG5);
+#endif /* NCURSE */
+ redraw();
+ break;
+ default:
+ break;
+ }
+ }
+ while (ret_value != 0);
+}
+
+char *
+is_in_string(string, substring) /* a strchr() look-alike for systems without
+ strchr() */
+char * string, *substring;
+{
+ char *full, *sub;
+
+ for (sub = substring; (sub != NULL) && (*sub != '\0'); sub++)
+ {
+ for (full = string; (full != NULL) && (*full != '\0');
+ full++)
+ {
+ if (*sub == *full)
+ return(full);
+ }
+ }
+ return(NULL);
+}
+
+/*
+ | handle names of the form "~/file", "~user/file",
+ | "$HOME/foo", "~/$FOO", etc.
+ */
+
+char *
+resolve_name(name)
+char *name;
+{
+ char long_buffer[1024];
+ char short_buffer[128];
+ char *buffer;
+ char *slash;
+ char *tmp;
+ char *start_of_var;
+ int offset;
+ int index;
+ int counter;
+ struct passwd *user;
+
+ if (name[0] == '~')
+ {
+ if (name[1] == '/')
+ {
+ index = getuid();
+ user = (struct passwd *) getpwuid(index);
+ slash = name + 1;
+ }
+ else
+ {
+ slash = strchr(name, '/');
+ if (slash == NULL)
+ return(name);
+ *slash = '\0';
+ user = (struct passwd *) getpwnam((name + 1));
+ *slash = '/';
+ }
+ if (user == NULL)
+ {
+ return(name);
+ }
+ buffer = malloc(strlen(user->pw_dir) + strlen(slash) + 1);
+ strcpy(buffer, user->pw_dir);
+ strcat(buffer, slash);
+ }
+ else
+ buffer = name;
+
+ if (is_in_string(buffer, "$"))
+ {
+ tmp = buffer;
+ index = 0;
+
+ while ((*tmp != '\0') && (index < 1024))
+ {
+
+ while ((*tmp != '\0') && (*tmp != '$') &&
+ (index < 1024))
+ {
+ long_buffer[index] = *tmp;
+ tmp++;
+ index++;
+ }
+
+ if ((*tmp == '$') && (index < 1024))
+ {
+ counter = 0;
+ start_of_var = tmp;
+ tmp++;
+ if (*tmp == '{') /* } */ /* bracketed variable name */
+ {
+ tmp++; /* { */
+ while ((*tmp != '\0') &&
+ (*tmp != '}') &&
+ (counter < 128))
+ {
+ short_buffer[counter] = *tmp;
+ counter++;
+ tmp++;
+ } /* { */
+ if (*tmp == '}')
+ tmp++;
+ }
+ else
+ {
+ while ((*tmp != '\0') &&
+ (*tmp != '/') &&
+ (*tmp != '$') &&
+ (counter < 128))
+ {
+ short_buffer[counter] = *tmp;
+ counter++;
+ tmp++;
+ }
+ }
+ short_buffer[counter] = '\0';
+ if ((slash = getenv(short_buffer)) != NULL)
+ {
+ offset = strlen(slash);
+ if ((offset + index) < 1024)
+ strcpy(&long_buffer[index], slash);
+ index += offset;
+ }
+ else
+ {
+ while ((start_of_var != tmp) && (index < 1024))
+ {
+ long_buffer[index] = *start_of_var;
+ start_of_var++;
+ index++;
+ }
+ }
+ }
+ }
+
+ if (index == 1024)
+ return(buffer);
+ else
+ long_buffer[index] = '\0';
+
+ if (name != buffer)
+ free(buffer);
+ buffer = malloc(index + 1);
+ strcpy(buffer, long_buffer);
+ }
+
+ return(buffer);
+}
+
+int
+restrict_mode()
+{
+ if (!restricted)
+ return(FALSE);
+
+ wmove(com_win, 0, 0);
+ wprintw(com_win, restricted_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(TRUE);
+}
+
+/*
+ | The following routine tests the input string against the list of
+ | strings, to determine if the string is a unique match with one of the
+ | valid values.
+ */
+
+int
+unique_test(string, list)
+char *string;
+char *list[];
+{
+ int counter;
+ int num_match;
+ int result;
+
+ num_match = 0;
+ counter = 0;
+ while (list[counter] != NULL)
+ {
+ result = compare(string, list[counter], FALSE);
+ if (result)
+ num_match++;
+ counter++;
+ }
+ return(num_match);
+}
+
+#ifndef NO_CATGETS
+/*
+ | Get the catalog entry, and if it got it from the catalog,
+ | make a copy, since the buffer will be overwritten by the
+ | next call to catgets().
+ */
+
+char *
+catgetlocal(number, string)
+int number;
+char *string;
+{
+ char *temp1;
+ char *temp2;
+
+ temp1 = catgets(catalog, 1, number, string);
+ if (temp1 != string)
+ {
+ temp2 = malloc(strlen(temp1) + 1);
+ strcpy(temp2, temp1);
+ temp1 = temp2;
+ }
+ return(temp1);
+}
+#endif /* NO_CATGETS */
+
+/*
+ | The following is to allow for using message catalogs which allow
+ | the software to be 'localized', that is, to use different languages
+ | all with the same binary. For more information, see your system
+ | documentation, or the X/Open Internationalization Guide.
+ */
+
+void
+strings_init()
+{
+ int counter;
+
+ setlocale(LC_ALL, "");
+#ifndef NO_CATGETS
+ catalog = catopen("ee", NL_CAT_LOCALE);
+#endif /* NO_CATGETS */
+
+ modes_menu[0].item_string = catgetlocal( 1, "modes menu");
+ mode_strings[1] = catgetlocal( 2, "tabs to spaces ");
+ mode_strings[2] = catgetlocal( 3, "case sensitive search");
+ mode_strings[3] = catgetlocal( 4, "margins observed ");
+ mode_strings[4] = catgetlocal( 5, "auto-paragraph format");
+ mode_strings[5] = catgetlocal( 6, "eightbit characters ");
+ mode_strings[6] = catgetlocal( 7, "info window ");
+ mode_strings[8] = catgetlocal( 8, "right margin ");
+ leave_menu[0].item_string = catgetlocal( 9, "leave menu");
+ leave_menu[1].item_string = catgetlocal( 10, "save changes");
+ leave_menu[2].item_string = catgetlocal( 11, "no save");
+ file_menu[0].item_string = catgetlocal( 12, "file menu");
+ file_menu[1].item_string = catgetlocal( 13, "read a file");
+ file_menu[2].item_string = catgetlocal( 14, "write a file");
+ file_menu[3].item_string = catgetlocal( 15, "save file");
+ file_menu[4].item_string = catgetlocal( 16, "print editor contents");
+ search_menu[0].item_string = catgetlocal( 17, "search menu");
+ search_menu[1].item_string = catgetlocal( 18, "search for ...");
+ search_menu[2].item_string = catgetlocal( 19, "search");
+ spell_menu[0].item_string = catgetlocal( 20, "spell menu");
+ spell_menu[1].item_string = catgetlocal( 21, "use 'spell'");
+ spell_menu[2].item_string = catgetlocal( 22, "use 'ispell'");
+ misc_menu[0].item_string = catgetlocal( 23, "miscellaneous menu");
+ misc_menu[1].item_string = catgetlocal( 24, "format paragraph");
+ misc_menu[2].item_string = catgetlocal( 25, "shell command");
+ misc_menu[3].item_string = catgetlocal( 26, "check spelling");
+ main_menu[0].item_string = catgetlocal( 27, "main menu");
+ main_menu[1].item_string = catgetlocal( 28, "leave editor");
+ main_menu[2].item_string = catgetlocal( 29, "help");
+ main_menu[3].item_string = catgetlocal( 30, "file operations");
+ main_menu[4].item_string = catgetlocal( 31, "redraw screen");
+ main_menu[5].item_string = catgetlocal( 32, "settings");
+ main_menu[6].item_string = catgetlocal( 33, "search");
+ main_menu[7].item_string = catgetlocal( 34, "miscellaneous");
+ help_text[0] = catgetlocal( 35, "Control keys: ");
+ help_text[1] = catgetlocal( 36, "^a ascii code ^i tab ^r right ");
+ help_text[2] = catgetlocal( 37, "^b bottom of text ^j newline ^t top of text ");
+ help_text[3] = catgetlocal( 38, "^c command ^k delete char ^u up ");
+ help_text[4] = catgetlocal( 39, "^d down ^l left ^v undelete word ");
+ help_text[5] = catgetlocal( 40, "^e search prompt ^m newline ^w delete word ");
+ help_text[6] = catgetlocal( 41, "^f undelete char ^n next page ^x search ");
+ help_text[7] = catgetlocal( 42, "^g begin of line ^o end of line ^y delete line ");
+ help_text[8] = catgetlocal( 43, "^h backspace ^p prev page ^z undelete line ");
+ help_text[9] = catgetlocal( 44, "^[ (escape) menu ESC-Enter: exit ee ");
+ help_text[10] = catgetlocal( 45, " ");
+ help_text[11] = catgetlocal( 46, "Commands: ");
+ help_text[12] = catgetlocal( 47, "help : get this info file : print file name ");
+ help_text[13] = catgetlocal( 48, "read : read a file char : ascii code of char ");
+ help_text[14] = catgetlocal( 49, "write : write a file case : case sensitive search ");
+ help_text[15] = catgetlocal( 50, "exit : leave and save nocase : case insensitive search ");
+ help_text[16] = catgetlocal( 51, "quit : leave, no save !cmd : execute \"cmd\" in shell ");
+ help_text[17] = catgetlocal( 52, "line : display line # 0-9 : go to line \"#\" ");
+ help_text[18] = catgetlocal( 53, "expand : expand tabs noexpand: do not expand tabs ");
+ help_text[19] = catgetlocal( 54, " ");
+ help_text[20] = catgetlocal( 55, " ee [+#] [-i] [-e] [-h] [file(s)] ");
+ help_text[21] = catgetlocal( 56, "+# :go to line # -i :no info window -e : don't expand tabs -h :no highlight");
+ control_keys[0] = catgetlocal( 57, "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page ");
+ control_keys[1] = catgetlocal( 58, "^a ascii code ^x search ^z undelete line ^d down ^n next page ");
+ control_keys[2] = catgetlocal( 59, "^b bottom of text ^g begin of line ^w delete word ^l left ");
+ control_keys[3] = catgetlocal( 60, "^t top of text ^o end of line ^v undelete word ^r right ");
+ control_keys[4] = catgetlocal( 61, "^c command ^k delete char ^f undelete char ESC-Enter: exit ee ");
+ command_strings[0] = catgetlocal( 62, "help : get help info |file : print file name |line : print line # ");
+ command_strings[1] = catgetlocal( 63, "read : read a file |char : ascii code of char |0-9 : go to line \"#\"");
+ command_strings[2] = catgetlocal( 64, "write: write a file |case : case sensitive search |exit : leave and save ");
+ command_strings[3] = catgetlocal( 65, "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save");
+ command_strings[4] = catgetlocal( 66, "expand: expand tabs |noexpand: do not expand tabs ");
+ com_win_message = catgetlocal( 67, " press Escape (^[) for menu");
+ no_file_string = catgetlocal( 68, "no file");
+ ascii_code_str = catgetlocal( 69, "ascii code: ");
+ printer_msg_str = catgetlocal( 70, "sending contents of buffer to \"%s\" ");
+ command_str = catgetlocal( 71, "command: ");
+ file_write_prompt_str = catgetlocal( 72, "name of file to write: ");
+ file_read_prompt_str = catgetlocal( 73, "name of file to read: ");
+ char_str = catgetlocal( 74, "character = %d");
+ unkn_cmd_str = catgetlocal( 75, "unknown command \"%s\"");
+ non_unique_cmd_msg = catgetlocal( 76, "entered command is not unique");
+ line_num_str = catgetlocal( 77, "line %d ");
+ line_len_str = catgetlocal( 78, "length = %d");
+ current_file_str = catgetlocal( 79, "current file is \"%s\" ");
+ usage0 = catgetlocal( 80, "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n");
+ usage1 = catgetlocal( 81, " -i turn off info window\n");
+ usage2 = catgetlocal( 82, " -e do not convert tabs to spaces\n");
+ usage3 = catgetlocal( 83, " -h do not use highlighting\n");
+ file_is_dir_msg = catgetlocal( 84, "file \"%s\" is a directory");
+ new_file_msg = catgetlocal( 85, "new file \"%s\"");
+ cant_open_msg = catgetlocal( 86, "can't open \"%s\"");
+ open_file_msg = catgetlocal( 87, "file \"%s\", %d lines");
+ file_read_fin_msg = catgetlocal( 88, "finished reading file \"%s\"");
+ reading_file_msg = catgetlocal( 89, "reading file \"%s\"");
+ read_only_msg = catgetlocal( 90, ", read only");
+ file_read_lines_msg = catgetlocal( 91, "file \"%s\", %d lines");
+ save_file_name_prompt = catgetlocal( 92, "enter name of file: ");
+ file_not_saved_msg = catgetlocal( 93, "no filename entered: file not saved");
+ changes_made_prompt = catgetlocal( 94, "changes have been made, are you sure? (y/n [n]) ");
+ yes_char = catgetlocal( 95, "y");
+ file_exists_prompt = catgetlocal( 96, "file already exists, overwrite? (y/n) [n] ");
+ create_file_fail_msg = catgetlocal( 97, "unable to create file \"%s\"");
+ writing_file_msg = catgetlocal( 98, "writing file \"%s\"");
+ file_written_msg = catgetlocal( 99, "\"%s\" %d lines, %d characters");
+ searching_msg = catgetlocal( 100, " ...searching");
+ str_not_found_msg = catgetlocal( 101, "string \"%s\" not found");
+ search_prompt_str = catgetlocal( 102, "search for: ");
+ exec_err_msg = catgetlocal( 103, "could not exec %s\n");
+ continue_msg = catgetlocal( 104, "press return to continue ");
+ menu_cancel_msg = catgetlocal( 105, "press Esc to cancel");
+ menu_size_err_msg = catgetlocal( 106, "menu too large for window");
+ press_any_key_msg = catgetlocal( 107, "press any key to continue ");
+ shell_prompt = catgetlocal( 108, "shell command: ");
+ formatting_msg = catgetlocal( 109, "...formatting paragraph...");
+ shell_echo_msg = catgetlocal( 110, "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-");
+ spell_in_prog_msg = catgetlocal( 111, "sending contents of edit buffer to 'spell'");
+ margin_prompt = catgetlocal( 112, "right margin is: ");
+ restricted_msg = catgetlocal( 113, "restricted mode: unable to perform requested operation");
+ ON = catgetlocal( 114, "ON");
+ OFF = catgetlocal( 115, "OFF");
+ HELP = catgetlocal( 116, "HELP");
+ WRITE = catgetlocal( 117, "WRITE");
+ READ = catgetlocal( 118, "READ");
+ LINE = catgetlocal( 119, "LINE");
+ FILE_str = catgetlocal( 120, "FILE");
+ CHARACTER = catgetlocal( 121, "CHARACTER");
+ REDRAW = catgetlocal( 122, "REDRAW");
+ RESEQUENCE = catgetlocal( 123, "RESEQUENCE");
+ AUTHOR = catgetlocal( 124, "AUTHOR");
+ VERSION = catgetlocal( 125, "VERSION");
+ CASE = catgetlocal( 126, "CASE");
+ NOCASE = catgetlocal( 127, "NOCASE");
+ EXPAND = catgetlocal( 128, "EXPAND");
+ NOEXPAND = catgetlocal( 129, "NOEXPAND");
+ Exit_string = catgetlocal( 130, "EXIT");
+ QUIT_string = catgetlocal( 131, "QUIT");
+ INFO = catgetlocal( 132, "INFO");
+ NOINFO = catgetlocal( 133, "NOINFO");
+ MARGINS = catgetlocal( 134, "MARGINS");
+ NOMARGINS = catgetlocal( 135, "NOMARGINS");
+ AUTOFORMAT = catgetlocal( 136, "AUTOFORMAT");
+ NOAUTOFORMAT = catgetlocal( 137, "NOAUTOFORMAT");
+ Echo = catgetlocal( 138, "ECHO");
+ PRINTCOMMAND = catgetlocal( 139, "PRINTCOMMAND");
+ RIGHTMARGIN = catgetlocal( 140, "RIGHTMARGIN");
+ HIGHLIGHT = catgetlocal( 141, "HIGHLIGHT");
+ NOHIGHLIGHT = catgetlocal( 142, "NOHIGHLIGHT");
+ EIGHTBIT = catgetlocal( 143, "EIGHTBIT");
+ NOEIGHTBIT = catgetlocal( 144, "NOEIGHTBIT");
+ /*
+ | additions
+ */
+ mode_strings[7] = catgetlocal( 145, "emacs key bindings ");
+ emacs_help_text[0] = help_text[0];
+ emacs_help_text[1] = catgetlocal( 146, "^a beginning of line ^i tab ^r restore word ");
+ emacs_help_text[2] = catgetlocal( 147, "^b back 1 char ^j undel char ^t top of text ");
+ emacs_help_text[3] = catgetlocal( 148, "^c command ^k delete line ^u bottom of text ");
+ emacs_help_text[4] = catgetlocal( 149, "^d delete char ^l undelete line ^v next page ");
+ emacs_help_text[5] = catgetlocal( 150, "^e end of line ^m newline ^w delete word ");
+ emacs_help_text[6] = catgetlocal( 151, "^f forward 1 char ^n next line ^x search ");
+ emacs_help_text[7] = catgetlocal( 152, "^g go back 1 page ^o ascii char insert ^y search prompt ");
+ emacs_help_text[8] = catgetlocal( 153, "^h backspace ^p prev line ^z next word ");
+ emacs_help_text[9] = help_text[9];
+ emacs_help_text[10] = help_text[10];
+ emacs_help_text[11] = help_text[11];
+ emacs_help_text[12] = help_text[12];
+ emacs_help_text[13] = help_text[13];
+ emacs_help_text[14] = help_text[14];
+ emacs_help_text[15] = help_text[15];
+ emacs_help_text[16] = help_text[16];
+ emacs_help_text[17] = help_text[17];
+ emacs_help_text[18] = help_text[18];
+ emacs_help_text[19] = help_text[19];
+ emacs_help_text[20] = help_text[20];
+ emacs_help_text[21] = help_text[21];
+ emacs_control_keys[0] = catgetlocal( 154, "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page");
+ emacs_control_keys[1] = catgetlocal( 155, "^o ascii code ^x search ^l undelete line ^n next li ^v next page");
+ emacs_control_keys[2] = catgetlocal( 156, "^u end of file ^a begin of line ^w delete word ^b back 1 char ^z next word");
+ emacs_control_keys[3] = catgetlocal( 157, "^t top of text ^e end of line ^r restore word ^f forward char ");
+ emacs_control_keys[4] = catgetlocal( 158, "^c command ^d delete char ^j undelete char ESC-Enter: exit");
+ EMACS_string = catgetlocal( 159, "EMACS");
+ NOEMACS_string = catgetlocal( 160, "NOEMACS");
+ usage4 = catgetlocal( 161, " +# put cursor at line #\n");
+ conf_dump_err_msg = catgetlocal( 162, "unable to open .init.ee for writing, no configuration saved!");
+ conf_dump_success_msg = catgetlocal( 163, "ee configuration saved in file %s");
+ modes_menu[10].item_string = catgetlocal( 164, "save editor configuration");
+ config_dump_menu[0].item_string = catgetlocal( 165, "save ee configuration");
+ config_dump_menu[1].item_string = catgetlocal( 166, "save in current directory");
+ config_dump_menu[2].item_string = catgetlocal( 167, "save in home directory");
+ conf_not_saved_msg = catgetlocal( 168, "ee configuration not saved");
+ ree_no_file_msg = catgetlocal( 169, "must specify a file when invoking ree");
+ menu_too_lrg_msg = catgetlocal( 180, "menu too large for window");
+ more_above_str = catgetlocal( 181, "^^more^^");
+ more_below_str = catgetlocal( 182, "VVmoreVV");
+ mode_strings[9] = catgetlocal( 183, "16 bit characters ");
+ chinese_cmd = catgetlocal( 184, "16BIT");
+ nochinese_cmd = catgetlocal( 185, "NO16BIT");
+
+ commands[0] = HELP;
+ commands[1] = WRITE;
+ commands[2] = READ;
+ commands[3] = LINE;
+ commands[4] = FILE_str;
+ commands[5] = REDRAW;
+ commands[6] = RESEQUENCE;
+ commands[7] = AUTHOR;
+ commands[8] = VERSION;
+ commands[9] = CASE;
+ commands[10] = NOCASE;
+ commands[11] = EXPAND;
+ commands[12] = NOEXPAND;
+ commands[13] = Exit_string;
+ commands[14] = QUIT_string;
+ commands[15] = "<";
+ commands[16] = ">";
+ commands[17] = "!";
+ commands[18] = "0";
+ commands[19] = "1";
+ commands[20] = "2";
+ commands[21] = "3";
+ commands[22] = "4";
+ commands[23] = "5";
+ commands[24] = "6";
+ commands[25] = "7";
+ commands[26] = "8";
+ commands[27] = "9";
+ commands[28] = CHARACTER;
+ commands[29] = chinese_cmd;
+ commands[30] = nochinese_cmd;
+ commands[31] = NULL;
+ init_strings[0] = CASE;
+ init_strings[1] = NOCASE;
+ init_strings[2] = EXPAND;
+ init_strings[3] = NOEXPAND;
+ init_strings[4] = INFO;
+ init_strings[5] = NOINFO;
+ init_strings[6] = MARGINS;
+ init_strings[7] = NOMARGINS;
+ init_strings[8] = AUTOFORMAT;
+ init_strings[9] = NOAUTOFORMAT;
+ init_strings[10] = Echo;
+ init_strings[11] = PRINTCOMMAND;
+ init_strings[12] = RIGHTMARGIN;
+ init_strings[13] = HIGHLIGHT;
+ init_strings[14] = NOHIGHLIGHT;
+ init_strings[15] = EIGHTBIT;
+ init_strings[16] = NOEIGHTBIT;
+ init_strings[17] = EMACS_string;
+ init_strings[18] = NOEMACS_string;
+ init_strings[19] = chinese_cmd;
+ init_strings[20] = nochinese_cmd;
+ init_strings[21] = NULL;
+
+ /*
+ | allocate space for strings here for settings menu
+ */
+
+ for (counter = 1; counter < NUM_MODES_ITEMS; counter++)
+ {
+ modes_menu[counter].item_string = malloc(80);
+ }
+
+#ifndef NO_CATGETS
+ catclose(catalog);
+#endif /* NO_CATGETS */
+}
+
diff --git a/text_cmds/ee/ee.i18n.guide b/text_cmds/ee/ee.i18n.guide
new file mode 100644
index 0000000..eef836a
--- /dev/null
+++ b/text_cmds/ee/ee.i18n.guide
@@ -0,0 +1,158 @@
+Easy Editor ("ee") provides the ability to translate the messages
+displayed to the user and the commands entered. This is done via message
+catalogs, following X/Open standards. ee supports eight bit characters,
+as well as 16-bit characters. The Chinese Big 5 code set is the 16-bit
+code set that ee was modified to handle, as it is relatively easy to
+support since two byte characters also take up two columns on the screen,
+thereby simplifying the screen position calculations. Other multibyte
+code sets may function, but have not been tested.
+
+(The name ee.i18n.guide is for "ee internationalization guide". The i18n
+abbreviation is used because there are 18 characters between the first
+letter ("i") and last ("n") of "internationalization".)
+
+All of the messages, warnings, information, and commands, are contained
+in the message catalog. Each numbered entry represents an individual
+string used by ee. Some strings contain formatting information for
+formatted print statements, which are of the form "%s", or "%d", these
+must be preserved in the translation, or the correct information will not
+be displayed. For those strings containing multiple formatting codes,
+the order of each item must be preserved as well.
+
+Message content
+1 title for modes, or settings menu
+2 - 8 entries for modes menu, each line should be the same length
+ (padded with spaces)
+9 - 34 other menu titles and entries
+35 - 56 help screen
+57 - 61 actions assigned to control keys
+62 - 66 commands information
+67 message displayed when info window turned off
+68 indication that no file name was entered when invoking ee
+69 prompt for decimal value of character to be entered
+70 message displaying the print command being invoked
+71 prompt for command
+72 prompt for name of file to be written
+73 prompt for name of file to be read
+74 string used to display the decimal value of the character
+ the cursor is on
+75 string displaying an unrecognized command
+76 string indicating that the command entered is not a unique
+ substring of a valid command
+77 string indicating the current line number
+78 string for displaying the length of the line
+79 string for displaying the name of the file
+80 - 83 strings showing how to invoke ee, and its options
+84 message indicating that the file entered is a directory, not a
+ text file
+85 message informing that the entered file does not yet exist
+86 message informing that the file can't be opened (because of
+ permission problems)
+87 message after file has been read with the file name and number
+ of lines read
+88 message indicating that the file has been read
+89 message indicating that the file is being read
+90 message indicating that permissions only allow the file to be
+ read, not written
+91 message after file has been read with the file name and number
+ of lines read
+92 prompt for name of file to be saved (used when no name was
+ entered for a file to edit)
+93 message indicating that the file was not written, since no
+ name was entered at the prompt
+94 prompt asking user if changes should not be saved ("yes_char"
+ will be expected for affirmative response)
+95 "yes" character, single character expected to confirm action
+ (can be upper or lower case, will be converted to upper-case
+ during test)
+96 prompt
+97 error message
+98 message indicating that the named file is being written
+99 message indicating the name of the file written, the number of
+ lines, and the number of characters (order of items must be
+ maintained)
+100 search in progress message
+101 message that the string was not found
+102 prompt for search
+103 message that string could not be executed
+104 self-explanatory
+105 message for menus, indicating that the Escape character will
+ allow the user to exit the menu
+106 error message indicating the menu won't fit on the screen
+107 self-explanatory
+108 prompt for shell command
+109 message displayed while formatting a paragraph
+110 string which places message for spell checking at top of
+ buffer (the portions 'list of unrecognized words' and
+ '-=-=-=-=-=-' may be replaced, but the rest must remain the
+ same)
+111 message informing that spell checking is in progress
+112 prompt for right margin
+113 error informing user that operation is not permitted in ree
+114 string indicating mode is turned 'on' in modes menu
+115 string indicating mode is turned 'off' in modes menu
+116 - 131 strings used for commands (some also used for initialization)
+132 - 144 strings used for initialization
+145 entry for settings menu for emacs key bindings settings
+146 - 153 help screen entries for emacs key bindings info
+154 - 158 info window entries for emacs key bindings info
+159 string for turning on emacs key bindings in the init file
+160 string for turning off emacs key bindings in the init file
+161 fifth line of usage statement
+162 error message when unable to save configuration file
+163 positive feedback about saving the configuration file
+164 - 167 menu items for saving editor configuration
+168 error message when unable to save configuration file
+169 error message for ree when not specifying the file
+180 self-explanatory
+181 - 182 indicators of more information in menu (for when scrolling
+ menus because menu contents won't fit vertically on screen)
+183 menu entry for modes menu for 16 bit characters
+184 - 185 strings for initialization to turn on or off 16 bit
+ character handling
+
+Care should be taken when translating commands and initialization keywords
+because the algorithm used for detecting uniqueness of entered commands
+will not be able to distinguish words that are not unique before the end
+of the shorter word, for example, it would not be able to distinguish the
+command 'abcd' from 'abcde'.
+
+After translating the messages, use the 'gencat' command to create the compiled
+catalog used when running the software. The standard syntax would be:
+
+ gencat ee.cat ee.msg
+
+Where ee.msg is the file containing the translations, and ee.cat is the
+compiled catalog. If the file ee.cat does not exist, it will be created.
+Check the documentation for your system for proper syntax.
+
+Message catalog placement varies from system to system. A common location
+for message catalogs is in /usr/lib/nls. In this directory are
+directories with the names of other languages. The default language is
+'C'. There is also an environment variable, named NLSPATH used to
+determine where message catalogs can be found. This variable is similar
+to the PATH variable used for commands, but with some differences. The
+NLSPATH variable must have the ability to handle different names for
+languages and the catalog files, so it has field descriptors for these. A
+typical setting for NLSPATH could be:
+
+ NLSPATH=/usr/lib/nls/%L/%N.cat:/usr/local/lib/nls/%L/%N.cat
+
+Where "%L" is the field descriptor for the language (obtained from the
+LANG environment variable) and "%N" is the name of the file (with the
+".cat" appended by the path variable, it is not passed from the requesting
+program). The colon (:) is used to separate paths, so in the above
+example there are two paths possible for message catalogs. You may wish
+to maintain catalogs for applications that are not supported by your
+system vendor in a location unique for you, and this is facilitated by the
+NLSPATH variable. Remember to set and export both the LANG and NLSPATH
+variables for each user that expects to use localization either in a
+system-wide profile or in each user's profile. See your system
+documentation for more information.
+
+The message catalog supplied with ee also uses the '$quote' directive to
+specify a quote around strings to ensure proper padding. This directive
+may not be supported on all systems, and lead to quotes being included in
+the string used in ee, which will cause incorrect behavior. If the
+'$quote' directive is not supported by your system's gencat command, edit
+the msg file to remove the leading and trailing quotation marks.
diff --git a/text_cmds/ee/ee.msg b/text_cmds/ee/ee.msg
new file mode 100644
index 0000000..28fa542
--- /dev/null
+++ b/text_cmds/ee/ee.msg
@@ -0,0 +1,186 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header: /home/hugh/sources/old_ae/RCS/ee.msg,v 1.8 1996/11/30 03:23:40 hugh Exp $
+$ $FreeBSD$
+$
+$
+$set 1
+$quote "
+1 "modes menu"
+2 "tabs to spaces "
+3 "case sensitive search"
+4 "margins observed "
+5 "auto-paragraph format"
+6 "eightbit characters "
+7 "info window "
+8 "right margin "
+9 "leave menu"
+10 "save changes"
+11 "no save"
+12 "file menu"
+13 "read a file"
+14 "write a file"
+15 "save file"
+16 "print editor contents"
+17 "search menu"
+18 "search for ..."
+19 "search"
+20 "spell menu"
+21 "use 'spell'"
+22 "use 'ispell'"
+23 "miscellaneous menu"
+24 "format paragraph"
+25 "shell command"
+26 "check spelling"
+27 "main menu"
+28 "leave editor"
+29 "help"
+30 "file operations"
+31 "redraw screen"
+32 "settings"
+33 "search"
+34 "miscellaneous"
+35 "Control keys: "
+36 "^a ascii code ^i tab ^r right "
+37 "^b bottom of text ^j newline ^t top of text "
+38 "^c command ^k delete char ^u up "
+39 "^d down ^l left ^v undelete word "
+40 "^e search prompt ^m newline ^w delete word "
+41 "^f undelete char ^n next page ^x search "
+42 "^g begin of line ^o end of line ^y delete line "
+43 "^h backspace ^p prev page ^z undelete line "
+44 "^[ (escape) menu "
+45 " "
+46 "Commands: "
+47 "help : get this info file : print file name "
+48 "read : read a file char : ascii code of char "
+49 "write : write a file case : case sensitive search "
+50 "exit : leave and save nocase : case insensitive search "
+51 "quit : leave, no save !cmd : execute \"cmd\" in shell "
+52 "line : display line # 0-9 : go to line \"#\" "
+53 "expand : expand tabs noexpand: do not expand tabs "
+54 " "
+55 " ee [+#] [-i] [-e] [-h] [file(s)] "
+56 "+# :go to line # -i :no info window -e : don't expand tabs -h :no highlight"
+57 "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page "
+58 "^a ascii code ^x search ^z undelete line ^d down ^n next page "
+59 "^b bottom of text ^g begin of line ^w delete word ^l left "
+60 "^t top of text ^o end of line ^v undelete word ^r right "
+61 "^c command ^k delete char ^f undelete char "
+62 "help : get help info |file : print file name |line : print line # "
+63 "read : read a file |char : ascii code of char |0-9 : go to line \"#\""
+64 "write: write a file |case : case sensitive search |exit : leave and save "
+65 "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save"
+66 "expand: expand tabs |noexpand: do not expand tabs "
+67 " press Escape (^[) for menu"
+68 "no file"
+69 "ascii code: "
+70 "sending contents of buffer to \"%s\" "
+71 "command: "
+72 "name of file to write: "
+73 "name of file to read: "
+74 "character = %d"
+75 "unknown command \"%s\""
+76 "entered command is not unique"
+77 "line %d "
+78 "length = %d"
+79 "current file is \"%s\" "
+80 "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n"
+81 " -i turn off info window\n"
+82 " -e do not convert tabs to spaces\n"
+83 " -h do not use highlighting\n"
+84 "file \"%s\" is a directory"
+85 "new file \"%s\""
+86 "can't open \"%s\""
+87 "file \"%s\", %d lines"
+88 "finished reading file \"%s\""
+89 "reading file \"%s\""
+90 ", read only"
+91 "file \"%s\", %d lines"
+92 "enter name of file: "
+93 "no filename entered: file not saved"
+94 "changes have been made, are you sure? (y/n [n]) "
+95 "y"
+96 "file already exists, overwrite? (y/n) [n] "
+97 "unable to create file \"%s\""
+98 "writing file \"%s\""
+99 "\"%s\" %d lines, %d characters"
+100 " ...searching"
+101 "string \"%s\" not found"
+102 "search for: "
+103 "could not exec %s\n"
+104 "press return to continue "
+105 "press Esc to cancel"
+106 "menu too large for window"
+107 "press any key to continue "
+108 "shell command: "
+109 "...formatting paragraph..."
+110 "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-"
+111 "sending contents of edit buffer to 'spell'"
+112 "right margin is: "
+113 "restricted mode: unable to perform requested operation"
+114 "ON"
+115 "OFF"
+116 "HELP"
+117 "WRITE"
+118 "READ"
+119 "LINE"
+120 "FILE"
+121 "CHARACTER"
+122 "REDRAW"
+123 "RESEQUENCE"
+124 "AUTHOR"
+125 "VERSION"
+126 "CASE"
+127 "NOCASE"
+128 "EXPAND"
+129 "NOEXPAND"
+130 "EXIT"
+131 "QUIT"
+132 "INFO"
+133 "NOINFO"
+134 "MARGINS"
+135 "NOMARGINS"
+136 "AUTOFORMAT"
+137 "NOAUTOFORMAT"
+138 "ECHO"
+139 "PRINTCOMMAND"
+140 "RIGHTMARGIN"
+141 "HIGHLIGHT"
+142 "NOHIGHLIGHT"
+143 "EIGHTBIT"
+144 "NOEIGHTBIT"
+145 "emacs key bindings "
+146 "^a beginning of line ^i tab ^r restore word "
+147 "^b back 1 char ^j undel char ^t top of text "
+148 "^c command ^k delete line ^u bottom of text "
+149 "^d delete char ^l undelete line ^v next page "
+150 "^e end of line ^m newline ^w delete word "
+151 "^f forward 1 char ^n next line ^x search "
+152 "^g go back 1 page ^o ascii char insert ^y search prompt "
+153 "^h backspace ^p prev line ^z next word "
+154 "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page"
+155 "^o ascii code ^x search ^l undelete line ^n next li ^v next page"
+156 "^u end of file ^a begin of line ^w delete word ^b back 1 char "
+157 "^t top of text ^e end of line ^r restore word ^f forward 1 char "
+158 "^c command ^d delete char ^j undelete char ^z next word "
+159 "EMACS"
+160 "NOEMACS"
+161 " +# put cursor at line #\n"
+162 "unable to open .init.ee for writing, no configuration saved!"
+163 "ee configuration saved in file %s"
+164 "save editor configuration"
+165 "save ee configuration"
+166 "save in current directory"
+167 "save in home directory"
+168 "ee configuration not saved"
+169 "must specify a file when invoking ree"
+180 "menu too large for window"
+181 "^^more^^"
+182 "VVmoreVV"
+183 "16 bit characters "
+184 "16BIT"
+185 "NO16BIT"
diff --git a/text_cmds/ee/ee_version.h b/text_cmds/ee/ee_version.h
new file mode 100644
index 0000000..c2db839
--- /dev/null
+++ b/text_cmds/ee/ee_version.h
@@ -0,0 +1,6 @@
+/*
+ | provide a version number for ee
+ */
+
+#define EE_VERSION "1.5.2"
+#define DATE_STRING "$Date: 2010/06/04 02:35:35 $"
diff --git a/text_cmds/ee/genstr b/text_cmds/ee/genstr
new file mode 100755
index 0000000..429f960
--- /dev/null
+++ b/text_cmds/ee/genstr
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -x
+
+if [ $# -lt 2 ]
+then
+ echo usage $0 source_file dest_file
+ exit 1
+fi
+
+trap 'rm -f /tmp/$$.out; exit 0' 0 # set up traps to clean up
+trap 'rm -f /tmp/$$.out; exit 1' 1 2 3 15 # on errors AND normal exit
+
+if [ -f $2 ]
+then
+ rm $2
+fi
+
+cat $1 | grep 'catgetlocal.*\"*\"' |
+ sed -e 's/^.*catgetlocal(//' |
+ sed -e 's/^[ ]*//' |
+ sed -e 's/, \"/ \"/' |
+ sed -e 's/);//' > /tmp/$$.out
+
+cat > $2 <<EOF
+\$
+\$
+\$set 1
+\$quote "
+EOF
+
+sort -n < /tmp/$$.out >> $2
diff --git a/text_cmds/ee/make.default b/text_cmds/ee/make.default
new file mode 100644
index 0000000..32ff05d
--- /dev/null
+++ b/text_cmds/ee/make.default
@@ -0,0 +1,57 @@
+# This is the make file for ee, the "easy editor".
+#
+# If building ee using curses, type "make curses", otherwise new_curse (a
+# subset of curses that supports ee) will be built and ee will use new_curse
+# instead of curses.
+#
+# The "install" target ("make install") will copy the ee binary to
+# the /usr/local/bin directory on the local system. The man page (ee.1)
+# will be copied into the /usr/local/man/man1 directory.
+#
+# The "clean" target ("make clean") will remove the ee and new_curse.o
+# object files, and the ee binary.
+#
+# If the system does not have localization routines, use the -DNO_CATGETS
+# define. If the system supports setlocale(), catopen(), and catgets() and
+# localization is desired, do not use -DNO_CATGETS.
+#
+# DEFINES is used for new_curse.c, and CFLAGS is used for ee.c.
+#
+
+# for System V, using new_curse with terminfo
+DEFINES = -DSYS5 -DNCURSE
+
+# for BSD, using new_curse with termcap
+#DEFINES = -DCAP -DNCURSE
+
+# for BSD systems with select(), using new_curse with termcap, use:
+#DEFINES = -DCAP -DNCURSE -DBSD_SELECT
+
+# flags for compilation
+CFLAGS = -s -DNO_CATGETS
+
+# For Sun systems, remove the '#' from the front of the next two lines:
+#DEFINES = -DSYS5 -DNCURSE
+#CFLAGS = -I/usr/5include -L/usr/5lib -DNO_CATGETS -s
+
+all : ee
+
+curses : ee.c
+ cc ee.c -o ee $(CFLAGS) -lcurses
+
+ee : ee.o new_curse.o
+ cc -o ee ee.o new_curse.o $(CFLAGS)
+
+ee.o : ee.c new_curse.h
+ cc -c ee.c $(DEFINES) $(CFLAGS)
+
+new_curse.o : new_curse.c new_curse.h
+ cc new_curse.c -c $(DEFINES) $(CFLAGS)
+
+install :
+ cp ee /usr/local/bin/ee
+ cp ee.1 /usr/local/man/man1/ee.1
+
+clean :
+ rm -f ee.o new_curse.o ee
+
diff --git a/text_cmds/ee/new_curse.c b/text_cmds/ee/new_curse.c
new file mode 100644
index 0000000..5ceec24
--- /dev/null
+++ b/text_cmds/ee/new_curse.c
@@ -0,0 +1,3819 @@
+/*
+ | new_curse.c
+ |
+ | A subset of curses developed for use with ae.
+ |
+ | written by Hugh Mahon
+ |
+ | Copyright (c) 1986, 1987, 1988, 1991, 1992, 1993, 1994, 1995, 2009 Hugh Mahon
+ | All rights reserved.
+ |
+ | Redistribution and use in source and binary forms, with or without
+ | modification, are permitted provided that the following conditions
+ | are met:
+ |
+ | * Redistributions of source code must retain the above copyright
+ | notice, this list of conditions and the following disclaimer.
+ | * 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 COPYRIGHT HOLDERS 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
+ | COPYRIGHT OWNER 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.
+ |
+ |
+ | All are rights reserved.
+ |
+ | $Header: /home/hugh/sources/old_ae/RCS/new_curse.c,v 1.54 2002/09/21 00:47:14 hugh Exp $
+ |
+ */
+
+char *copyright_message[] = { "Copyright (c) 1986, 1987, 1988, 1991, 1992, 1993, 1994, 1995, 2009 Hugh Mahon",
+ "All rights are reserved."};
+
+char * new_curse_name= "@(#) new_curse.c $Revision: 1.54 $";
+
+#include "new_curse.h"
+#include <signal.h>
+#include <fcntl.h>
+
+#ifdef SYS5
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#ifdef BSD_SELECT
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef SLCT_HDR
+#include <sys/select.h> /* on AIX */
+#endif /* SLCT_HDR */
+
+#endif /* BSD_SELECT */
+
+#ifdef HAS_STDLIB
+#include <stdlib.h>
+#endif
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAS_UNISTD
+#include <unistd.h>
+#endif
+
+#ifdef HAS_SYS_IOCTL
+#include <sys/ioctl.h>
+#endif
+
+
+WINDOW *curscr;
+static WINDOW *virtual_scr;
+WINDOW *stdscr;
+WINDOW *last_window_refreshed;
+
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+#endif
+
+#define min(a, b) (a < b ? a : b)
+#define highbitset(a) ((a) & 0x80)
+
+#ifndef CAP
+#define String_Out(table, stack, place) Info_Out(table, stack, place)
+#else
+#define String_Out(table, stack, place) Cap_Out(table, stack, place)
+#endif
+
+#define bw__ 0 /* booleans */
+#define am__ 1
+#define xb__ 2
+#define xs__ 3 /* hp glitch (standout not erased by overwrite) */
+#define xn__ 4
+#define eo__ 5
+#define gn__ 6 /* generic type terminal */
+#define hc__ 7 /* hardcopy terminal */
+#define km__ 8
+#define hs__ 9
+#define in__ 10
+#define da__ 11
+#define db__ 12
+#define mi__ 13 /* safe to move during insert mode */
+#define ms__ 14 /* safe to move during standout mode */
+#define os__ 15
+#define es__ 16
+#define xt__ 17
+#define hz__ 18 /* hazeltine glitch */
+#define ul__ 19
+#define xo__ 20
+#define chts__ 21
+#define nxon__ 22
+#define nrrmc__ 23
+#define npc__ 24
+#define mc5i__ 25
+
+#define co__ 0 /* number of columns */ /* numbers */
+#define it__ 1 /* spaces per tab */
+#define li__ 2 /* number of lines */
+#define lm__ 3
+#define sg__ 4 /* magic cookie glitch */
+#define pb__ 5
+#define vt__ 6
+#define ws__ 7
+
+#define cols__ 0
+#define lines__ 2
+#define xmc__ 4
+#define vt__ 6
+#define wsl__ 7
+#define nlab__ 8
+#define lh__ 9
+#define lw__ 10
+
+#define bt__ 0 /* back tab */ /* strings */
+#define bl__ 1 /* bell */
+#define cr__ 2 /* carriage return */
+#define cs__ 3 /* change scroll region */
+#define ct__ 4 /* clear all tab stops */
+#define cl__ 5 /* clear screen and home cursor */
+#define ce__ 6 /* clear to end of line */
+#define cd__ 7 /* clear to end of display */
+#define ch__ 8 /* set cursor column */
+#define CC__ 9 /* term, settable cmd char in */
+#define cm__ 10 /* screen rel cursor motion, row, column */
+#define do__ 11 /* down one line */
+#define ho__ 12 /* home cursor */
+#define vi__ 13 /* make cursor invisible */
+#define le__ 14 /* move cursor left one space */
+#define CM__ 15 /* memory rel cursor addressing */
+#define ve__ 16 /* make cursor appear normal */
+#define nd__ 17 /* non-destructive space (cursor right) */
+#define ll__ 18 /* last line, first col */
+#define up__ 19 /* cursor up */
+#define vs__ 20
+#define dc__ 21 /* delete character */
+#define dl__ 22 /* delete line */
+#define ds__ 23
+#define hd__ 24
+#define as__ 25
+#define mb__ 26
+#define md__ 27 /* turn on bold */
+#define ti__ 28
+#define dm__ 29 /* turn on delete mode */
+#define mh__ 30 /* half bright mode */
+#define im__ 31 /* insert mode */
+#define mk__ 32
+#define mp__ 33
+#define mr__ 34
+#define so__ 35 /* enter standout mode */
+#define us__ 36
+#define ec__ 37
+#define ae__ 38
+#define me__ 39
+#define te__ 40
+#define ed__ 41
+#define ei__ 42 /* exit insert mode */
+#define se__ 43 /* exit standout mode */
+#define ue__ 44
+#define vb__ 45
+#define ff__ 46
+#define fs__ 47
+#define i1__ 48
+#define i2__ 49
+#define i3__ 50
+#define if__ 51
+#define ic__ 52
+#define al__ 53
+#define ip__ 54
+#define kb__ 55 /* backspace key */
+#define ka__ 56
+#define kC__ 57
+#define kt__ 58
+#define kD__ 59
+#define kL__ 60
+#define kd__ 61
+#define kM__ 62
+#define kE__ 63
+#define kS__ 64
+#define k0__ 65
+#define k1__ 66
+#define kf10__ 67
+#define k2__ 68
+#define k3__ 69
+#define k4__ 70
+#define k5__ 71
+#define k6__ 72
+#define k7__ 73
+#define k8__ 74
+#define k9__ 75
+#define kh__ 76
+#define kI__ 77
+#define kA__ 78
+#define kl__ 79
+#define kH__ 80
+#define kN__ 81
+#define kP__ 82
+#define kr__ 83
+#define kF__ 84
+#define kR__ 85
+#define kT__ 86
+#define ku__ 87 /* key up */
+#define ke__ 88
+#define ks__ 89
+#define l0__ 90
+#define l1__ 91
+#define la__ 92
+#define l2__ 93
+#define l3__ 94
+#define l4__ 95
+#define l5__ 96
+#define l6__ 97
+#define l7__ 98
+#define l8__ 99
+#define l9__ 100
+#define mo__ 101
+#define mm__ 102
+#define nw__ 103
+#define pc__ 104
+#define DC__ 105
+#define DL__ 106
+#define DO__ 107
+#define IC__ 118
+#define SF__ 109
+#define AL__ 110
+#define LE__ 111
+#define RI__ 112
+#define SR__ 113
+#define UP__ 114
+#define pk__ 115
+#define pl__ 116
+#define px__ 117
+#define ps__ 118
+#define pf__ 119
+#define po__ 120
+#define rp__ 121
+#define r1__ 122
+#define r2__ 123
+#define r3__ 124
+#define rf__ 125
+#define rc__ 126
+#define cv__ 127
+#define sc__ 128
+#define sf__ 129
+#define sr__ 130
+#define sa__ 131 /* sgr */
+#define st__ 132
+#define wi__ 133
+#define ta__ 134
+#define ts__ 135
+#define uc__ 136
+#define hu__ 137
+#define iP__ 138
+#define K1__ 139
+#define K2__ 140
+#define K3__ 141
+#define K4__ 142
+#define K5__ 143
+#define pO__ 144
+#define ml__ 145
+#define mu__ 146
+#define rmp__ 145
+#define acsc__ 146
+#define pln__ 147
+#define kcbt__ 148
+#define smxon__ 149
+#define rmxon__ 150
+#define smam__ 151
+#define rmam__ 152
+#define xonc__ 153
+#define xoffc__ 154
+#define enacs__ 155
+#define smln__ 156
+#define rmln__ 157
+#define kbeg__ 158
+#define kcan__ 159
+#define kclo__ 160
+#define kcmd__ 161
+#define kcpy__ 162
+#define kcrt__ 163
+#define kend__ 164
+#define kent__ 165
+#define kext__ 166
+#define kfnd__ 167
+#define khlp__ 168
+#define kmrk__ 169
+#define kmsg__ 170
+#define kmov__ 171
+#define knxt__ 172
+#define kopn__ 173
+#define kopt__ 174
+#define kprv__ 175
+#define kprt__ 176
+#define krdo__ 177
+#define kref__ 178
+#define krfr__ 179
+#define krpl__ 180
+#define krst__ 181
+#define kres__ 182
+#define ksav__ 183
+#define kspd__ 184
+#define kund__ 185
+#define kBEG__ 186
+#define kCAN__ 187
+#define kCMD__ 188
+#define kCPY__ 189
+#define kCRT__ 190
+#define kDC__ 191
+#define kDL__ 192
+#define kslt__ 193
+#define kEND__ 194
+#define kEOL__ 195
+#define kEXT__ 196
+#define kFND__ 197
+#define kHLP__ 198
+#define kHOM__ 199
+#define kIC__ 200
+#define kLFT__ 201
+#define kMSG__ 202
+#define kMOV__ 203
+#define kNXT__ 204
+#define kOPT__ 205
+#define kPRV__ 206
+#define kPRT__ 207
+#define kRDO__ 208
+#define kRPL__ 209
+#define kRIT__ 210
+#define kRES__ 211
+#define kSAV__ 212
+#define kSPD__ 213
+#define kUND__ 214
+#define rfi__ 215
+#define kf11__ 216
+#define kf12__ 217
+#define kf13__ 218
+#define kf14__ 219
+#define kf15__ 220
+#define kf16__ 221
+#define kf17__ 222
+#define kf18__ 223
+#define kf19__ 224
+#define kf20__ 225
+#define kf21__ 226
+#define kf22__ 227
+#define kf23__ 228
+#define kf24__ 229
+#define kf25__ 230
+#define kf26__ 231
+#define kf27__ 232
+#define kf28__ 233
+#define kf29__ 234
+#define kf30__ 235
+#define kf31__ 236
+#define kf32__ 237
+#define kf33__ 238
+#define kf34__ 239
+#define kf35__ 240
+#define kf36__ 241
+#define kf37__ 242
+#define kf38__ 243
+#define kf39__ 244
+#define kf40__ 245
+#define kf41__ 246
+#define kf42__ 247
+#define kf43__ 248
+#define kf44__ 249
+#define kf45__ 250
+#define kf46__ 251
+#define kf47__ 252
+#define kf48__ 253
+#define kf49__ 254
+#define kf50__ 255
+#define kf51__ 256
+#define kf52__ 257
+#define kf53__ 258
+#define kf54__ 259
+#define kf55__ 260
+#define kf56__ 261
+#define kf57__ 262
+#define kf58__ 263
+#define kf59__ 264
+#define kf60__ 265
+#define kf61__ 266
+#define kf62__ 267
+#define kf63__ 268
+#define el1__ 269
+#define mgc__ 270
+#define smgl__ 271
+#define smgr__ 272
+
+#ifdef CAP
+char *Boolean_names[] = {
+"bw", "am", "xb", "xs", "xn", "eo", "gn", "hc", "km", "hs", "in", "da", "db",
+"mi", "ms", "os", "es", "xt", "hz", "ul", "xo", "HC", "nx", "NR", "NP", "5i"
+};
+
+char *Number_names[] = {
+"co#", "it#", "li#", "lm#", "sg#", "pb#", "vt#", "ws#", "Nl#", "lh#", "lw#"
+};
+
+char *String_names[] = {
+"bt=", "bl=", "cr=", "cs=", "ct=", "cl=", "ce=", "cd=", "ch=", "CC=", "cm=",
+"do=", "ho=", "vi=", "le=", "CM=", "ve=", "nd=", "ll=", "up=", "vs=", "dc=",
+"dl=", "ds=", "hd=", "as=", "mb=", "md=", "ti=", "dm=", "mh=", "im=", "mk=",
+"mp=", "mr=", "so=", "us=", "ec=", "ae=", "me=", "te=", "ed=", "ei=", "se=",
+"ue=", "vb=", "ff=", "fs=", "i1=", "i2=", "i3=", "if=", "ic=", "al=", "ip=",
+"kb=", "ka=", "kC=", "kt=", "kD=", "kL=", "kd=", "kM=", "kE=", "kS=", "k0=",
+"k1=", "k;=", "k2=", "k3=", "k4=", "k5=", "k6=", "k7=", "k8=", "k9=", "kh=",
+"kI=", "kA=", "kl=", "kH=", "kN=", "kP=", "kr=", "kF=", "kR=", "kT=", "ku=",
+"ke=", "ks=", "l0=", "l1=", "la=", "l2=", "l3=", "l4=", "l5=", "l6=", "l7=",
+"l8=", "l9=", "mo=", "mm=", "nw=", "pc=", "DC=", "DL=", "DO=", "IC=", "SF=",
+"AL=", "LE=", "RI=", "SR=", "UP=", "pk=", "pl=", "px=", "ps=", "pf=", "po=",
+"rp=", "r1=", "r2=", "r3=", "rf=", "rc=", "cv=", "sc=", "sf=", "sr=", "sa=",
+"st=", "wi=", "ta=", "ts=", "uc=", "hu=", "iP=", "K1=", "K3=", "K2=", "K4=",
+"K5=", "pO=", "rP=", "ac=", "pn=", "kB=", "SX=", "RX=", "SA=", "RA=", "XN=",
+"XF=", "eA=", "LO=", "LF=", "@1=", "@2=", "@3=", "@4=", "@5=", "@6=", "@7=",
+"@8=", "@9=", "@0=", "%1=", "%2=", "%3=", "%4=", "%5=", "%6=", "%7=", "%8=",
+"%9=", "%0=", "&1=", "&2=", "&3=", "&4=", "&5=", "&6=", "&7=", "&8=", "&9=",
+"&0=", "*1=", "*2=", "*3=", "*4=", "*5=", "*6=", "*7=", "*8=", "*9=", "*0=",
+"#1=", "#2=", "#3=", "#4=", "%a=", "%b=", "%c=", "%d=", "%e=", "%f=", "%g=",
+"%h=", "%i=", "%j=", "!1=", "!2=", "!3=", "RF=", "F1=", "F2=", "F3=", "F4=",
+"F5=", "F6=", "F7=", "F8=", "F9=", "FA=", "FB=", "FC=", "FD=", "FE=", "FF=",
+"FG=", "FH=", "FI=", "FJ=", "FK=", "FL=", "FM=", "FN=", "FO=", "FP=", "FQ=",
+"FR=", "FS=", "FT=", "FU=", "FV=", "FW=", "FX=", "FY=", "FZ=", "Fa=", "Fb=",
+"Fc=", "Fd=", "Fe=", "Ff=", "Fg=", "Fh=", "Fi=", "Fj=", "Fk=", "Fl=", "Fm=",
+"Fn=", "Fo=", "Fp=", "Fq=", "Fr=", "cb=", "MC=", "ML=", "MR="
+};
+#endif
+
+char *new_curse = "October 1987";
+
+char in_buff[100]; /* buffer for ungetch */
+int bufp; /* next free position in in_buff */
+
+char *TERMINAL_TYPE = NULL; /* terminal type to be gotten from environment */
+int CFOUND = FALSE;
+int Data_Line_len = 0;
+int Max_Key_len; /* max length of a sequence sent by a key */
+char *Data_Line = NULL;
+char *TERM_PATH = NULL;
+char *TERM_data_ptr = NULL;
+char *Term_File_name = NULL; /* name of file containing terminal description */
+FILE *TFP; /* file pointer to file with terminal des. */
+int Fildes; /* file descriptor for terminfo file */
+int STAND = FALSE; /* is standout mode activated? */
+int TERM_INFO = FALSE; /* is terminfo being used (TRUE), or termcap (FALSE) */
+int Time_Out; /* set when time elapsed while trying to read function key */
+int Curr_x; /* current x position on screen */
+int Curr_y; /* current y position on the screen */
+int LINES;
+int COLS;
+int Move_It; /* flag to move cursor if magic cookie glitch */
+int initialized = FALSE; /* tells whether new_curse is initialized */
+float speed;
+float chars_per_millisecond;
+int Repaint_screen; /* if an operation to change screen impossible, repaint screen */
+int Intr; /* storeage for interrupt character */
+int Parity; /* 0 = no parity, 1 = odd parity, 2 = even parity */
+int Noblock; /* for BSD systems */
+int Num_bits; /* number of bits per character */
+int Flip_Bytes; /* some systems have byte order reversed */
+int interrupt_flag = FALSE; /* set true if SIGWINCH received */
+
+#ifndef CAP
+char *Strings;
+#endif
+
+#if !defined(TERMCAP)
+#define TERMCAP "/etc/termcap"
+#endif
+
+struct KEYS {
+ int length; /* length of string sent by key */
+ char *string; /* string sent by key */
+ int value; /* CURSES value of key (9-bit) */
+ };
+
+struct KEY_STACK {
+ struct KEYS *element;
+ struct KEY_STACK *next;
+ };
+
+struct KEY_STACK *KEY_TOS = NULL;
+struct KEY_STACK *KEY_POINT;
+
+/*
+ |
+ | Not all systems have good terminal information, so we will define
+ | keyboard information here for the most widely used terminal type,
+ | the VT100.
+ |
+ */
+
+struct KEYS vt100[] =
+ {
+ { 3, "\033[A", 0403 }, /* key up */
+ { 3, "\033[C", 0405 }, /* key right */
+ { 3, "\033[D", 0404 }, /* key left */
+
+ { 4, "\033[6~", 0522 }, /* key next page */
+ { 4, "\033[5~", 0523 }, /* key prev page */
+ { 3, "\033[[", 0550 }, /* key end */
+ { 3, "\033[@", 0406 }, /* key home */
+ { 4, "\033[2~", 0513 }, /* key insert char */
+
+ { 3, "\033[y", 0410 }, /* key F0 */
+ { 3, "\033[P", 0411 }, /* key F1 */
+ { 3, "\033[Q", 0412 }, /* key F2 */
+ { 3, "\033[R", 0413 }, /* key F3 */
+ { 3, "\033[S", 0414 }, /* key F4 */
+ { 3, "\033[t", 0415 }, /* key F5 */
+ { 3, "\033[u", 0416 }, /* key F6 */
+ { 3, "\033[v", 0417 }, /* key F7 */
+ { 3, "\033[l", 0420 }, /* key F8 */
+ { 3, "\033[w", 0421 }, /* key F9 */
+ { 3, "\033[x", 0422 }, /* key F10 */
+
+ { 5, "\033[10~", 0410 }, /* key F0 */
+ { 5, "\033[11~", 0411 }, /* key F1 */
+ { 5, "\033[12~", 0412 }, /* key F2 */
+ { 5, "\033[13~", 0413 }, /* key F3 */
+ { 5, "\033[14~", 0414 }, /* key F4 */
+ { 5, "\033[15~", 0415 }, /* key F5 */
+ { 5, "\033[17~", 0416 }, /* key F6 */
+ { 5, "\033[18~", 0417 }, /* key F7 */
+ { 5, "\033[19~", 0420 }, /* key F8 */
+ { 5, "\033[20~", 0421 }, /* key F9 */
+ { 5, "\033[21~", 0422 }, /* key F10 */
+ { 5, "\033[23~", 0423 }, /* key F11 */
+ { 5, "\033[24~", 0424 }, /* key F12 */
+ { 3, "\033[q", 0534 }, /* ka1 upper-left of keypad */
+ { 3, "\033[s", 0535 }, /* ka3 upper-right of keypad */
+ { 3, "\033[r", 0536 }, /* kb2 center of keypad */
+ { 3, "\033[p", 0537 }, /* kc1 lower-left of keypad */
+ { 3, "\033[n", 0540 }, /* kc3 lower-right of keypad */
+
+ /*
+ | The following are the same keys as above, but with
+ | a different character following the escape char.
+ */
+
+ { 3, "\033OA", 0403 }, /* key up */
+ { 3, "\033OC", 0405 }, /* key right */
+ { 3, "\033OD", 0404 }, /* key left */
+ { 3, "\033OB", 0402 }, /* key down */
+ { 4, "\033O6~", 0522 }, /* key next page */
+ { 4, "\033O5~", 0523 }, /* key prev page */
+ { 3, "\033O[", 0550 }, /* key end */
+ { 3, "\033O@", 0406 }, /* key home */
+ { 4, "\033O2~", 0513 }, /* key insert char */
+
+ { 3, "\033Oy", 0410 }, /* key F0 */
+ { 3, "\033OP", 0411 }, /* key F1 */
+ { 3, "\033OQ", 0412 }, /* key F2 */
+ { 3, "\033OR", 0413 }, /* key F3 */
+ { 3, "\033OS", 0414 }, /* key F4 */
+ { 3, "\033Ot", 0415 }, /* key F5 */
+ { 3, "\033Ou", 0416 }, /* key F6 */
+ { 3, "\033Ov", 0417 }, /* key F7 */
+ { 3, "\033Ol", 0420 }, /* key F8 */
+ { 3, "\033Ow", 0421 }, /* key F9 */
+ { 3, "\033Ox", 0422 }, /* key F10 */
+
+ { 5, "\033O10~", 0410 }, /* key F0 */
+ { 5, "\033O11~", 0411 }, /* key F1 */
+ { 5, "\033O12~", 0412 }, /* key F2 */
+ { 5, "\033O13~", 0413 }, /* key F3 */
+ { 5, "\033O14~", 0414 }, /* key F4 */
+ { 5, "\033O15~", 0415 }, /* key F5 */
+ { 5, "\033O17~", 0416 }, /* key F6 */
+ { 5, "\033O18~", 0417 }, /* key F7 */
+ { 5, "\033O19~", 0420 }, /* key F8 */
+ { 5, "\033O20~", 0421 }, /* key F9 */
+ { 5, "\033O21~", 0422 }, /* key F10 */
+ { 5, "\033O23~", 0423 }, /* key F11 */
+ { 5, "\033O24~", 0424 }, /* key F12 */
+ { 3, "\033Oq", 0534 }, /* ka1 upper-left of keypad */
+ { 3, "\033Os", 0535 }, /* ka3 upper-right of keypad */
+ { 3, "\033Or", 0536 }, /* kb2 center of keypad */
+ { 3, "\033Op", 0537 }, /* kc1 lower-left of keypad */
+ { 3, "\033On", 0540 }, /* kc3 lower-right of keypad */
+
+ { 0, "", 0 } /* end */
+ };
+
+struct Parameters {
+ int value;
+ struct Parameters *next;
+ };
+
+int Key_vals[] = {
+ 0407, 0526, 0515, 0525, 0512, 0510, 0402, 0514, 0517, 0516, 0410, 0411,
+ 0422, 0412, 0413, 0414, 0415, 0416, 0417, 0420, 0421, 0406, 0513, 0511,
+ 0404, 0533, 0522, 0523, 0405, 0520, 0521, 0524, 0403,
+ 0534, 0535, 0536, 0537, 0540, 0541, 0542, 0543, 0544, 0545, 0546, 0547,
+ 0550, 0527, 0551, 0552, 0553, 0554, 0555, 0556, 0557, 0560, 0561, 0562,
+ 0532, 0563, 0564, 0565, 0566, 0567, 0570, 0571, 0627, 0630, 0572, 0573,
+ 0574, 0575, 0576, 0577, 0600, 0601, 0602, 0603, 0604, 0605, 0606, 0607,
+ 0610, 0611, 0612, 0613, 0614, 0615, 0616, 0617, 0620, 0621, 0622, 0623,
+ 0624, 0625, 0626, 0423, 0424, 0425, 0426, 0427, 0430, 0431,
+ 0432, 0433, 0434, 0435, 0436, 0437, 0440, 0441, 0442, 0443, 0444, 0445,
+ 0446, 0447, 0450, 0451, 0452, 0453, 0454, 0455, 0456, 0457, 0460, 0461,
+ 0462, 0463, 0464, 0465, 0466, 0467, 0470, 0471, 0472, 0473, 0474, 0475,
+ 0476, 0477, 0500, 0501, 0502, 0503, 0504, 0505, 0506, 0507
+};
+
+int attributes_set[9];
+
+static int nc_attributes = 0; /* global attributes for new_curse to observe */
+
+#ifdef SYS5
+struct termio Terminal;
+struct termio Saved_tty;
+#else
+struct sgttyb Terminal;
+struct sgttyb Saved_tty;
+#endif
+
+char *tc_;
+
+int Booleans[128];
+int Numbers[128];
+char *String_table[1024];
+
+int *virtual_lines;
+
+static char nc_scrolling_ability = FALSE;
+
+char *terminfo_path[] = {
+ "/usr/lib/terminfo",
+ "/usr/share/lib/terminfo",
+ "/usr/share/terminfo",
+ NULL
+ };
+
+#ifdef CAP
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif /* __STDC__ */
+
+int tc_Get_int P_((int));
+void CAP_PARSE P_((void));
+void Find_term P_((void));
+
+#undef P_
+
+#endif /* CAP */
+
+
+#ifndef __STDC__
+#ifndef HAS_STDLIB
+extern char *fgets();
+extern char *malloc();
+extern char *getenv();
+FILE *fopen(); /* declaration for open function */
+#endif /* HAS_STDLIB */
+#endif /* __STDC__ */
+
+#ifdef SIGWINCH
+
+/*
+ | Copy the contents of one window to another.
+ */
+
+void
+copy_window(origin, destination)
+WINDOW *origin, *destination;
+{
+ int row, column;
+ struct _line *orig, *dest;
+
+ orig = origin->first_line;
+ dest = destination->first_line;
+
+ for (row = 0;
+ row < (min(origin->Num_lines, destination->Num_lines));
+ row++)
+ {
+ for (column = 0;
+ column < (min(origin->Num_cols, destination->Num_cols));
+ column++)
+ {
+ dest->row[column] = orig->row[column];
+ dest->attributes[column] = orig->attributes[column];
+ }
+ dest->changed = orig->changed;
+ dest->scroll = orig->scroll;
+ dest->last_char = min(orig->last_char, destination->Num_cols);
+ orig = orig->next_screen;
+ dest = dest->next_screen;
+ }
+ destination->LX = min((destination->Num_cols - 1), origin->LX);
+ destination->LY = min((destination->Num_lines - 1), origin->LY);
+ destination->Attrib = origin->Attrib;
+ destination->scroll_up = origin->scroll_up;
+ destination->scroll_down = origin->scroll_down;
+ destination->SCROLL_CLEAR = origin->SCROLL_CLEAR;
+}
+
+void
+reinitscr(foo)
+int foo;
+{
+ WINDOW *local_virt;
+ WINDOW *local_std;
+ WINDOW *local_cur;
+
+ signal(SIGWINCH, reinitscr);
+#ifdef TIOCGWINSZ
+ if (ioctl(0, TIOCGWINSZ, &ws) >= 0)
+ {
+ if (ws.ws_row == LINES && ws.ws_col == COLS)
+ return;
+ if (ws.ws_row > 0)
+ LINES = ws.ws_row;
+ if (ws.ws_col > 0)
+ COLS = ws.ws_col;
+ }
+#endif /* TIOCGWINSZ */
+ local_virt = newwin(LINES, COLS, 0, 0);
+ local_std = newwin(LINES, COLS, 0, 0);
+ local_cur = newwin(LINES, COLS, 0, 0);
+ copy_window(virtual_scr, local_virt);
+ copy_window(stdscr, local_std);
+ copy_window(curscr, local_cur);
+ delwin(virtual_scr);
+ delwin(stdscr);
+ delwin(curscr);
+ virtual_scr = local_virt;
+ stdscr = local_std;
+ curscr = local_cur;
+ free(virtual_lines);
+ virtual_lines = (int *) malloc(LINES * (sizeof(int)));
+ interrupt_flag = TRUE;
+}
+#endif /* SIGWINCH */
+
+void
+initscr() /* initialize terminal for operations */
+{
+ int value;
+ int counter;
+ char *lines_string;
+ char *columns_string;
+#ifdef CAP
+ char *pointer;
+#endif /* CAP */
+
+#ifdef DIAG
+printf("starting initscr \n");fflush(stdout);
+#endif
+ if (initialized)
+ return;
+#ifdef BSD_SELECT
+ setbuf(stdin, NULL);
+#endif /* BSD_SELECT */
+ Flip_Bytes = FALSE;
+ Parity = 0;
+ Time_Out = FALSE;
+ bufp = 0;
+ Move_It = FALSE;
+ Noblock = FALSE;
+#ifdef SYS5
+ value = ioctl(0, TCGETA, &Terminal);
+ if (Terminal.c_cflag & PARENB)
+ {
+ if (Terminal.c_cflag & PARENB)
+ Parity = 1;
+ else
+ Parity = 2;
+ }
+ if ((Terminal.c_cflag & CS8) == CS8)
+ {
+ Num_bits = 8;
+ }
+ else if ((Terminal.c_cflag & CS7) == CS7)
+ Num_bits = 7;
+ else if ((Terminal.c_cflag & CS6) == CS6)
+ Num_bits = 6;
+ else
+ Num_bits = 5;
+ value = Terminal.c_cflag & 037;
+ switch (value) {
+ case 01: speed = 50.0;
+ break;
+ case 02: speed = 75.0;
+ break;
+ case 03: speed = 110.0;
+ break;
+ case 04: speed = 134.5;
+ break;
+ case 05: speed = 150.0;
+ break;
+ case 06: speed = 200.0;
+ break;
+ case 07: speed = 300.0;
+ break;
+ case 010: speed = 600.0;
+ break;
+ case 011: speed = 900.0;
+ break;
+ case 012: speed = 1200.0;
+ break;
+ case 013: speed = 1800.0;
+ break;
+ case 014: speed = 2400.0;
+ break;
+ case 015: speed = 3600.0;
+ break;
+ case 016: speed = 4800.0;
+ break;
+ case 017: speed = 7200.0;
+ break;
+ case 020: speed = 9600.0;
+ break;
+ case 021: speed = 19200.0;
+ break;
+ case 022: speed = 38400.0;
+ break;
+ default: speed = 0.0;
+ }
+#else
+ value = ioctl(0, TIOCGETP, &Terminal);
+ if (Terminal.sg_flags & EVENP)
+ Parity = 2;
+ else if (Terminal.sg_flags & ODDP)
+ Parity = 1;
+ value = Terminal.sg_ospeed;
+ switch (value) {
+ case 01: speed = 50.0;
+ break;
+ case 02: speed = 75.0;
+ break;
+ case 03: speed = 110.0;
+ break;
+ case 04: speed = 134.5;
+ break;
+ case 05: speed = 150.0;
+ break;
+ case 06: speed = 200.0;
+ break;
+ case 07: speed = 300.0;
+ break;
+ case 010: speed = 600.0;
+ break;
+ case 011: speed = 1200.0;
+ break;
+ case 012: speed = 1800.0;
+ break;
+ case 013: speed = 2400.0;
+ break;
+ case 014: speed = 4800.0;
+ break;
+ case 015: speed = 9600.0;
+ break;
+ default: speed = 0.0;
+ }
+#endif
+ chars_per_millisecond = (0.001 * speed) / 8.0;
+ TERMINAL_TYPE = getenv("TERM");
+ if (TERMINAL_TYPE == NULL)
+ {
+ printf("unknown terminal type\n");
+ exit(0);
+ }
+#ifndef CAP
+ Fildes = -1;
+ TERM_PATH = getenv("TERMINFO");
+ if (TERM_PATH != NULL)
+ {
+ Data_Line_len = 23 + strlen(TERM_PATH) + strlen(TERMINAL_TYPE);
+ Term_File_name = malloc(Data_Line_len);
+ sprintf(Term_File_name, "%s/%c/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ if (Fildes == -1)
+ {
+ sprintf(Term_File_name, "%s/%x/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ }
+ }
+ counter = 0;
+ while ((Fildes == -1) && (terminfo_path[counter] != NULL))
+ {
+ TERM_PATH = terminfo_path[counter];
+ Data_Line_len = 23 + strlen(TERM_PATH) + strlen(TERMINAL_TYPE);
+ Term_File_name = malloc(Data_Line_len);
+ sprintf(Term_File_name, "%s/%c/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ if (Fildes == -1)
+ {
+ sprintf(Term_File_name, "%s/%x/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ }
+ counter++;
+ }
+ if (Fildes == -1)
+ {
+ free(Term_File_name);
+ Term_File_name = NULL;
+ }
+ else
+ TERM_INFO = INFO_PARSE();
+#else
+ /*
+ | termcap information can be in the TERMCAP env variable, if so
+ | use that, otherwise check the /etc/termcap file
+ */
+ if ((pointer = Term_File_name = getenv("TERMCAP")) != NULL)
+ {
+ if (*Term_File_name != '/')
+ Term_File_name = TERMCAP;
+ }
+ else
+ {
+ Term_File_name = TERMCAP;
+ }
+ if ((TFP = fopen(Term_File_name, "r")) == NULL)
+ {
+ printf("unable to open %s file \n", TERMCAP);
+ exit(0);
+ }
+ for (value = 0; value < 1024; value++)
+ String_table[value] = NULL;
+ for (value = 0; value < 128; value++)
+ Booleans[value] = 0;
+ for (value = 0; value < 128; value++)
+ Numbers[value] = 0;
+ Data_Line = malloc(512);
+ if (pointer && *pointer != '/')
+ {
+ TERM_data_ptr = pointer;
+ CAP_PARSE();
+ }
+ else
+ {
+ Find_term();
+ CAP_PARSE();
+ }
+#endif
+ if (String_table[pc__] == NULL)
+ String_table[pc__] = "\0";
+ if ((String_table[cm__] == NULL) || (Booleans[hc__]))
+ {
+ fprintf(stderr, "sorry, unable to use this terminal type for screen editing\n");
+ exit(0);
+ }
+ Key_Get();
+ keys_vt100();
+ LINES = Numbers[li__];
+ COLS = Numbers[co__];
+ if ((lines_string = getenv("LINES")) != NULL)
+ {
+ value = atoi(lines_string);
+ if (value > 0)
+ LINES = value;
+ }
+ if ((columns_string = getenv("COLUMNS")) != NULL)
+ {
+ value = atoi(columns_string);
+ if (value > 0)
+ COLS = value;
+ }
+#ifdef TIOCGWINSZ
+ /*
+ | get the window size
+ */
+ if (ioctl(0, TIOCGWINSZ, &ws) >= 0)
+ {
+ if (ws.ws_row > 0)
+ LINES = ws.ws_row;
+ if (ws.ws_col > 0)
+ COLS = ws.ws_col;
+ }
+#endif
+ virtual_scr = newwin(LINES, COLS, 0, 0);
+ stdscr = newwin(LINES, COLS, 0, 0);
+ curscr = newwin(LINES, COLS, 0, 0);
+ wmove(stdscr, 0, 0);
+ werase(stdscr);
+ Repaint_screen = TRUE;
+ initialized = TRUE;
+ virtual_lines = (int *) malloc(LINES * (sizeof(int)));
+
+#ifdef SIGWINCH
+ /*
+ | reset size of windows and LINES and COLS if term window
+ | changes size
+ */
+ signal(SIGWINCH, reinitscr);
+#endif /* SIGWINCH */
+
+ /*
+ | check if scrolling is available
+ */
+
+ nc_scrolling_ability = ((String_table[al__] != NULL) &&
+ (String_table[dl__])) || ((String_table[cs__])
+ && (String_table[sr__]));
+
+}
+
+#ifndef CAP
+int
+Get_int() /* get a two-byte integer from the terminfo file */
+{
+ int High_byte;
+ int Low_byte;
+ int temp;
+
+ Low_byte = *((unsigned char *) TERM_data_ptr++);
+ High_byte = *((unsigned char *) TERM_data_ptr++);
+ if (Flip_Bytes)
+ {
+ temp = Low_byte;
+ Low_byte = High_byte;
+ High_byte = temp;
+ }
+ if ((High_byte == 255) && (Low_byte == 255))
+ return (-1);
+ else
+ return(Low_byte + (High_byte * 256));
+}
+
+int
+INFO_PARSE() /* parse off the data in the terminfo data file */
+{
+ int offset;
+ int magic_number = 0;
+ int counter = 0;
+ int Num_names = 0;
+ int Num_bools = 0;
+ int Num_ints = 0;
+ int Num_strings = 0;
+ int string_table_len = 0;
+ char *temp_ptr;
+
+ TERM_data_ptr = Data_Line = malloc((10240 * (sizeof(char))));
+ Data_Line_len = read(Fildes, Data_Line, 10240);
+ if ((Data_Line_len >= 10240) || (Data_Line_len < 0))
+ return(0);
+ /*
+ | get magic number
+ */
+ magic_number = Get_int();
+ /*
+ | if magic number not right, reverse byte order and check again
+ */
+ if (magic_number != 282)
+ {
+ Flip_Bytes = TRUE;
+ TERM_data_ptr--;
+ TERM_data_ptr--;
+ magic_number = Get_int();
+ if (magic_number != 282)
+ return(0);
+ }
+ /*
+ | get the number of each type in the terminfo data file
+ */
+ Num_names = Get_int();
+ Num_bools = Get_int();
+ Num_ints = Get_int();
+ Num_strings = Get_int();
+ string_table_len = Get_int();
+ Strings = malloc(string_table_len);
+ while (Num_names > 0)
+ {
+ TERM_data_ptr++;
+ Num_names--;
+ }
+ counter = 0;
+ while (Num_bools)
+ {
+ Num_bools--;
+ Booleans[counter++] = *TERM_data_ptr++;
+ }
+ if ((unsigned long)TERM_data_ptr & 1) /* force alignment */
+ TERM_data_ptr++;
+ counter = 0;
+ while (Num_ints)
+ {
+ Num_ints--;
+ Numbers[counter] = Get_int();
+ counter++;
+ }
+ temp_ptr = TERM_data_ptr + Num_strings + Num_strings;
+ memcpy(Strings, temp_ptr, string_table_len);
+ counter = bt__;
+ while (Num_strings)
+ {
+ Num_strings--;
+ if ((offset=Get_int()) != -1)
+ {
+ if (String_table[counter] == NULL)
+ String_table[counter] = Strings + offset;
+ }
+ else
+ String_table[counter] = NULL;
+ counter++;
+ }
+ close(Fildes);
+ free(Data_Line);
+ return(TRUE);
+}
+#endif /* ifndef CAP */
+
+int
+AtoI() /* convert ascii text to integers */
+{
+ int Temp;
+
+ Temp = 0;
+ while ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Temp = (Temp * 10) + (*TERM_data_ptr - '0');
+ TERM_data_ptr++;
+ }
+ return(Temp);
+}
+
+void
+Key_Get() /* create linked list with all key sequences obtained from terminal database */
+{
+ int Counter;
+ int Klen;
+ int key_def;
+ struct KEY_STACK *Spoint;
+
+ Max_Key_len = 0;
+ Counter = 0;
+ key_def = kb__;
+ while (key_def <= kf63__)
+ {
+ if (key_def == ke__)
+ key_def = K1__;
+ else if (key_def == (K5__ + 1))
+ key_def = kcbt__;
+ else if (key_def == (kcbt__ + 1))
+ key_def = kbeg__;
+ else if (key_def == (kUND__ + 1))
+ key_def = kf11__;
+ if (String_table[key_def] != NULL)
+ {
+ if (KEY_TOS == NULL)
+ Spoint = KEY_TOS = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ else
+ {
+ Spoint = KEY_TOS;
+ while (Spoint->next != NULL)
+ Spoint = Spoint->next;
+ Spoint->next = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ Spoint = Spoint->next;
+ }
+ Spoint->next = NULL;
+ Spoint->element = (struct KEYS *) malloc(sizeof(struct KEYS));
+ Spoint->element->string = String_table[key_def];
+ Spoint->element->length = strlen(String_table[key_def]);
+ Spoint->element->value = Key_vals[Counter];
+ Klen = strlen(Spoint->element->string);
+ if (Klen > Max_Key_len)
+ Max_Key_len = Klen;
+ /*
+ | Some terminal types accept keystrokes of the form
+ | \E[A and \EOA, substituting '[' for 'O'. Make a
+ | duplicate of such key strings (since the
+ | database will only have one version) so new_curse
+ | can understand both.
+ */
+ if ((Spoint->element->length > 1) &&
+ ((String_table[key_def][1] == '[') ||
+ (String_table[key_def][1] == 'O')))
+ {
+ Spoint->next = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ Spoint = Spoint->next;
+ Spoint->next = NULL;
+ Spoint->element = (struct KEYS *) malloc(sizeof(struct KEYS));
+ Spoint->element->length = strlen(String_table[key_def]);
+ Spoint->element->string = malloc(Spoint->element->length + 1);
+ strcpy(Spoint->element->string, String_table[key_def]);
+ Spoint->element->value = Key_vals[Counter];
+ Klen = strlen(Spoint->element->string);
+ if (Klen > Max_Key_len)
+ Max_Key_len = Klen;
+
+ if (String_table[key_def][1] == '[')
+ Spoint->element->string[1] = 'O';
+ else
+ Spoint->element->string[1] = '[';
+ }
+ }
+ key_def++;
+ Counter++;
+ }
+}
+
+/*
+ | insert information about keys for a vt100 terminal
+ */
+
+void
+keys_vt100()
+{
+ int counter;
+ int Klen;
+ struct KEY_STACK *Spoint;
+
+ Spoint = KEY_TOS;
+ while (Spoint->next != NULL)
+ Spoint = Spoint->next;
+ for (counter = 0; vt100[counter].length != 0; counter++)
+ {
+ Spoint->next = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ Spoint = Spoint->next;
+ Spoint->next = NULL;
+ Spoint->element = &vt100[counter];
+ Klen = strlen(Spoint->element->string);
+ if (Klen > Max_Key_len)
+ Max_Key_len = Klen;
+ }
+}
+
+#ifdef CAP
+char *
+String_Get(param) /* read the string */
+char *param;
+{
+ char *String;
+ char *Temp;
+ int Counter;
+
+ if (param == NULL)
+ {
+ while (*TERM_data_ptr != '=')
+ TERM_data_ptr++;
+ Temp = ++TERM_data_ptr;
+ Counter = 1;
+ while ((*Temp != ':') && (*Temp != (char)NULL))
+ {
+ Counter++;
+ Temp++;
+ }
+ if (Counter == 1) /* no data */
+ return(NULL);
+ String = Temp = malloc(Counter);
+ while ((*TERM_data_ptr != ':') && (*TERM_data_ptr != (char)NULL))
+ {
+ if (*TERM_data_ptr == '\\')
+ {
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == 'n')
+ *Temp = '\n';
+ else if (*TERM_data_ptr == 't')
+ *Temp = '\t';
+ else if (*TERM_data_ptr == 'b')
+ *Temp = '\b';
+ else if (*TERM_data_ptr == 'r')
+ *Temp = '\r';
+ else if (*TERM_data_ptr == 'f')
+ *Temp = '\f';
+ else if ((*TERM_data_ptr == 'e') || (*TERM_data_ptr == 'E'))
+ *Temp = '\033'; /* escape */
+ else if (*TERM_data_ptr == '\\')
+ *Temp = '\\';
+ else if (*TERM_data_ptr == '\'')
+ *Temp = '\'';
+ else if ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Counter = 0;
+ while ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Counter = (8 * Counter) + (*TERM_data_ptr - '0');
+ TERM_data_ptr++; /* ? */
+ }
+ *Temp = Counter;
+ TERM_data_ptr--;
+ }
+ TERM_data_ptr++;
+ Temp++;
+ }
+ else if (*TERM_data_ptr == '^')
+ {
+ TERM_data_ptr++;
+ if ((*TERM_data_ptr >= '@') && (*TERM_data_ptr <= '_'))
+ *Temp = *TERM_data_ptr - '@';
+ else if (*TERM_data_ptr == '?')
+ *Temp = 127;
+ TERM_data_ptr++;
+ Temp++;
+ }
+ else
+ *Temp++ = *TERM_data_ptr++;
+ }
+ *Temp = (char)NULL;
+ param = String;
+ }
+ else
+ {
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != ':'))
+ TERM_data_ptr++;
+ }
+ return(param);
+}
+
+int
+tc_Get_int(param) /* read the integer */
+int param;
+{
+ int Itemp;
+
+ if (param == 0)
+ {
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != '#'))
+ TERM_data_ptr++;
+ TERM_data_ptr++;
+ Itemp = AtoI();
+ param = Itemp;
+ }
+ else
+ {
+ while (*TERM_data_ptr != ':')
+ TERM_data_ptr++;
+ }
+ return(param);
+}
+
+void
+Find_term() /* find terminal description in termcap file */
+{
+ char *Name;
+ char *Ftemp;
+
+ Ftemp = Name = malloc(strlen(TERMINAL_TYPE) + 2);
+ strcpy(Name, TERMINAL_TYPE);
+ while (*Ftemp != (char)NULL)
+ Ftemp++;
+ *Ftemp++ = '|';
+ *Ftemp = (char)NULL;
+ CFOUND = FALSE;
+ Data_Line_len = strlen(TERMINAL_TYPE) + 1;
+ while ((!CFOUND) && ((TERM_data_ptr=fgets(Data_Line, 512, TFP)) != NULL))
+ {
+ if ((*TERM_data_ptr != ' ') && (*TERM_data_ptr != '\t') && (*TERM_data_ptr != '#'))
+ {
+ while ((!CFOUND) && (*TERM_data_ptr != (char)NULL))
+ {
+ CFOUND = !strncmp(TERM_data_ptr, Name, Data_Line_len);
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != '|') && (*TERM_data_ptr != '#') && (*TERM_data_ptr != ':'))
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == '|')
+ TERM_data_ptr++;
+ else if (!CFOUND)
+ *TERM_data_ptr = (char)NULL;
+ }
+ }
+ }
+ if (!CFOUND)
+ {
+ printf("terminal type %s not found\n", TERMINAL_TYPE);
+ exit(0);
+ }
+}
+
+void
+CAP_PARSE() /* parse off the data in the termcap data file */
+{
+ int offset;
+ int found;
+
+ do
+ {
+ while (*TERM_data_ptr != (char)NULL)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < 26); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, Boolean_names[offset], 2))
+ {
+ found = TRUE;
+ Booleans[offset] = TRUE;
+ }
+ }
+ if (!found)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < lw__); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, Number_names[offset], 3))
+ {
+ found = TRUE;
+ Numbers[offset] = tc_Get_int(Numbers[offset]);
+ }
+ }
+ }
+ if (!found)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < smgr__); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, String_names[offset], 3))
+ {
+ found = TRUE;
+ String_table[offset] = String_Get(String_table[offset]);
+ }
+ }
+ }
+
+ if (!strncmp(TERM_data_ptr, "tc=", 3))
+ tc_ = String_Get(NULL);
+ while ((*TERM_data_ptr != ':') && (*TERM_data_ptr != (char)NULL))
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == ':')
+ TERM_data_ptr++;
+ }
+ } while (((TERM_data_ptr = fgets(Data_Line, 512, TFP)) != NULL) && ((*TERM_data_ptr == ' ') || (*TERM_data_ptr == '\t')));
+ if (tc_ != NULL)
+ {
+ TERMINAL_TYPE = tc_;
+ rewind(TFP);
+ Find_term();
+ tc_ = NULL;
+ CAP_PARSE();
+ }
+ else
+ fclose(TFP);
+}
+#endif /* ifdef CAP */
+
+struct _line *
+Screenalloc(columns)
+int columns;
+{
+ int i;
+ struct _line *tmp;
+
+ tmp = (struct _line *) malloc(sizeof (struct _line));
+ tmp->row = malloc(columns + 1);
+ tmp->attributes = malloc(columns + 1);
+ tmp->prev_screen = NULL;
+ tmp->next_screen = NULL;
+ for (i = 0; i < columns; i++)
+ {
+ tmp->row[i] = ' ';
+ tmp->attributes[i] = '\0';
+ }
+ tmp->scroll = tmp->changed = FALSE;
+ tmp->row[0] = '\0';
+ tmp->attributes[0] = '\0';
+ tmp->row[columns] = '\0';
+ tmp->attributes[columns] = '\0';
+ tmp->last_char = 0;
+ return(tmp);
+}
+
+WINDOW *newwin(lines, cols, start_l, start_c)
+int lines, cols; /* number of lines and columns to be in window */
+int start_l, start_c; /* starting line and column to be inwindow */
+{
+ WINDOW *Ntemp;
+ struct _line *temp_screen;
+ int i;
+
+ Ntemp = (WINDOW *) malloc(sizeof(WINDOW));
+ Ntemp->SR = start_l;
+ Ntemp->SC = start_c;
+ Ntemp->Num_lines = lines;
+ Ntemp->Num_cols = cols;
+ Ntemp->LX = 0;
+ Ntemp->LY = 0;
+ Ntemp->scroll_down = Ntemp->scroll_up = 0;
+ Ntemp->SCROLL_CLEAR = FALSE;
+ Ntemp->Attrib = FALSE;
+ Ntemp->first_line = temp_screen = Screenalloc(cols);
+ Ntemp->first_line->number = 0;
+ Ntemp->line_array = (struct _line **) malloc(LINES * sizeof(struct _line *));
+
+ Ntemp->line_array[0] = Ntemp->first_line;
+
+ for (i = 1; i < lines; i++)
+ {
+ temp_screen->next_screen = Screenalloc(cols);
+ temp_screen->next_screen->number = i;
+ temp_screen->next_screen->prev_screen = temp_screen;
+ temp_screen = temp_screen->next_screen;
+ Ntemp->line_array[i] = temp_screen;
+ }
+ Ntemp->first_line->prev_screen = NULL;
+ temp_screen->next_screen = NULL;
+ return(Ntemp);
+}
+
+#ifdef CAP
+void
+Cap_Out(string, p_list, place) /* interpret the output string if necessary */
+char *string;
+int p_list[]; /* stack of values */
+int place; /* place keeper of top of stack */
+{
+ char *Otemp; /* temporary string pointer to parse output */
+ int delay;
+ int p1, p2, temp;
+ float chars;
+
+ if (string == NULL)
+ return;
+
+ if (p_list != NULL)
+ {
+ p1 = p_list[--place];
+ p2 = p_list[--place];
+ }
+ delay = 0;
+ Otemp = string;
+ if ((*Otemp >= '0') && (*Otemp <= '9'))
+ {
+ delay = atoi(Otemp);
+ while ((*Otemp >= '0') && (*Otemp <= '9'))
+ Otemp++;
+ if (*Otemp == '*')
+ Otemp++;
+ }
+ while (*Otemp != (char)NULL)
+ {
+ if (*Otemp == '%')
+ {
+ Otemp++;
+ if ((*Otemp == 'd') || (*Otemp == '2') || (*Otemp == '3') || (*Otemp == '.') || (*Otemp == '+'))
+ {
+ if (*Otemp == 'd')
+ printf("%d", p1);
+ else if (*Otemp == '2')
+ printf("%02d", p1);
+ else if (*Otemp == '3')
+ printf("%03d", p1);
+ else if (*Otemp == '+')
+ {
+ Otemp++;
+ p1 += *Otemp;
+ putchar(p1);
+ }
+ else if (*Otemp == '.')
+ putchar(p1);
+ p1 = p2;
+ p2 = 0;
+ }
+ else if (*Otemp == '>')
+ {
+ Otemp++;
+ if (p1 > *Otemp)
+ {
+ Otemp++;
+ p1 += *Otemp;
+ }
+ else
+ Otemp++;
+ }
+ else if (*Otemp == 'r')
+ {
+ temp = p1;
+ p1 = p2;
+ p2 = temp;
+ }
+ else if (*Otemp == 'i')
+ {
+ p1++;
+ p2++;
+ }
+ else if (*Otemp == '%')
+ putchar(*Otemp);
+ else if (*Otemp == 'n')
+ {
+ p1 ^= 0140;
+ p2 ^= 0140;
+ }
+ else if (*Otemp == 'B')
+ {
+ p1 = (16 * (p1/10)) + (p1 % 10);
+ p2 = (16 * (p2/10)) + (p2 % 10);
+ }
+ else if (*Otemp == 'D')
+ {
+ p1 = (p1 - 2 * (p1 % 16));
+ p2 = (p2 - 2 * (p2 % 16));
+ }
+ }
+ else
+ putchar (*Otemp);
+ Otemp++;
+ }
+ if (delay != 0)
+ {
+ chars = delay * chars_per_millisecond;
+ delay = chars;
+ if ((chars - delay) > 0.0)
+ delay++;
+ for (; delay > 0; delay--)
+ putchar(*String_table[pc__]);
+ }
+ fflush(stdout);
+}
+
+#else
+
+ char *Otemp; /* temporary string pointer to parse output */
+ float chars;
+ int p[10];
+ int variable[27];
+
+int
+Operation(Temp_Stack, place) /* handle conditional operations */
+int Temp_Stack[];
+int place;
+{
+ int temp;
+
+ if (*Otemp == 'd')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ printf("%d", temp);
+ }
+ else if (!strncmp(Otemp, "2d", 2))
+ {
+ temp = Temp_Stack[--place];
+ printf("%2d", temp);
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "3d", 2))
+ {
+ temp = Temp_Stack[--place];
+ printf("%0d", temp);
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "02d", 3))
+ {
+ temp = Temp_Stack[--place];
+ printf("%02d", temp);
+ Otemp++;
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "03d", 3))
+ {
+ temp = Temp_Stack[--place];
+ printf("%03d", temp);
+ Otemp++;
+ Otemp++;
+ Otemp++;
+ }
+ else if (*Otemp == '+')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp += Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '-')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp -= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '*')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp *= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '/')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp /= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == 'm')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp %= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '&')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp &= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '|')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp |= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '^')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp ^= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '=')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = (temp == Temp_Stack[--place]);
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '>')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = temp > Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '<')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = temp < Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == 'c')
+ {
+ Otemp++;
+ putchar(Temp_Stack[--place]);
+ }
+ else if (*Otemp == 'i')
+ {
+ Otemp++;
+ p[1]++;
+ p[2]++;
+ }
+ else if (*Otemp == '%')
+ {
+ putchar(*Otemp);
+ Otemp++;
+ }
+ else if (*Otemp == '!')
+ {
+ temp = ! Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ Otemp++;
+ }
+ else if (*Otemp == '~')
+ {
+ temp = ~Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ Otemp++;
+ }
+ else if (*Otemp == 'p')
+ {
+ Otemp++;
+ Temp_Stack[place++] = p[*Otemp - '0'];
+ Otemp++;
+ }
+ else if (*Otemp == 'P')
+ {
+ Otemp++;
+ Temp_Stack[place++] = variable[*Otemp - 'a'];
+ Otemp++;
+ }
+ else if (*Otemp == 'g')
+ {
+ Otemp++;
+ variable[*Otemp - 'a'] = Temp_Stack[--place];
+ Otemp++;
+ }
+ else if (*Otemp == '\'')
+ {
+ Otemp++;
+ Temp_Stack[place++] = *Otemp;
+ Otemp++;
+ Otemp++;
+ }
+ else if (*Otemp == '{')
+ {
+ Otemp++;
+ temp = atoi(Otemp);
+ Temp_Stack[place++] = temp;
+ while (*Otemp != '}')
+ Otemp++;
+ Otemp++;
+ }
+ return(place);
+}
+
+void
+Info_Out(string, p_list, place) /* interpret the output string if necessary */
+char *string;
+int p_list[];
+int place;
+{
+ char *tchar;
+ int delay;
+ int temp;
+ int Cond_FLAG;
+ int EVAL;
+ int Cond_Stack[128];
+ int Cond_place;
+ int Stack[128];
+ int Top_of_stack;
+
+ if (string == NULL)
+ return;
+
+ Cond_FLAG = FALSE;
+ Cond_place = 0;
+ Top_of_stack = 0;
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0;
+ p[4] = 0;
+ p[5] = 0;
+ p[6] = 0;
+ p[7] = 0;
+ p[8] = 0;
+ p[9] = 0;
+ if (p_list != NULL)
+ {
+ for (temp = 1; (place != 0); temp++)
+ {
+ p[temp] = p_list[--place];
+ }
+ }
+ delay = 0;
+ Otemp = string;
+ while (*Otemp != '\0')
+ {
+ if (*Otemp == '%')
+ {
+ Otemp++;
+ if ((*Otemp == '?') || (*Otemp == 't') || (*Otemp == 'e') || (*Otemp == ';'))
+ {
+ if (*Otemp == '?')
+ {
+ Otemp++;
+ Cond_FLAG = TRUE;
+ EVAL = TRUE;
+ while (EVAL)
+ {
+ /*
+ | find the end of the
+ | conditional statement
+ */
+ while ((strncmp(Otemp, "%t", 2)) && (*Otemp != '\0'))
+ {
+ /*
+ | move past '%'
+ */
+ Otemp++;
+ Cond_place = Operation(Cond_Stack, Cond_place);
+ }
+
+ /*
+ | if condition is true
+ */
+ if ((Cond_place > 0) && (Cond_Stack[Cond_place-1]))
+ {
+ /*
+ | end conditional
+ | parsing
+ */
+ EVAL = FALSE;
+ Otemp++;
+ Otemp++;
+ }
+ else /* condition is false */
+ {
+ /*
+ | find 'else' or end
+ | of if statement
+ */
+ while ((strncmp(Otemp, "%e", 2)) && (strncmp(Otemp, "%;", 2)) && (*Otemp != '\0'))
+ Otemp++;
+ /*
+ | if an 'else' found
+ */
+ if ((*Otemp != '\0') && (!strncmp(Otemp, "%e", 2)))
+ {
+ Otemp++;
+ Otemp++;
+ tchar = Otemp;
+ /*
+ | check for 'then' part
+ */
+ while ((*tchar != '\0') && (strncmp(tchar, "%t", 2)) && (strncmp(tchar, "%;", 2)))
+ tchar++;
+ /*
+ | if end of string
+ */
+ if (*tchar == '\0')
+ {
+ EVAL = FALSE;
+ Cond_FLAG = FALSE;
+ Otemp = tchar;
+ }
+ /*
+ | if end of if found,
+ | set up to parse
+ | info
+ */
+ else if (!strncmp(tchar, "%;", 2))
+ EVAL = FALSE;
+ /*
+ | otherwise, check
+ | conditional in
+ | 'else'
+ */
+ }
+ /*
+ | if end of if found,
+ | get out of if
+ | statement
+ */
+ else if ((*Otemp != '\0') && (!strncmp(Otemp, "%;", 2)))
+ {
+ EVAL = FALSE;
+ Otemp++;
+ Otemp++;
+ }
+ else /* Otemp == NULL */
+ {
+ EVAL = FALSE;
+ Cond_FLAG = FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ Otemp++;
+ Cond_FLAG = FALSE;
+ if (*Otemp != ';')
+ {
+ while ((*Otemp != '\0') && (strncmp(Otemp, "%;", 2)))
+ Otemp++;
+ if (*Otemp != '\0')
+ {
+ Otemp++;
+ Otemp++;
+ }
+ }
+ else
+ Otemp++;
+ }
+ }
+ else
+ {
+ Top_of_stack = Operation(Stack, Top_of_stack);
+ }
+ }
+ else if (!strncmp(Otemp, "$<", 2))
+ {
+ Otemp++;
+ Otemp++;
+ delay = atoi(Otemp);
+ while (*Otemp != '>')
+ Otemp++;
+ Otemp++;
+ chars = delay * chars_per_millisecond;
+ delay = chars;
+ if ((chars - delay) > 0.0)
+ delay++;
+ if (String_table[pc__] == NULL)
+ temp = 0;
+ else
+ temp = *String_table[pc__];
+ for (; delay > 0; delay--)
+ putc(temp, stdout);
+ }
+ else
+ {
+ putchar(*Otemp);
+ Otemp++;
+ }
+ }
+ fflush(stdout);
+}
+#endif
+
+void
+wmove(window, row, column) /* move cursor to indicated position in window */
+WINDOW *window;
+int row, column;
+{
+ if ((row < window->Num_lines) && (column < window->Num_cols))
+ {
+ window->LX = column;
+ window->LY = row;
+ }
+}
+
+void
+clear_line(line, column, cols)
+struct _line *line;
+int column;
+int cols;
+{
+ int j;
+
+ if (column > line->last_char)
+ {
+ for (j = line->last_char; j < column; j++)
+ {
+ line->row[j] = ' ';
+ line->attributes[j] = '\0';
+ }
+ }
+ line->last_char = column;
+ line->row[column] = '\0';
+ line->attributes[column] = '\0';
+ line->changed = TRUE;
+}
+
+void
+werase(window) /* clear the specified window */
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR = CLEAR;
+ window->scroll_up = window->scroll_down = 0;
+ for (i = 0, tmp = window->first_line; i < window->Num_lines; i++, tmp = tmp->next_screen)
+ clear_line(tmp, 0, window->Num_cols);
+}
+
+void
+wclrtoeol(window) /* erase from current cursor position to end of line */
+WINDOW *window;
+{
+ int column, row;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR = CHANGE;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ clear_line(tmp, column, window->Num_cols);
+}
+
+void
+wrefresh(window) /* flush all previous output */
+WINDOW *window;
+{
+ wnoutrefresh(window);
+#ifdef DIAG
+{
+ struct _line *temp;
+ int value;
+ fprintf(stderr, "columns=%d, lines=%d, SC=%d, SR=%d\n",window->Num_cols, window->Num_lines, window->SC, window->SR);
+ for (value = 0, temp = window->first_line; value < window->Num_lines; value++, temp = temp->next_screen)
+ {
+ if (temp->number == -1)
+ fprintf(stderr, "line moved ");
+ if (temp->scroll)
+ fprintf(stderr, "scroll_x is set: ");
+ fprintf(stderr, "lc%d=%s|\n", temp->last_char, temp->row);
+ }
+ fprintf(stderr, "+-------------------- virtual screen ----------------------------------------+\n");
+ fprintf(stderr, "columns=%d, lines=%d \n",virtual_scr->Num_cols, virtual_scr->Num_lines);
+ for (value = 0, temp = virtual_scr->first_line; value < virtual_scr->Num_lines; value++, temp = temp->next_screen)
+ {
+ if (temp->number == -1)
+ fprintf(stderr, "line moved ");
+ if (temp->scroll)
+ fprintf(stderr, "scroll_x is set: ");
+ fprintf(stderr, "lc%d=%s|\n", temp->last_char, temp->row);
+ }
+ fprintf(stderr, "columns=%d, lines=%d \n",curscr->Num_cols, curscr->Num_lines);
+ for (value = 0, temp = curscr->first_line; value < curscr->Num_lines; value++, temp = temp->next_screen)
+ fprintf(stderr, "line=%s|\n", temp->row);
+}
+#endif
+ doupdate();
+ virtual_scr->SCROLL_CLEAR = FALSE;
+ virtual_scr->scroll_down = virtual_scr->scroll_up = 0;
+ fflush(stdout);
+}
+
+void
+touchwin(window)
+WINDOW *window;
+{
+ struct _line *user_line;
+ int line_counter = 0;
+
+ for (line_counter = 0, user_line = window->first_line;
+ line_counter < window->Num_lines; line_counter++)
+ {
+ user_line->changed = TRUE;
+ }
+ window->SCROLL_CLEAR = TRUE;
+}
+
+void
+wnoutrefresh(window)
+WINDOW *window;
+{
+ struct _line *user_line;
+ struct _line *virtual_line;
+ int line_counter = 0;
+ int user_col = 0;
+ int virt_col = 0;
+
+ if (window->SR >= virtual_scr->Num_lines)
+ return;
+ user_line = window->first_line;
+ virtual_line = virtual_scr->first_line;
+ virtual_scr->SCROLL_CLEAR = window->SCROLL_CLEAR;
+ virtual_scr->LX = window->LX + window->SC;
+ virtual_scr->LY = window->LY + window->SR;
+ virtual_scr->scroll_up = window->scroll_up;
+ virtual_scr->scroll_down = window->scroll_down;
+ if ((last_window_refreshed == window) && (!window->SCROLL_CLEAR))
+ return;
+ for (line_counter = 0; line_counter < window->SR; line_counter++)
+ {
+ virtual_line = virtual_line->next_screen;
+ }
+ for (line_counter = 0; (line_counter < window->Num_lines)
+ && ((line_counter + window->SR) < virtual_scr->Num_lines);
+ line_counter++)
+ {
+ if ((last_window_refreshed != window) || (user_line->changed) || ((SCROLL | CLEAR) & window->SCROLL_CLEAR))
+ {
+ for (user_col = 0, virt_col = window->SC;
+ (virt_col < virtual_scr->Num_cols)
+ && (user_col < user_line->last_char);
+ virt_col++, user_col++)
+ {
+ virtual_line->row[virt_col] = user_line->row[user_col];
+ virtual_line->attributes[virt_col] = user_line->attributes[user_col];
+ }
+ for (user_col = user_line->last_char,
+ virt_col = window->SC + user_line->last_char;
+ (virt_col < virtual_scr->Num_cols)
+ && (user_col < window->Num_cols);
+ virt_col++, user_col++)
+ {
+ virtual_line->row[virt_col] = ' ';
+ virtual_line->attributes[virt_col] = '\0';
+ }
+ }
+ if (virtual_scr->Num_cols != window->Num_cols)
+ {
+ if (virtual_line->last_char < (user_line->last_char + window->SC))
+ {
+ if (virtual_line->row[virtual_line->last_char] == '\0')
+ virtual_line->row[virtual_line->last_char] = ' ';
+ virtual_line->last_char =
+ min(virtual_scr->Num_cols,
+ (user_line->last_char + window->SC));
+ }
+ }
+ else
+ virtual_line->last_char = user_line->last_char;
+ virtual_line->row[virtual_line->last_char] = '\0';
+ virtual_line->changed = user_line->changed;
+ virtual_line = virtual_line->next_screen;
+ user_line = user_line->next_screen;
+ }
+ window->SCROLL_CLEAR = FALSE;
+ window->scroll_up = window->scroll_down = 0;
+ last_window_refreshed = window;
+}
+
+void
+flushinp() /* flush input */
+{
+}
+
+void
+ungetch(c) /* push a character back on input */
+int c;
+{
+ if (bufp < 100)
+ in_buff[bufp++] = c;
+}
+
+#ifdef BSD_SELECT
+int
+timed_getchar()
+{
+ struct timeval tv;
+ fd_set fds;
+ int ret_val;
+ int nfds = 1;
+ char temp;
+
+ FD_ZERO(&fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000; /* half a second */
+ FD_SET(0, &fds);
+ Time_Out = FALSE; /* just in case */
+
+ ret_val = select(nfds, &fds, 0, 0, &tv);
+
+ /*
+ | if ret_val is less than zero, there was no input
+ | otherwise, get a character and return it
+ */
+
+ if (ret_val <= 0)
+ {
+ Time_Out = TRUE;
+ return(-1);
+ }
+
+ return(read(0, &temp, 1)? temp : -1);
+}
+#endif
+
+int
+wgetch(window) /* get character from specified window */
+WINDOW *window;
+{
+ int in_value;
+ char temp;
+#ifndef SYS5
+ int old_arg;
+#endif /* SYS5 */
+
+#ifdef BSD_SELECT
+ if (Noblock)
+ in_value = ((bufp > 0) ? in_buff[--bufp] : timed_getchar());
+ else
+ in_value = ((bufp > 0) ? in_buff[--bufp] : read(0, &temp, 1)? temp : -1);
+#else /* BSD_SELECT */
+#ifdef SYS5
+ in_value = ((bufp > 0) ? in_buff[--bufp] :
+ (read(0, &temp, 1)> 0) ? temp : -1);
+#else /* SYS5 */
+ if (Noblock)
+ {
+ Time_Out = FALSE;
+ old_arg = fcntl(0, F_GETFL, 0);
+ in_value = fcntl(0, F_SETFL, old_arg | FNDELAY);
+ }
+ in_value = ((bufp > 0) ? in_buff[--bufp] : read(0, &temp, 1)? temp : -1);
+ if (Noblock)
+ {
+ fcntl(0, F_SETFL, old_arg);
+ if (Time_Out)
+ in_value = -1;
+ }
+#endif /* SYS5 */
+#endif /* BSD_SELECT */
+
+ if (in_value != -1)
+ {
+ in_value &= 0xff;
+ if ((Parity) && (Num_bits < 8))
+ /* strip eighth bit if parity in use */
+ in_value &= 0177;
+ }
+ else if (interrupt_flag)
+ {
+ interrupt_flag = FALSE;
+ in_value = wgetch(window);
+ }
+
+ if ((in_value == '\033') || (in_value == '\037'))/* escape character */
+ in_value = Get_key(in_value);
+ return(in_value);
+}
+
+#ifndef BSD_SELECT
+void
+Clear(arg) /* notify that time out has occurred */
+int arg;
+{
+ Time_Out = TRUE;
+#ifdef DEBUG
+fprintf(stderr, "inside Clear()\n");
+fflush(stderr);
+#endif /* DEBUG */
+}
+#endif /* BSD_SELECT */
+
+int
+Get_key(first_char) /* try to decode key sequence */
+int first_char; /* first character of sequence */
+{
+ int in_char;
+ int Count;
+ char string[128];
+ char *Gtemp;
+ int Found;
+#ifdef SYS5
+ struct termio Gterminal;
+#else
+ struct sgttyb Gterminal;
+#endif
+ struct KEY_STACK *St_point;
+#if (!defined( BSD_SELECT)) || (!defined(SYS5))
+ int value;
+#endif /* BSD_SELECT */
+
+ Count = 0;
+ Gtemp = string;
+ string[Count++] = first_char;
+ string[Count] = '\0';
+ Time_Out = FALSE;
+#ifndef BSD_SELECT
+ signal(SIGALRM, Clear);
+ value = alarm(1);
+#endif /* BSD_SELECT */
+ Noblock = TRUE;
+#ifdef SYS5
+ Gterminal.c_cc[VTIME] = 0; /* timeout value */
+ Gterminal.c_lflag &= ~ICANON; /* disable canonical operation */
+ Gterminal.c_lflag &= ~ECHO; /* disable echo */
+#endif
+ Count = 1;
+ Found = FALSE;
+ while ((Count < Max_Key_len) && (!Time_Out) && (!Found))
+ {
+ in_char = wgetch(stdscr);
+#ifdef DEBUG
+fprintf(stderr, "back in GetKey()\n");
+fflush(stderr);
+#endif /* DEBUG */
+ if (in_char != -1)
+ {
+ string[Count++] = in_char;
+ string[Count] = '\0';
+ St_point = KEY_TOS;
+ while ((St_point != NULL) && (!Found))
+ {
+ if (!strcmp(string, St_point->element->string))
+ Found = TRUE;
+ else
+ St_point = St_point->next;
+ }
+ }
+ }
+#ifndef BSD_SELECT
+ if (!Time_Out)
+ value = alarm(0);
+#endif /* BSD_SELECT */
+#ifdef SYS5
+/* value = ioctl(0, TCSETA, &Terminal);*/
+#else
+ value = ioctl(0, TIOCSETP, &Terminal);
+/* value = fcntl(0, F_SETFL, old_arg);*/
+#endif
+ Noblock = FALSE;
+ if (Found)
+ {
+ return(St_point->element->value);
+ }
+ else
+ {
+ while (Count > 1)
+ {
+ if ((string[--Count] != -1) &&
+ ((unsigned char) (string[Count]) != 255))
+ {
+#ifdef DIAG
+fprintf(stderr, "ungetting character %d\n", string[Count]);fflush(stdout);
+#endif
+ ungetch(string[Count]);
+ }
+ }
+ return(first_char);
+ }
+}
+
+void
+waddch(window, c) /* output the character in the specified window */
+WINDOW *window;
+int c;
+{
+ int column, j;
+ int shift; /* number of spaces to shift if a tab */
+ struct _line *tmpline;
+
+#ifdef DIAG
+/*printf("starting waddch \n");fflush(stdout);*/
+#endif
+ column = window->LX;
+ if (c == '\t')
+ {
+ shift = (column + 1) % 8;
+ if (shift == 0)
+ shift++;
+ else
+ shift = 9 - shift;
+ while (shift > 0)
+ {
+ shift--;
+ waddch(window, ' ');
+ }
+ }
+ else if ((column < window->Num_cols) && (window->LY < window->Num_lines))
+ {
+ if ((c == '~') && (Booleans[hz__]))
+ c = '@';
+
+ if (( c != '\b') && (c != '\n') && (c != '\r'))
+ {
+ tmpline = window->line_array[window->LY];
+ tmpline->row[column] = c;
+ tmpline->attributes[column] = window->Attrib;
+ tmpline->changed = TRUE;
+ if (column >= tmpline->last_char)
+ {
+ if (column > tmpline->last_char)
+ for (j = tmpline->last_char; j < column; j++)
+ {
+ tmpline->row[j] = ' ';
+ tmpline->attributes[j] = '\0';
+ }
+ tmpline->row[column + 1] = '\0';
+ tmpline->attributes[column + 1] = '\0';
+ tmpline->last_char = column + 1;
+ }
+ }
+ if (c == '\n')
+ {
+ wclrtoeol(window);
+ window->LX = window->Num_cols;
+ }
+ else if (c == '\r')
+ window->LX = 0;
+ else if (c == '\b')
+ window->LX--;
+ else
+ window->LX++;
+ }
+ if (window->LX >= window->Num_cols)
+ {
+ window->LX = 0;
+ window->LY++;
+ if (window->LY >= window->Num_lines)
+ {
+ window->LY = window->Num_lines - 1;
+/* window->LY = row;
+ wmove(window, 0, 0);
+ wdeleteln(window);
+ wmove(window, row, 0);*/
+ }
+ }
+ window->SCROLL_CLEAR = CHANGE;
+}
+
+void
+winsertln(window) /* insert a blank line into the specified window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+ struct _line *tmp1;
+
+ window->scroll_down += 1;
+ window->SCROLL_CLEAR = SCROLL;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; (row < window->Num_lines) && (tmp->next_screen != NULL); row++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = NULL;
+ tmp1 = tmp;
+ clear_line(tmp1, 0, window->Num_cols);
+ tmp1->number = -1;
+ for (row = 0, tmp = window->first_line; (row < window->LY) && (tmp->next_screen != NULL); row++)
+ tmp = tmp->next_screen;
+ if ((window->LY == (window->Num_lines - 1)) && (window->Num_lines > 1))
+ {
+ tmp1->next_screen = tmp->next_screen;
+ tmp->next_screen = tmp1;
+ tmp->changed = TRUE;
+ tmp->next_screen->prev_screen = tmp;
+ }
+ else if (window->Num_lines > 1)
+ {
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp1;
+ tmp1->prev_screen = tmp->prev_screen;
+ tmp->prev_screen = tmp1;
+ tmp1->next_screen = tmp;
+ tmp->changed = TRUE;
+ tmp->scroll = DOWN;
+ }
+ if (window->LY == 0)
+ window->first_line = tmp1;
+
+ for (row = 0, tmp1 = window->first_line;
+ row < window->Num_lines; row++)
+ {
+ window->line_array[row] = tmp1;
+ tmp1 = tmp1->next_screen;
+ }
+}
+
+void
+wdeleteln(window) /* delete a line in the specified window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+ struct _line *tmpline;
+
+ if (window->Num_lines > 1)
+ {
+ window->scroll_up += 1;
+ window->SCROLL_CLEAR = SCROLL;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ if (window->LY == 0)
+ window->first_line = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ {
+ tmp->next_screen->changed = TRUE;
+ tmp->next_screen->scroll = UP;
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ }
+ tmpline = tmp;
+ clear_line(tmpline, 0, window->Num_cols);
+ tmpline->number = -1;
+ for (row = 0, tmp = window->first_line; tmp->next_screen != NULL; row++)
+ tmp = tmp->next_screen;
+ if (tmp != NULL)
+ {
+ tmp->next_screen = tmpline;
+ tmp->next_screen->prev_screen = tmp;
+ tmp->changed = TRUE;
+ tmp = tmp->next_screen;
+ }
+ else
+ tmp = tmpline;
+ tmp->next_screen = NULL;
+
+ for (row = 0, tmp = window->first_line; row < window->Num_lines; row++)
+ {
+ window->line_array[row] = tmp;
+ tmp = tmp->next_screen;
+ }
+ }
+ else
+ {
+ clear_line(window->first_line, 0, window->Num_cols);
+ }
+}
+
+void
+wclrtobot(window) /* delete from current position to end of the window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR |= CLEAR;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ clear_line(tmp, column, window->Num_cols);
+ for (row = (window->LY + 1); row < window->Num_lines; row++)
+ {
+ tmp = tmp->next_screen;
+ clear_line(tmp, 0, window->Num_cols);
+ }
+ wmove(window, row, column);
+}
+
+void
+wstandout(window) /* begin standout mode in window */
+WINDOW *window;
+{
+ if (Numbers[sg__] < 1) /* if not magic cookie glitch */
+ window->Attrib |= A_STANDOUT;
+}
+
+void
+wstandend(window) /* end standout mode in window */
+WINDOW *window;
+{
+ window->Attrib &= ~A_STANDOUT;
+}
+
+void
+waddstr(window, string) /* write 'string' in window */
+WINDOW *window;
+char *string;
+{
+ char *wstring;
+
+ for (wstring = string; *wstring != '\0'; wstring++)
+ waddch(window, *wstring);
+}
+
+void
+clearok(window, flag) /* erase screen and redraw at next refresh */
+WINDOW *window;
+int flag;
+{
+ Repaint_screen = TRUE;
+}
+
+
+void
+echo() /* turn on echoing */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag |= ECHO; /* enable echo */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags |= ECHO; /* enable echo */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+noecho() /* turn off echoing */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag &= ~ECHO; /* disable echo */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags &= ~ECHO; /* disable echo */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+raw() /* set to read characters immediately */
+{
+ int value;
+
+#ifdef SYS5
+ Intr = Terminal.c_cc[VINTR]; /* get the interrupt character */
+ Terminal.c_lflag &= ~ICANON; /* disable canonical operation */
+ Terminal.c_lflag &= ~ISIG; /* disable signal checking */
+#ifdef FLUSHO
+ Terminal.c_lflag &= ~FLUSHO;
+#endif
+#ifdef PENDIN
+ Terminal.c_lflag &= ~PENDIN;
+#endif
+#ifdef IEXTEN
+ Terminal.c_lflag &= ~IEXTEN;
+#endif
+ Terminal.c_cc[VMIN] = 1; /* minimum of one character */
+ Terminal.c_cc[VTIME] = 0; /* timeout value */
+ Terminal.c_cc[VINTR] = 0; /* eliminate interrupt */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags |= RAW; /* enable raw mode */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+noraw() /* set to normal character read mode */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag |= ICANON; /* enable canonical operation */
+ Terminal.c_lflag |= ISIG; /* enable signal checking */
+ Terminal.c_cc[VEOF] = 4; /* EOF character = 4 */
+ Terminal.c_cc[VEOL] = '\0'; /* EOL = 0 */
+ Terminal.c_cc[VINTR] = Intr; /* reset interrupt char */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags &= ~RAW; /* disable raw mode */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+/* old_arg = fcntl(0, F_GETFL, 0);
+ value = fcntl(0, F_SETFL, old_arg & ~FNDELAY);*/
+#endif
+}
+
+void
+nl()
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_iflag |= ICRNL; /* enable carriage-return to line-feed mapping */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+nonl()
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_iflag &= ~ICRNL; /* disable carriage-return to line-feed mapping */
+ Terminal.c_iflag &= ~IGNCR; /* do not ignore carriage-return */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+saveterm()
+{
+}
+
+void
+fixterm()
+{
+}
+
+void
+resetterm()
+{
+}
+
+void
+nodelay(window, flag)
+WINDOW *window;
+int flag;
+{
+}
+
+void
+idlok(window, flag)
+WINDOW *window;
+int flag;
+{
+}
+
+void
+keypad(window, flag)
+WINDOW *window;
+int flag;
+{
+ if (flag)
+ String_Out(String_table[ks__], NULL, 0);
+ else
+ String_Out(String_table[ke__], NULL, 0);
+}
+
+void
+savetty() /* save current tty stats */
+{
+ int value;
+
+#ifdef SYS5
+ value = ioctl(0, TCGETA, &Saved_tty); /* set characteristics */
+#else
+ value = ioctl(0, TIOCGETP, &Saved_tty); /* set characteristics */
+#endif
+}
+
+void
+resetty() /* restore previous tty stats */
+{
+ int value;
+
+#ifdef SYS5
+ value = ioctl(0, TCSETA, &Saved_tty); /* set characteristics */
+#else
+ value = ioctl(0, TIOCSETP, &Saved_tty); /* set characteristics */
+#endif
+}
+
+void
+endwin() /* end windows */
+{
+ keypad(stdscr, FALSE);
+ initialized = FALSE;
+ delwin(curscr);
+ delwin(virtual_scr);
+ delwin(stdscr);
+#ifndef SYS5
+{
+ int old_arg, value;
+/* old_arg = fcntl(0, F_GETFL, 0);
+ value = fcntl(0, F_SETFL, old_arg & ~FNDELAY);*/
+}
+#endif
+}
+
+void
+delwin(window) /* delete the window structure */
+WINDOW *window;
+{
+ int i;
+
+ for (i = 1; (i < window->Num_lines) && (window->first_line->next_screen != NULL); i++)
+ {
+ window->first_line = window->first_line->next_screen;
+ free(window->first_line->prev_screen->row);
+ free(window->first_line->prev_screen->attributes);
+ free(window->first_line->prev_screen);
+ }
+ if (window == last_window_refreshed)
+ last_window_refreshed = 0;
+ if (window->first_line != NULL)
+ {
+ free(window->first_line->row);
+ free(window->first_line->attributes);
+ free(window->first_line);
+ free(window);
+ }
+}
+
+#ifndef __STDC__
+void
+wprintw(va_alist)
+va_dcl
+#else /* __STDC__ */
+void
+wprintw(WINDOW *window, const char *format, ...)
+#endif /* __STDC__ */
+{
+#ifndef __STDC__
+ WINDOW *window;
+ char *format;
+ va_list ap;
+#else
+ va_list ap;
+#endif
+ int value;
+ char *fpoint;
+ char *wtemp;
+
+#ifndef __STDC__
+ va_start(ap);
+ window = va_arg(ap, WINDOW *);
+ format = va_arg(ap, char *);
+#else /* __STDC__ */
+ va_start(ap, format);
+#endif /* __STDC__ */
+
+ fpoint = (char *) format;
+ while (*fpoint != '\0')
+ {
+ if (*fpoint == '%')
+ {
+ fpoint++;
+ if (*fpoint == 'd')
+ {
+ value = va_arg(ap, int);
+ iout(window, value);
+ }
+ else if (*fpoint == 'c')
+ {
+ value = va_arg(ap, int);
+ waddch(window, value);
+ }
+ else if (*fpoint == 's')
+ {
+ wtemp = va_arg(ap, char *);
+ waddstr(window, wtemp);
+ }
+ fpoint++;
+ }
+ else if (*fpoint == '\\')
+ {
+ fpoint++;
+ if (*fpoint == 'n')
+ waddch(window, '\n');
+ else if ((*fpoint >= '0') && (*fpoint <= '9'))
+ {
+ value = 0;
+ while ((*fpoint >= '0') && (*fpoint <= '9'))
+ {
+ value = (value * 8) + (*fpoint - '0');
+ fpoint++;
+ }
+ waddch(window, value);
+ }
+ fpoint++;
+ }
+ else
+ waddch(window, *fpoint++);
+ }
+#ifdef __STDC__
+ va_end(ap);
+#endif /* __STDC__ */
+}
+
+void
+iout(window, value) /* output characters */
+WINDOW *window;
+int value;
+{
+ int i;
+
+ if ((i = value / 10) != 0)
+ iout(window, i);
+ waddch(window, ((value % 10) + '0'));
+}
+
+int
+Comp_line(line1, line2) /* compare lines */
+struct _line *line1;
+struct _line *line2;
+{
+ int count1;
+ int i;
+ char *att1, *att2;
+ char *c1, *c2;
+
+ if (line1->last_char != line2->last_char)
+ return(2);
+
+ c1 = line1->row;
+ c2 = line2->row;
+ att1 = line1->attributes;
+ att2 = line2->attributes;
+ i = 0;
+ while ((c1[i] != '\0') && (c2[i] != '\0') && (c1[i] == c2[i]) && (att1[i] == att2[i]))
+ i++;
+ count1 = i + 1;
+ if ((count1 == 1) && (c1[i] == '\0') && (c2[i] == '\0'))
+ count1 = 0; /* both lines blank */
+ else if ((c1[i] == '\0') && (c2[i] == '\0'))
+ count1 = -1; /* equal */
+ else
+ count1 = 1; /* lines unequal */
+ return(count1);
+}
+
+struct _line *
+Insert_line(row, end_row, window) /* insert line into screen */
+int row;
+int end_row;
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+ struct _line *tmp1;
+
+ for (i = 0, tmp = curscr->first_line; i < window->SR; i++)
+ tmp = tmp->next_screen;
+ if ((end_row + window->SR) == 0)
+ curscr->first_line = curscr->first_line->next_screen;
+ top_of_win = tmp;
+ /*
+ | find bottom line to delete
+ */
+ for (i = 0, tmp = top_of_win; (tmp->next_screen != NULL) && (i < end_row); i++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ tmp1 = tmp;
+ /*
+ | clear deleted line
+ */
+ clear_line(tmp, 0, window->Num_cols);
+ tmp1->number = -1;
+ for (i = 0, tmp = curscr->first_line; (tmp->next_screen != NULL) && (i < window->SR); i++)
+ tmp = tmp->next_screen;
+ top_of_win = tmp;
+ for (i = 0, tmp = top_of_win; i < row; i++)
+ tmp = tmp->next_screen;
+ if ((tmp->prev_screen != NULL) && (window->Num_lines > 0))
+ tmp->prev_screen->next_screen = tmp1;
+ tmp1->prev_screen = tmp->prev_screen;
+ tmp->prev_screen = tmp1;
+ tmp1->next_screen = tmp;
+ if ((row + window->SR) == 0)
+ curscr->first_line = tmp1;
+ if (tmp1->next_screen != NULL)
+ tmp1 = tmp1->next_screen;
+
+ if ((!String_table[cs__]) && (end_row < window->Num_lines))
+ {
+ Position(window, (window->SR + end_row), 0);
+ String_Out(String_table[dl__], NULL, 0);
+ }
+ Position(window, (window->SR + row), 0);
+ if (String_table[al__] != NULL)
+ String_Out(String_table[al__], NULL, 0);
+ else
+ String_Out(String_table[sr__], NULL, 0);
+
+ for (i = 0, top_of_win = curscr->first_line; (top_of_win->next_screen != NULL) && (i < window->SR); i++)
+ top_of_win = top_of_win->next_screen;
+ return(tmp1);
+}
+
+
+struct _line *
+Delete_line(row, end_row, window) /* delete a line on screen */
+int row;
+int end_row;
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+ struct _line *tmp1;
+ struct _line *tmp2;
+
+ i = 0;
+ tmp = curscr->first_line;
+ while (i < window->SR)
+ {
+ i++;
+ tmp = tmp->next_screen;
+ }
+ /*
+ | find line to delete
+ */
+ top_of_win = tmp;
+ if ((row + window->SR) == 0)
+ curscr->first_line = top_of_win->next_screen;
+ for (i = 0, tmp = top_of_win; i < row; i++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ tmp2 = tmp->next_screen;
+ tmp1 = tmp;
+ /*
+ | clear deleted line
+ */
+ clear_line(tmp1, 0, window->Num_cols);
+ tmp1->number = -1;
+ /*
+ | find location to insert deleted line
+ */
+ for (i = 0, tmp = curscr->first_line; (tmp->next_screen != NULL) && (i < window->SR); i++)
+ tmp = tmp->next_screen;
+ top_of_win = tmp;
+ for (i = 0, tmp = top_of_win; (i < end_row) && (tmp->next_screen != NULL); i++)
+ tmp = tmp->next_screen;
+ tmp1->next_screen = tmp;
+ tmp1->prev_screen = tmp->prev_screen;
+ if (tmp1->prev_screen != NULL)
+ tmp1->prev_screen->next_screen = tmp1;
+ tmp->prev_screen = tmp1;
+
+ Position(window, (window->SR + row), 0);
+ String_Out(String_table[dl__], NULL, 0);
+ if ((!String_table[cs__]) && (end_row < window->Num_lines))
+ {
+ Position(window, (window->SR + end_row), 0);
+ String_Out(String_table[al__], NULL, 0);
+ }
+ else if ((String_table[cs__] != NULL) && (String_table[dl__] == NULL))
+ {
+ Position(window, (window->SR + end_row), 0);
+ putchar('\n');
+ }
+
+ if (row == (window->Num_lines-1))
+ tmp2 = tmp1;
+ if ((row + window->SR) == 0)
+ curscr->first_line = top_of_win = tmp2;
+ return(tmp2);
+}
+
+void
+CLEAR_TO_EOL(window, row, column)
+WINDOW *window;
+int row, column;
+{
+ int x, y;
+ struct _line *tmp1;
+
+ for (y = 0, tmp1 = curscr->first_line; (y < (window->SR+row)) && (tmp1->next_screen != NULL); y++)
+ tmp1 = tmp1->next_screen;
+ for (x = column; x<window->Num_cols; x++)
+ {
+ tmp1->row[x] = ' ';
+ tmp1->attributes[x] = '\0';
+ }
+ tmp1->row[column] = '\0';
+ tmp1->last_char = column;
+ if (column < COLS)
+ {
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(window, row, column);
+ attribute_off();
+ }
+ if (String_table[ce__] != NULL)
+ String_Out(String_table[ce__], NULL, 0);
+ else
+ {
+ for (x = column; x < window->Num_cols; x++)
+ putchar(' ');
+ Curr_x = x;
+ }
+ }
+}
+
+int
+check_delete(window, line, offset, pointer_new, pointer_old)
+WINDOW *window;
+int line, offset;
+struct _line *pointer_new, *pointer_old;
+{
+ int end_old;
+ int end_new;
+ int k;
+ int changed;
+ char *old_lin;
+ char *new_lin;
+ char *old_att;
+ char *new_att;
+
+ changed = FALSE;
+ new_lin = pointer_new->row;
+ new_att = pointer_new->attributes;
+ old_lin = pointer_old->row;
+ old_att = pointer_old->attributes;
+ end_old = end_new = offset;
+ while (((new_lin[end_new] != old_lin[end_old]) || (new_att[end_new] != old_att[end_old])) && (old_lin[end_old] != '\0') && (new_lin[end_old] != '\0'))
+ end_old++;
+ if (old_lin[end_old] != '\0')
+ {
+ k = 0;
+ while ((old_lin[end_old+k] == new_lin[end_new+k]) && (new_att[end_new+k] == old_att[end_old+k]) && (new_lin[end_new+k] != '\0') && (old_lin[end_old+k] != '\0') && (k < 10))
+ k++;
+ if ((k > 8) || ((new_lin[end_new+k] == '\0') && (k != 0)))
+ {
+ if (new_lin[end_new+k] == '\0')
+ {
+ Position(window, line, (end_new+k));
+ CLEAR_TO_EOL(window, line, (end_new+k));
+ }
+ Position(window, line, offset);
+ for (k = offset; k < end_old; k++)
+ Char_del(old_lin, old_att, offset, window->Num_cols);
+ while ((old_lin[offset] != '\0') && (offset < COLS))
+ offset++;
+ pointer_old->last_char = offset;
+ changed = TRUE;
+ }
+ }
+ return(changed);
+}
+
+/*
+ | Check if characters were inserted in the middle of a line, and if
+ | so, insert them.
+ */
+
+int
+check_insert(window, line, offset, pointer_new, pointer_old)
+WINDOW *window;
+int line, offset;
+struct _line *pointer_new, *pointer_old;
+{
+ int changed;
+ int end_old, end_new;
+ int k;
+ int same = FALSE;
+ int old_off;
+ int insert;
+ char *old_lin;
+ char *new_lin;
+ char *old_att;
+ char *new_att;
+
+ changed = FALSE;
+ new_lin = pointer_new->row;
+ new_att = pointer_new->attributes;
+ old_lin = pointer_old->row;
+ old_att = pointer_old->attributes;
+ end_old = end_new = offset;
+ while (((new_lin[end_new] != old_lin[end_old]) || (new_att[end_new] != old_att[end_old])) && (new_lin[end_new] != '\0') && (old_lin[end_new] != '\0'))
+ end_new++;
+ if (new_lin[end_new] != '\0')
+ {
+ k = 0;
+ while ((old_lin[end_old+k] == new_lin[end_new+k]) && (old_att[end_old+k] == new_att[end_new+k]) && (new_lin[end_new+k] != '\0') && (old_lin[end_old+k] != '\0') && (k < 10))
+ k++;
+ /*
+ | check for commonality between rest of lines (are the old
+ | and new lines the same, except for a chunk in the middle?)
+ | if the rest of the lines are common, do not insert text
+ */
+ old_off = end_new;
+ while ((old_lin[old_off] != '\0') && (new_lin[old_off] != '\0') && (old_lin[old_off] == new_lin[old_off]) && (old_att[old_off] == new_att[old_off]))
+ old_off++;
+ if ((old_lin[old_off] == new_lin[old_off]) && (old_att[old_off] == new_att[old_off]))
+ same = TRUE;
+ if ((!same) && ((k > 8) || ((new_lin[end_new+k] == '\0') && (k != 0))))
+ {
+ Position(window, line, offset);
+ insert = FALSE;
+ if (String_table[ic__] == NULL)
+ {
+ String_Out(String_table[im__], NULL, 0);
+ insert = TRUE;
+ }
+ for (k = offset; k < end_new; k++)
+ {
+ if (!insert)
+ String_Out(String_table[ic__], NULL, 0);
+ Char_ins(old_lin, old_att, new_lin[k], new_att[k], k, window->Num_cols);
+ }
+ if (insert)
+ String_Out(String_table[ei__], NULL, 0);
+ while ((old_lin[offset] != '\0') && (offset < COLS))
+ offset++;
+ pointer_old->last_char = offset;
+ changed = TRUE;
+ }
+ }
+ return(changed);
+}
+
+void
+doupdate()
+{
+ WINDOW *window;
+ int similar;
+ int diff;
+ int begin_old, begin_new;
+ int end_old, end_new;
+ int count1, j;
+ int from_top, tmp_ft, offset;
+ int changed;
+ int first_time;
+ int first_same;
+ int last_same;
+ int list[10];
+ int bottom;
+
+ struct _line *curr;
+ struct _line *virt;
+ struct _line *old;
+
+ struct _line *new;
+
+ struct _line *old1, *new1;
+
+ char *cur_lin;
+ char *vrt_lin;
+ char *cur_att;
+ char *vrt_att;
+ char *att1, *att2;
+ char *c1, *c2;
+
+ char NC_chinese = FALSE; /* flag to indicate handling Chinese */
+
+ window = virtual_scr;
+
+ if ((nc_attributes & A_NC_BIG5) != 0)
+ NC_chinese = TRUE;
+
+ if (Repaint_screen)
+ {
+ if (String_table[cl__])
+ String_Out(String_table[cl__], NULL, 0);
+ else
+ {
+ from_top = 0;
+ while (from_top < LINES)
+ {
+ Position(curscr, from_top, 0);
+ if (String_table[ce__] != NULL)
+ String_Out(String_table[ce__], NULL, 0);
+ else
+ {
+ for (j = 0; j < window->Num_cols; j++)
+ putchar(' ');
+ }
+ from_top++;
+ }
+ }
+ for (from_top = 0, curr = curscr->first_line; from_top < curscr->Num_lines; from_top++, curr = curr->next_screen)
+ {
+ Position(curscr, from_top, 0);
+ for (j = 0; (curr->row[j] != '\0') && (j < curscr->Num_cols); j++)
+ {
+ Char_out(curr->row[j], curr->attributes[j], curr->row, curr->attributes, j);
+ }
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(curscr, from_top, j);
+ attribute_off();
+ }
+ }
+ Repaint_screen = FALSE;
+ }
+
+ similar = 0;
+ diff = FALSE;
+ top_of_win = curscr->first_line;
+
+ for (from_top = 0, curr = top_of_win, virt = window->first_line;
+ from_top < window->Num_lines; from_top++)
+ {
+ virtual_lines[from_top] = TRUE;
+ if ((similar = Comp_line(curr, virt)) > 0)
+ {
+ virtual_lines[from_top] = FALSE;
+ diff = TRUE;
+ }
+ curr = curr->next_screen;
+ virt = virt->next_screen;
+ }
+
+ from_top = 0;
+ virt = window->first_line;
+ curr = top_of_win;
+ similar = 0;
+ /*
+ | if the window has lines that are different, check for scrolling
+ */
+ if (diff)
+ {
+ last_same = -1;
+ changed = FALSE;
+ for (first_same = window->Num_lines;
+ (first_same > from_top) && (virtual_lines[first_same - 1]);
+ first_same--)
+ ;
+ for (last_same = 0;
+ (last_same < window->Num_lines) && (virtual_lines[last_same]== FALSE);
+ last_same++)
+ ;
+ while ((from_top < first_same) && nc_scrolling_ability)
+ /* check entire lines for diffs */
+ {
+
+ if (from_top >= last_same)
+ {
+ for (last_same = from_top;
+ (last_same < window->Num_lines) &&
+ (virtual_lines[last_same] == FALSE);
+ last_same++)
+ ;
+ }
+ if (!virtual_lines[from_top])
+ {
+ diff = TRUE;
+ /*
+ | check for lines deleted (scroll up)
+ */
+ for (tmp_ft = from_top+1, old = curr->next_screen;
+ ((window->scroll_up) && (diff) &&
+ (tmp_ft < last_same) &&
+ (!virtual_lines[tmp_ft]));
+ tmp_ft++)
+ {
+ if ((Comp_line(old, virt) == -1) && (!virtual_lines[from_top]))
+ {
+ /*
+ | Find the bottom of the
+ | area that should be
+ | scrolled.
+ */
+ for (bottom = tmp_ft, old1 = old,
+ new1 = virt, count1 = 0;
+ (bottom < window->Num_lines) &&
+ (Comp_line(old1, new1) <= 0);
+ bottom++, old1 = old1->next_screen,
+ new1 = new1->next_screen,
+ count1++)
+ ;
+ if (count1 > 3)
+ {
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = from_top;
+ list[0] = min((bottom - 1), (window->Num_lines - 1));
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ for (offset = (tmp_ft - from_top); (offset > 0); offset--)
+ {
+ old = Delete_line(from_top, min((bottom - 1), (window->Num_lines - 1)), window);
+ diff = FALSE;
+ }
+
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = 0;
+ list[0] = LINES - 1;
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ top_of_win = curscr->first_line;
+ curr = top_of_win;
+ for (offset = 0; offset < from_top; offset++)
+ curr = curr->next_screen;
+ for (offset = from_top, old=curr, new=virt;
+ offset < window->Num_lines;
+ old=old->next_screen, new=new->next_screen,
+ offset++)
+ {
+ similar = Comp_line(old, new);
+ virtual_lines[offset] = (similar > 0 ? FALSE : TRUE);
+ }
+ }
+ }
+ else
+ old = old->next_screen;
+ }
+ /*
+ | check for lines inserted (scroll down)
+ */
+ for (tmp_ft = from_top-1, old = curr->prev_screen;
+ ((window->scroll_down) && (tmp_ft >= 0) &&
+ (diff) &&
+ (!virtual_lines[tmp_ft]));
+ tmp_ft--)
+ {
+ if (Comp_line(old, virt) == -1)
+ {
+ /*
+ | Find the bottom of the
+ | area that should be
+ | scrolled.
+ */
+ for (bottom = from_top, old1 = old,
+ new1 = virt, count1 = 0;
+ (bottom < window->Num_lines) &&
+ (Comp_line(old1, new1) <= 0);
+ bottom++, old1 = old1->next_screen,
+ new1 = new1->next_screen,
+ count1++)
+ ;
+ if (count1 > 3)
+ {
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = tmp_ft;
+ list[0] = min((bottom - 1), (window->Num_lines - 1));
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ for (offset = (from_top - tmp_ft); (offset > 0); offset--)
+ {
+ old = Insert_line(tmp_ft, min((bottom - 1), (window->Num_lines -1)), window);
+ diff = FALSE;
+ }
+
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = 0;
+ list[0] = LINES - 1;
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ top_of_win = curscr->first_line;
+ curr = top_of_win;
+ for (offset = 0; offset < from_top; offset++)
+ curr = curr->next_screen;
+ for (offset = from_top, old=curr, new=virt;
+ offset < window->Num_lines;
+ old=old->next_screen, new=new->next_screen,
+ offset++)
+ {
+ similar = Comp_line(old, new);
+ virtual_lines[offset] = (similar > 0 ? FALSE : TRUE);
+ }
+ }
+ }
+ else
+ old = old->prev_screen;
+ }
+ }
+ from_top++;
+ curr = curr->next_screen;
+ virt = virt->next_screen;
+ }
+ }
+
+
+ /*
+ | Scrolling done, now need to insert, delete, or modify text
+ | within lines.
+ */
+
+ for (from_top = 0, curr = curscr->first_line; from_top < window->SR; from_top++)
+ curr = curr->next_screen;
+ top_of_win = curr;
+ for (from_top = 0, curr = top_of_win, virt = window->first_line; from_top < window->Num_lines; from_top++, curr = curr->next_screen, virt = virt->next_screen)
+ {
+
+ /*
+ | If either 'insert mode' or 'insert char' are
+ | available, enter the following 'if' statement,
+ | else, need to simply rewrite the contents of the line
+ | at the point where the contents of the line change.
+ */
+
+ if (((String_table[ic__]) || (String_table[im__])) &&
+ (String_table[dc__]) && (curr->row[0] != '\0') &&
+ (!NC_chinese))
+ {
+ j = 0;
+ first_time = TRUE;
+ vrt_lin = virt->row;
+ vrt_att = virt->attributes;
+ cur_lin = curr->row;
+ cur_att = curr->attributes;
+ while ((vrt_lin[j] != '\0') && (j < window->Num_cols))
+ {
+ if ((STAND) && (Booleans[xs__]))
+ {
+ while ((vrt_lin[j] == cur_lin[j]) && (vrt_att[j] == cur_att[j]) && (vrt_lin[j] != '\0') && (vrt_att[j]))
+ j++;
+ if ((STAND) && (!vrt_att[j]))
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ attribute_off();
+ }
+ }
+ else
+ {
+ while ((vrt_lin[j] == cur_lin[j]) && (vrt_att[j] == cur_att[j]) && (vrt_lin[j] != '\0'))
+ j++;
+ }
+ if ((vrt_att[j] != cur_att[j]) && (cur_att[j]) && (Booleans[xs__]))
+ {
+ Position(window, from_top, j);
+/* CLEAR_TO_EOL(window, from_top, j);*/
+ attribute_off();
+ attribute_off();
+ }
+ if (vrt_lin[j] != '\0')
+ {
+ begin_new = j;
+ begin_old = j;
+ end_old = j;
+ end_new = j;
+ if ((first_time) && (virt->changed))
+ {
+ if (curr->last_char <= virt->last_char)
+ changed = check_insert(window, from_top, j, virt, curr);
+ }
+ changed = check_delete(window, from_top, j, virt, curr);
+ first_time = FALSE;
+ virt->changed = FALSE;
+ if (!changed)
+ changed = check_insert(window, from_top, j, virt, curr);
+ if (((!changed) || (cur_lin[j] != vrt_lin[j]) || (cur_att[j] != vrt_att[j])) && (j < window->Num_cols))
+ {
+ if ((vrt_lin[j] == ' ') && (cur_lin[j] == '\0') && (vrt_att[j] == cur_att[j]))
+ cur_lin[j] = ' ';
+ else
+ {
+ Position(window, from_top, j);
+ Char_out(vrt_lin[j], vrt_att[j], cur_lin, cur_att, j);
+ }
+ }
+ if ((vrt_lin[j] != '\0'))
+ j++;
+ }
+ if ((STAND) && (!vrt_att[j]))
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ }
+ }
+ if ((vrt_lin[j] == '\0') && (cur_lin[j] != '\0'))
+ {
+ Position(window, from_top, j);
+ CLEAR_TO_EOL(window, from_top, j);
+ }
+ }
+ else /*if ((similar != -1) && (similar != 0))*/
+ {
+ j = 0;
+ c1 = curr->row;
+ att1 = curr->attributes;
+ c2 = virt->row;
+ att2 = virt->attributes;
+ while ((j < window->Num_cols) && (c2[j] != '\0'))
+ {
+ while ((c1[j] == c2[j]) && (att1[j] == att2[j]) && (j < window->Num_cols) && (c2[j] != '\0'))
+ j++;
+
+ /*
+ | if previous character is an eight bit
+ | char, start redraw from that character
+ */
+
+ if ((NC_chinese) && (highbitset(c1[j - 1])))
+ j--;
+ begin_old = j;
+ begin_new = j;
+ if ((j < window->Num_cols) && (c2[j] != '\0'))
+ {
+ Position(window, from_top, begin_old);
+ CLEAR_TO_EOL(window, from_top, j);
+ Position(window, from_top, begin_old);
+ for (j = begin_old; (c2[j] != '\0') && (j < window->Num_cols); j++)
+ Char_out(c2[j], att2[j], c1, att1, j);
+ }
+ }
+ if ((c2[j] == '\0') && (c1[j] != '\0'))
+ {
+ Position(window, from_top, j);
+ CLEAR_TO_EOL(window, from_top, j);
+ }
+ }
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ }
+ virt->number = from_top;
+ }
+ Position(window, window->LY, window->LX);
+}
+
+void
+Position(window, row, col) /* position the cursor for output on the screen */
+WINDOW *window;
+int row;
+int col;
+{
+ int list[10];
+ int place;
+
+ int pos_row;
+ int pos_column;
+
+ pos_row = row + window->SR;
+ pos_column = col + window->SC;
+ if ((pos_row != Curr_y) || (pos_column != Curr_x))
+ {
+ if (String_table[cm__] != NULL) /* && (row < window->Num_lines) && (column < window->Num_cols))*/
+ {
+ place = 0;
+ list[place++] = pos_column;
+ list[place++] = pos_row;
+ String_Out(String_table[cm__], list, place);
+ if ((STAND) && (!Booleans[ms__]))
+ attribute_on();
+ }
+ Curr_x = pos_column;
+ Curr_y = pos_row;
+ }
+}
+
+void
+Char_del(line, attrib, offset, maxlen) /* delete chars from line */
+char *line;
+char *attrib;
+int offset;
+int maxlen;
+{
+ int one, two;
+
+ for (one = offset, two = offset+1; (line[one] != '\0') && (one < maxlen); one++, two++)
+ {
+ line[one] = line[two];
+ attrib[one] = attrib[two];
+ }
+ String_Out(String_table[dc__], NULL, 0);
+}
+
+void
+Char_ins(line, attrib, newc, newatt, offset, maxlen) /* insert chars in line */
+char *line;
+char *attrib;
+char newc;
+char newatt;
+int offset;
+int maxlen;
+{
+ int one, two;
+
+ one = 0;
+ while ((line[one] != '\0') && (one < (maxlen - 2)))
+ one++;
+ for (two = one + 1; (two > offset); one--, two--)
+ {
+ line[two] = line[one];
+ attrib[two] = attrib[one];
+ }
+ line[offset] = newc;
+ attrib[offset] = newatt;
+ Char_out(newc, newatt, line, attrib, offset);
+}
+
+void
+attribute_on()
+{
+ if (String_table[sa__])
+ {
+ attributes_set[0] = 1;
+ String_Out(String_table[sa__], attributes_set, 1);
+ }
+ else if (String_table[so__])
+ String_Out(String_table[so__], NULL, 0);
+}
+
+void
+attribute_off()
+{
+ if (String_table[me__])
+ String_Out(String_table[me__], NULL, 0);
+ else if (String_table[sa__])
+ {
+ attributes_set[0] = 0;
+ String_Out(String_table[sa__], attributes_set, 1);
+ }
+ else if (String_table[se__])
+ String_Out(String_table[se__], NULL, 0);
+}
+
+void
+Char_out(newc, newatt, line, attrib, offset) /* output character with proper attribute */
+char newc;
+char newatt;
+char *line;
+char *attrib;
+int offset;
+{
+
+
+ if ((newatt) && (!STAND))
+ {
+ STAND = TRUE;
+ attribute_on();
+ }
+ else if ((STAND) && (!newatt))
+ {
+ STAND = FALSE;
+ attribute_off();
+ }
+
+ if ((newatt) && (STAND) && (Booleans[xs__]))
+ {
+ attribute_on();
+ }
+
+ if (!((Curr_y >= (LINES - 1)) && (Curr_x >= (COLS - 1))))
+ {
+ putchar(newc);
+ line[offset] = newc;
+ attrib[offset] = newatt;
+ }
+ Curr_x++;
+}
+
+/*
+ |
+ | The two routines that follow, nc_setattrib(), nc_clearattrib(), are
+ | hacks that notify new_curse to handle characters that have the high
+ | bit set as the first of two bytes of a multi-byte string.
+ |
+ */
+
+void
+nc_setattrib(flag)
+int flag;
+{
+ nc_attributes |= flag;
+}
+
+void
+nc_clearattrib(flag)
+int flag;
+{
+ nc_attributes &= ~flag;
+}
+
diff --git a/text_cmds/ee/new_curse.h b/text_cmds/ee/new_curse.h
new file mode 100644
index 0000000..db538bb
--- /dev/null
+++ b/text_cmds/ee/new_curse.h
@@ -0,0 +1,260 @@
+/*
+ | new_curse.h
+ |
+ | A subset of curses developed for use with ae.
+ |
+ | written by Hugh Mahon
+ |
+ | THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+ | NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+ | MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+ | IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ | FITNESS FOR A PARTICULAR PURPOSE. Neither
+ | Hewlett-Packard nor Hugh Mahon shall be liable
+ | for errors contained herein, nor for
+ | incidental or consequential damages in
+ | connection with the furnishing, performance or
+ | use of this material. Neither Hewlett-Packard
+ | nor Hugh Mahon assumes any responsibility for
+ | the use or reliability of this software or
+ | documentation. This software and
+ | documentation is totally UNSUPPORTED. There
+ | is no support contract available. Hewlett-
+ | Packard has done NO Quality Assurance on ANY
+ | of the program or documentation. You may find
+ | the quality of the materials inferior to
+ | supported materials.
+ |
+ | This software is not a product of Hewlett-Packard, Co., or any
+ | other company. No support is implied or offered with this software.
+ | You've got the source, and you're on your own.
+ |
+ | This software may be distributed under the terms of Larry Wall's
+ | Artistic license, a copy of which is included in this distribution.
+ |
+ | This notice must be included with this software and any derivatives.
+ |
+ | Copyright (c) 1986, 1987, 1988, 1991, 1995 Hugh Mahon
+ | All are rights reserved.
+ |
+ */
+
+#include <stdio.h>
+
+#ifdef SYS5
+#include <termio.h>
+#else
+#include <sgtty.h>
+#include <fcntl.h>
+#endif
+
+#define KEY_BREAK 0401
+#define KEY_DOWN 0402
+#define KEY_UP 0403
+#define KEY_LEFT 0404
+#define KEY_RIGHT 0405
+#define KEY_HOME 0406
+#define KEY_BACKSPACE 0407
+#define KEY_F0 0410
+#define KEY_F(n) (KEY_F0+(n))
+#define KEY_DL 0510
+#define KEY_IL 0511
+#define KEY_DC 0512
+#define KEY_IC 0513
+#define KEY_EIC 0514
+#define KEY_CLEAR 0515
+#define KEY_EOS 0516
+#define KEY_EOL 0517
+#define KEY_SF 0520
+#define KEY_SR 0521
+#define KEY_NPAGE 0522
+#define KEY_PPAGE 0523
+#define KEY_STAB 0524
+#define KEY_CTAB 0525
+#define KEY_CATAB 0526
+#define KEY_ENTER 0527
+#define KEY_SRESET 0530
+#define KEY_RESET 0531
+#define KEY_PRINT 0532
+#define KEY_LL 0533
+#define KEY_A1 0534
+#define KEY_A3 0535
+#define KEY_B2 0536
+#define KEY_C1 0537
+#define KEY_C3 0540
+#define KEY_BTAB 0541
+#define KEY_BEG 0542
+#define KEY_CANCEL 0543
+#define KEY_CLOSE 0544
+#define KEY_COMMAND 0545
+#define KEY_COPY 0546
+#define KEY_CREATE 0547
+#define KEY_END 0550
+#define KEY_EXIT 0551
+#define KEY_FIND 0552
+#define KEY_HELP 0553
+#define KEY_MARK 0554
+#define KEY_MESSAGE 0555
+#define KEY_MOVE 0556
+#define KEY_NEXT 0557
+#define KEY_OPEN 0560
+#define KEY_OPTIONS 0561
+#define KEY_PREVIOUS 0562
+#define KEY_REDO 0563
+#define KEY_REFERENCE 0564
+#define KEY_REFRESH 0565
+#define KEY_REPLACE 0566
+#define KEY_RESTART 0567
+#define KEY_RESUME 0570
+#define KEY_SAVE 0571
+#define KEY_SBEG 0572
+#define KEY_SCANCEL 0573
+#define KEY_SCOMMAND 0574
+#define KEY_SCOPY 0575
+#define KEY_SCREATE 0576
+#define KEY_SDC 0577
+#define KEY_SDL 0600
+#define KEY_SELECT 0601
+#define KEY_SEND 0602
+#define KEY_SEOL 0603
+#define KEY_SEXIT 0604
+#define KEY_SFIND 0605
+#define KEY_SHELP 0606
+#define KEY_SHOME 0607
+#define KEY_SIC 0610
+#define KEY_SLEFT 0611
+#define KEY_SMESSAGE 0612
+#define KEY_SMOVE 0613
+#define KEY_SNEXT 0614
+#define KEY_SOPTIONS 0615
+#define KEY_SPREVIOUS 0616
+#define KEY_SPRINT 0617
+#define KEY_SREDO 0620
+#define KEY_SREPLACE 0621
+#define KEY_SRIGHT 0622
+#define KEY_SRSUME 0623
+#define KEY_SSAVE 0624
+#define KEY_SSUSPEND 0625
+#define KEY_SUNDO 0626
+#define KEY_SUSPEND 0627
+#define KEY_UNDO 0630
+
+#define TRUE 1
+#define FALSE 0
+
+#define A_STANDOUT 0001 /* standout mode */
+#define A_NC_BIG5 0x0100 /* Handle Chinese Big5 characters */
+#define SCROLL 1 /* text has been scrolled */
+#define CLEAR 2 /* window has been cleared */
+#define CHANGE 3 /* window has been changed */
+#define UP 1 /* direction of scroll */
+#define DOWN 2
+
+struct _line {
+ struct _line *next_screen;
+ struct _line *prev_screen;
+ char *row;
+ char *attributes;
+ int last_char;
+ int changed;
+ int scroll;
+ int number;
+ };
+
+struct _line *top_of_win;
+
+typedef struct WIND {
+ int SR; /* starting row */
+ int SC; /* starting column */
+ int LC; /* last column */
+ int LX; /* last cursor column position */
+ int LY; /* last cursor row position */
+ int Attrib; /* attributes active in window */
+ int Num_lines; /* number of lines */
+ int Num_cols; /* number of columns */
+ int scroll_up; /* number of lines moved */
+ int scroll_down;
+ int SCROLL_CLEAR; /* indicates that window has been scrolled or cleared */
+ struct _line *first_line;
+ struct _line **line_array;
+ } WINDOW;
+
+extern WINDOW *curscr;
+extern WINDOW *stdscr;
+
+extern int LINES, COLS;
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif
+
+extern void copy_window P_((WINDOW *origin, WINDOW *destination));
+extern void reinitscr P_((int));
+extern void initscr P_((void));
+extern int Get_int P_((void));
+extern int INFO_PARSE P_((void));
+extern int AtoI P_((void));
+extern void Key_Get P_((void));
+extern void keys_vt100 P_((void));
+extern struct _line *Screenalloc P_((int columns));
+extern WINDOW *newwin P_((int lines, int cols, int start_l, int start_c));
+extern int Operation P_((int Temp_Stack[], int place));
+extern void Info_Out P_((char *string, int p_list[], int place));
+extern void wmove P_((WINDOW *window, int row, int column));
+extern void clear_line P_((struct _line *line, int column, int cols));
+extern void werase P_((WINDOW *window));
+extern void wclrtoeol P_((WINDOW *window));
+extern void wrefresh P_((WINDOW *window));
+extern void touchwin P_((WINDOW *window));
+extern void wnoutrefresh P_((WINDOW *window));
+extern void flushinp P_((void));
+extern void ungetch P_((int c));
+extern int wgetch P_((WINDOW *window));
+extern void Clear P_((int));
+extern int Get_key P_((int first_char));
+extern void waddch P_((WINDOW *window, int c));
+extern void winsertln P_((WINDOW *window));
+extern void wdeleteln P_((WINDOW *window));
+extern void wclrtobot P_((WINDOW *window));
+extern void wstandout P_((WINDOW *window));
+extern void wstandend P_((WINDOW *window));
+extern void waddstr P_((WINDOW *window, char *string));
+extern void clearok P_((WINDOW *window, int flag));
+extern void echo P_((void));
+extern void noecho P_((void));
+extern void raw P_((void));
+extern void noraw P_((void));
+extern void nl P_((void));
+extern void nonl P_((void));
+extern void saveterm P_((void));
+extern void fixterm P_((void));
+extern void resetterm P_((void));
+extern void nodelay P_((WINDOW *window, int flag));
+extern void idlok P_((WINDOW *window, int flag));
+extern void keypad P_((WINDOW *window, int flag));
+extern void savetty P_((void));
+extern void resetty P_((void));
+extern void endwin P_((void));
+extern void delwin P_((WINDOW *window));
+extern void wprintw P_((WINDOW *window, const char* format, ...));
+extern void iout P_((WINDOW *window, int value));
+extern int Comp_line P_((struct _line *line1, struct _line *line2));
+extern struct _line *Insert_line P_((int row, int end_row, WINDOW *window));
+extern struct _line *Delete_line P_((int row, int end_row, WINDOW *window));
+extern void CLEAR_TO_EOL P_((WINDOW *window, int row, int column));
+extern int check_delete P_((WINDOW *window, int line, int offset, struct _line *pointer_new, struct _line *pointer_old));
+extern int check_insert P_((WINDOW *window, int line, int offset, struct _line *pointer_new, struct _line *pointer_old));
+extern void doupdate P_((void));
+extern void Position P_((WINDOW *window, int row, int col));
+extern void Char_del P_((char *line, char *attrib, int offset, int maxlen));
+extern void Char_ins P_((char *line, char *attrib, int newc, int newatt, int offset, int maxlen));
+extern void attribute_on P_((void));
+extern void attribute_off P_((void));
+extern void Char_out P_((int newc, int newatt, char *line, char *attrib, int offset));
+
+extern void nc_setattrib P_((int));
+extern void nc_clearattrib P_((int));
+#undef P_
+
diff --git a/text_cmds/expand/expand.1 b/text_cmds/expand/expand.1
new file mode 100644
index 0000000..4fb5b07
--- /dev/null
+++ b/text_cmds/expand/expand.1
@@ -0,0 +1,118 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)expand.1 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD: src/usr.bin/expand/expand.1,v 1.15 2006/10/13 16:22:24 ru Exp $
+.\"
+.Dd October 13, 2006
+.Dt EXPAND 1
+.Os
+.Sh NAME
+.Nm expand ,
+.Nm unexpand
+.Nd expand tabs to spaces, and vice versa
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl t
+.Sm off
+.Ar tab1 , tab2 , ... , tabn
+.Sm on
+.Oc
+.Op Ar
+.Nm unexpand
+.Oo
+.Fl a | t
+.Sm off
+.Ar tab1 , tab2 , ... , tabn
+.Sm on
+.Oc
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility processes the named files or the standard input writing
+the standard output with tabs changed into blanks.
+Backspace characters are preserved into the output and decrement
+the column count for tab calculations.
+The
+.Nm
+utility is useful for pre-processing character files
+(before sorting, looking at specific columns, etc.) that
+contain tabs.
+.Pp
+The
+.Nm unexpand
+utility puts tabs back into the data from the standard input or the named
+files and writes the result on the standard output.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+.Nm ( unexpand
+only.)
+By default, only leading blanks and tabs
+are reconverted to maximal strings of tabs.
+If the
+.Fl a
+option is given, then tabs are inserted whenever they would compress the
+resultant file by replacing two or more characters.
+.It Fl t Sm Ar tab1 , tab2 , ... , tabn Sm
+Set tab stops at column positions
+.Ar tab1 , tab2 , ... , tabn .
+If only a single number is given, tab stops are set that number of
+column positions apart instead of the default number of 8.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+and
+.Nm unexpand
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std expand unexpand
+.Sh STANDARDS
+The
+.Nm
+and
+.Nm unexpand
+utilities conform to
+.St -p1003.1-2001 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/text_cmds/expand/expand.c b/text_cmds/expand/expand.c
new file mode 100644
index 0000000..73a3ee0
--- /dev/null
+++ b/text_cmds/expand/expand.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1980, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/expand/expand.c,v 1.15 2004/06/24 13:42:26 tjr Exp $");
+
+#include <ctype.h>
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+/*
+ * expand - expand tabs to equivalent spaces
+ */
+int nstops;
+int tabstops[100];
+
+static void getstops(char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ const char *curfile;
+ wint_t wc;
+ int c, column;
+ int n;
+ int rval;
+ int width;
+
+ setlocale(LC_CTYPE, "");
+
+ /* handle obsolete syntax */
+ while (argc > 1 && argv[1] && argv[1][0] == '-' &&
+ isdigit((unsigned char)argv[1][1])) {
+ getstops(&argv[1][1]);
+ argc--; argv++;
+ }
+
+ while ((c = getopt (argc, argv, "t:")) != -1) {
+ switch (c) {
+ case 't':
+ getstops(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ rval = 0;
+ do {
+ if (argc > 0 && *argv) {
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ warn("%s", argv[0]);
+ rval = 1;
+ argc--, argv++;
+ continue;
+ }
+ curfile = argv[0];
+ argc--, argv++;
+ } else
+ curfile = "stdin";
+ column = 0;
+ while ((wc = getwchar()) != WEOF) {
+ switch (wc) {
+ case '\t':
+ if (nstops == 0) {
+ do {
+ putwchar(' ');
+ column++;
+ } while (column & 07);
+ continue;
+ }
+ if (nstops == 1) {
+ do {
+ putwchar(' ');
+ column++;
+ } while (((column - 1) % tabstops[0]) != (tabstops[0] - 1));
+ continue;
+ }
+ for (n = 0; n < nstops; n++)
+ if (tabstops[n] > column)
+ break;
+ if (n == nstops) {
+ putwchar(' ');
+ column++;
+ continue;
+ }
+ while (column < tabstops[n]) {
+ putwchar(' ');
+ column++;
+ }
+ continue;
+
+ case '\b':
+ if (column)
+ column--;
+ putwchar('\b');
+ continue;
+
+ default:
+ putwchar(wc);
+ if ((width = wcwidth(wc)) > 0)
+ column += width;
+ continue;
+
+ case '\n':
+ putwchar(wc);
+ column = 0;
+ continue;
+ }
+ }
+ if (ferror(stdin)) {
+ warn("%s", curfile);
+ rval = 1;
+ }
+ } while (argc > 0);
+ exit(rval);
+}
+
+static void
+getstops(char *cp)
+{
+ int i;
+
+ nstops = 0;
+ for (;;) {
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + *cp++ - '0';
+ if (i <= 0)
+ errx(1, "bad tab stop spec");
+ if (nstops > 0 && i <= tabstops[nstops-1])
+ errx(1, "bad tab stop spec");
+ if (nstops == sizeof(tabstops) / sizeof(*tabstops))
+ errx(1, "too many tab stops");
+ tabstops[nstops++] = i;
+ if (*cp == 0)
+ break;
+ if (*cp != ',' && !isblank((unsigned char)*cp))
+ errx(1, "bad tab stop spec");
+ cp++;
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf (stderr, "usage: expand [-t tablist] [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/expand/xcodescripts/link-man-pages.sh b/text_cmds/expand/xcodescripts/link-man-pages.sh
new file mode 100644
index 0000000..1a3d985
--- /dev/null
+++ b/text_cmds/expand/xcodescripts/link-man-pages.sh
@@ -0,0 +1,3 @@
+set -e -x
+ln -f "$DSTROOT"/usr/share/man/man1/expand.1 \
+ "$DSTROOT"/usr/share/man/man1/unexpand.1
diff --git a/text_cmds/fmt/fmt.1 b/text_cmds/fmt/fmt.1
new file mode 100644
index 0000000..6480c82
--- /dev/null
+++ b/text_cmds/fmt/fmt.1
@@ -0,0 +1,196 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fmt.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/fmt/fmt.1,v 1.14 2004/08/02 11:12:13 tjr Exp $
+.\"
+.\" Modified by Gareth McCaughan to describe the new version of `fmt'
+.\" rather than the old one.
+.Dd August 2, 2004
+.Dt FMT 1
+.Os
+.Sh NAME
+.Nm fmt
+.Nd simple text formatter
+.Sh SYNOPSIS
+.Nm fmt
+.Op Fl cmnps
+.Op Fl d Ar chars
+.Op Fl l Ar num
+.Op Fl t Ar num
+.Op Ar goal Oo Ar maximum Oc | Fl Ns Ar width | Fl w Ar width
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is a simple text formatter which reads the concatenation of input
+files (or standard input if none are given) and produces on standard
+output a version of its input with lines as close to the
+.Ar goal
+length
+as possible without exceeding the
+.Ar maximum .
+The
+.Ar goal
+length defaults
+to 65 and the
+.Ar maximum
+to 10 more than the
+.Ar goal
+length.
+Alternatively, a single
+.Ar width
+parameter can be specified either by prepending a hyphen to it or by using
+.Fl w .
+For example,
+.Dq Li fmt -w 72 ,
+.Dq Li fmt -72 ,
+and
+.Dq Li fmt 72 72
+all produce identical output.
+The spacing at the beginning of the input lines is preserved in the output,
+as are blank lines and interword spacing.
+Lines are joined or split only at white space; that is, words are never
+joined or hyphenated.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Center the text, line by line.
+In this case, most of the other
+options are ignored; no splitting or joining of lines is done.
+.It Fl m
+Try to format mail header lines contained in the input sensibly.
+.It Fl n
+Format lines beginning with a
+.Ql \&.
+(dot) character.
+Normally,
+.Nm
+does not fill these lines, for compatibility with
+.Xr nroff 1 .
+.It Fl p
+Allow indented paragraphs.
+Without the
+.Fl p
+flag, any change in the amount of whitespace at the start of a line
+results in a new paragraph being begun.
+.It Fl s
+Collapse whitespace inside lines, so that multiple whitespace
+characters are turned into a single space.
+(Or, at the end of a
+sentence, a double space.)
+.It Fl d Ar chars
+Treat the
+.Ar chars
+(and no others) as sentence-ending characters.
+By default the
+sentence-ending characters are full stop
+.Pq Ql \&. ,
+question mark
+.Pq Ql \&?
+and exclamation mark
+.Pq Ql \&! .
+Remember that some characters may need to be
+escaped to protect them from your shell.
+.It Fl l Ar number
+Replace multiple spaces with tabs at the start of each output
+line, if possible.
+Each
+.Ar number
+spaces will be replaced with one tab.
+The default is 8.
+If
+.Ar number
+is 0, spaces are preserved.
+.It Fl t Ar number
+Assume that the input files' tabs assume
+.Ar number
+spaces per tab stop.
+The default is 8.
+.El
+.Pp
+The
+.Nm
+utility
+is meant to format mail messages prior to sending, but may also be useful
+for other simple tasks.
+For instance,
+within visual mode of the
+.Xr ex 1
+editor (e.g.,
+.Xr vi 1 )
+the command
+.Pp
+.Dl \&!}fmt
+.Pp
+will reformat a paragraph,
+evening the lines.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh SEE ALSO
+.Xr fold 1 ,
+.Xr mail 1 ,
+.Xr nroff 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3 .
+.Pp
+The version described herein is a complete rewrite and appeared in
+.Fx 4.4 .
+.Sh AUTHORS
+.An Kurt Shoens
+.An Liz Allen
+(added
+.Ar goal
+length concept)
+.An Gareth McCaughan
+.Sh BUGS
+The program was designed to be simple and fast \- for more complex
+operations, the standard text processors are likely to be more appropriate.
+.Pp
+When the first line of an indented paragraph is very long (more than
+about twice the goal length), the indentation in the output can be
+wrong.
+.Pp
+The
+.Nm
+utility is not infallible in guessing what lines are mail headers and what
+lines are not.
diff --git a/text_cmds/fmt/fmt.c b/text_cmds/fmt/fmt.c
new file mode 100644
index 0000000..853695f
--- /dev/null
+++ b/text_cmds/fmt/fmt.c
@@ -0,0 +1,668 @@
+/* $OpenBSD: fmt.c,v 1.16 2000/06/25 15:35:42 pjanzen Exp $ */
+
+/* Sensible version of fmt
+ *
+ * Syntax: fmt [ options ] [ goal [ max ] ] [ filename ... ]
+ *
+ * Since the documentation for the original fmt is so poor, here
+ * is an accurate description of what this one does. It's usually
+ * the same. The *mechanism* used may differ from that suggested
+ * here. Note that we are *not* entirely compatible with fmt,
+ * because fmt gets so many things wrong.
+ *
+ * 1. Tabs are expanded, assuming 8-space tab stops.
+ * If the `-t <n>' option is given, we assume <n>-space
+ * tab stops instead.
+ * Trailing blanks are removed from all lines.
+ * x\b == nothing, for any x other than \b.
+ * Other control characters are simply stripped. This
+ * includes \r.
+ * 2. Each line is split into leading whitespace and
+ * everything else. Maximal consecutive sequences of
+ * lines with the same leading whitespace are considered
+ * to form paragraphs, except that a blank line is always
+ * a paragraph to itself.
+ * If the `-p' option is given then the first line of a
+ * paragraph is permitted to have indentation different
+ * from that of the other lines.
+ * If the `-m' option is given then a line that looks
+ * like a mail message header, if it is not immediately
+ * preceded by a non-blank non-message-header line, is
+ * taken to start a new paragraph, which also contains
+ * any subsequent lines with non-empty leading whitespace.
+ * Unless the `-n' option is given, lines beginning with
+ * a . (dot) are not formatted.
+ * 3. The "everything else" is split into words; a word
+ * includes its trailing whitespace, and a word at the
+ * end of a line is deemed to be followed by a single
+ * space, or two spaces if it ends with a sentence-end
+ * character. (See the `-d' option for how to change that.)
+ * If the `-s' option has been given, then a word's trailing
+ * whitespace is replaced by what it would have had if it
+ * had occurred at end of line.
+ * 4. Each paragraph is sent to standard output as follows.
+ * We output the leading whitespace, and then enough words
+ * to make the line length as near as possible to the goal
+ * without exceeding the maximum. (If a single word would
+ * exceed the maximum, we output that anyway.) Of course
+ * the trailing whitespace of the last word is ignored.
+ * We then emit a newline and start again if there are any
+ * words left.
+ * Note that for a blank line this translates as "We emit
+ * a newline".
+ * If the `-l <n>' option is given, then leading whitespace
+ * is modified slightly: <n> spaces are replaced by a tab.
+ * Indented paragraphs (see above under `-p') make matters
+ * more complicated than this suggests. Actually every paragraph
+ * has two `leading whitespace' values; the value for the first
+ * line, and the value for the most recent line. (While processing
+ * the first line, the two are equal. When `-p' has not been
+ * given, they are always equal.) The leading whitespace
+ * actually output is that of the first line (for the first
+ * line of *output*) or that of the most recent line (for
+ * all other lines of output).
+ * When `-m' has been given, message header paragraphs are
+ * taken as having first-leading-whitespace empty and
+ * subsequent-leading-whitespace two spaces.
+ *
+ * Multiple input files are formatted one at a time, so that a file
+ * never ends in the middle of a line.
+ *
+ * There's an alternative mode of operation, invoked by giving
+ * the `-c' option. In that case we just center every line,
+ * and most of the other options are ignored. This should
+ * really be in a separate program, but we must stay compatible
+ * with old `fmt'.
+ *
+ * QUERY: Should `-m' also try to do the right thing with quoted text?
+ * QUERY: `-b' to treat backslashed whitespace as old `fmt' does?
+ * QUERY: Option meaning `never join lines'?
+ * QUERY: Option meaning `split in mid-word to avoid overlong lines'?
+ * (Those last two might not be useful, since we have `fold'.)
+ *
+ * Differences from old `fmt':
+ *
+ * - We have many more options. Options that aren't understood
+ * generate a lengthy usage message, rather than being
+ * treated as filenames.
+ * - Even with `-m', our handling of message headers is
+ * significantly different. (And much better.)
+ * - We don't treat `\ ' as non-word-breaking.
+ * - Downward changes of indentation start new paragraphs
+ * for us, as well as upward. (I think old `fmt' behaves
+ * in the way it does in order to allow indented paragraphs,
+ * but this is a broken way of making indented paragraphs
+ * behave right.)
+ * - Given the choice of going over or under |goal_length|
+ * by the same amount, we go over; old `fmt' goes under.
+ * - We treat `?' as ending a sentence, and not `:'. Old `fmt'
+ * does the reverse.
+ * - We return approved return codes. Old `fmt' returns
+ * 1 for some errors, and *the number of unopenable files*
+ * when that was all that went wrong.
+ * - We have fewer crashes and more helpful error messages.
+ * - We don't turn spaces into tabs at starts of lines unless
+ * specifically requested.
+ * - New `fmt' is somewhat smaller and slightly faster than
+ * old `fmt'.
+ *
+ * Bugs:
+ *
+ * None known. There probably are some, though.
+ *
+ * Portability:
+ *
+ * I believe this code to be pretty portable. It does require
+ * that you have `getopt'. If you need to include "getopt.h"
+ * for this (e.g., if your system didn't come with `getopt'
+ * and you installed it yourself) then you should arrange for
+ * NEED_getopt_h to be #defined.
+ *
+ * Everything here should work OK even on nasty 16-bit
+ * machines and nice 64-bit ones. However, it's only really
+ * been tested on my FreeBSD machine. Your mileage may vary.
+ */
+
+/* Copyright (c) 1997 Gareth McCaughan. All rights reserved.
+ *
+ * Redistribution and use of this code, in source or binary forms,
+ * with or without modification, are permitted subject to the following
+ * conditions:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - If you distribute modified source code it must also include
+ * a notice saying that it has been modified, and giving a brief
+ * description of what changes have been made.
+ *
+ * Disclaimer: I am not responsible for the results of using this code.
+ * If it formats your hard disc, sends obscene messages to
+ * your boss and kills your children then that's your problem
+ * not mine. I give absolutely no warranty of any sort as to
+ * what the program will do, and absolutely refuse to be held
+ * liable for any consequences of your using it.
+ * Thank you. Have a nice day.
+ */
+
+/* RCS change log:
+ * Revision 1.5 1998/03/02 18:02:21 gjm11
+ * Minor changes for portability.
+ *
+ * Revision 1.4 1997/10/01 11:51:28 gjm11
+ * Repair broken indented-paragraph handling.
+ * Add mail message header stuff.
+ * Improve comments and layout.
+ * Make usable with non-BSD systems.
+ * Add revision display to usage message.
+ *
+ * Revision 1.3 1997/09/30 16:24:47 gjm11
+ * Add copyright notice, rcsid string and log message.
+ *
+ * Revision 1.2 1997/09/30 16:13:39 gjm11
+ * Add options: -d <chars>, -l <width>, -p, -s, -t <width>, -h .
+ * Parse options with `getopt'. Clean up code generally.
+ * Make comments more accurate.
+ *
+ * Revision 1.1 1997/09/30 11:29:57 gjm11
+ * Initial revision
+ */
+
+#ifndef lint
+static const char copyright[] =
+ "Copyright (c) 1997 Gareth McCaughan. All rights reserved.\n";
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/fmt/fmt.c,v 1.22 2004/08/02 11:10:20 tjr Exp $");
+
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+/* Something that, we hope, will never be a genuine line length,
+ * indentation etc.
+ */
+#define SILLY ((size_t)-1)
+
+/* I used to use |strtoul| for this, but (1) not all systems have it
+ * and (2) it's probably better to use |strtol| to detect negative
+ * numbers better.
+ * If |fussyp==0| then we don't complain about non-numbers
+ * (returning 0 instead), but we do complain about bad numbers.
+ */
+static size_t
+get_positive(const char *s, const char *err_mess, int fussyP) {
+ char *t;
+ long result = strtol(s,&t,0);
+ if (*t) { if (fussyP) goto Lose; else return 0; }
+ if (result<=0) { Lose: errx(EX_USAGE, "%s", err_mess); }
+ return (size_t) result;
+}
+
+static size_t
+get_nonnegative(const char *s, const char *err_mess, int fussyP) {
+ char *t;
+ long result = strtol(s,&t,0);
+ if (*t) { if (fussyP) goto Lose; else return 0; }
+ if (result<0) { Lose: errx(EX_USAGE, "%s", err_mess); }
+ return (size_t) result;
+}
+
+/* Global variables */
+
+static int centerP=0; /* Try to center lines? */
+static size_t goal_length=0; /* Target length for output lines */
+static size_t max_length=0; /* Maximum length for output lines */
+static int coalesce_spaces_P=0; /* Coalesce multiple whitespace -> ' ' ? */
+static int allow_indented_paragraphs=0; /* Can first line have diff. ind.? */
+static int tab_width=8; /* Number of spaces per tab stop */
+static size_t output_tab_width=8; /* Ditto, when squashing leading spaces */
+static const wchar_t *sentence_enders=L".?!"; /* Double-space after these */
+static int grok_mail_headers=0; /* treat embedded mail headers magically? */
+static int format_troff=0; /* Format troff? */
+
+static int n_errors=0; /* Number of failed files. Return on exit. */
+static wchar_t *output_buffer=0; /* Output line will be built here */
+static size_t x; /* Horizontal position in output line */
+static size_t x0; /* Ditto, ignoring leading whitespace */
+static size_t output_buffer_length = 0;
+static size_t pending_spaces; /* Spaces to add before next word */
+static int output_in_paragraph=0; /* Any of current para written out yet? */
+
+/* Prototypes */
+
+static void process_named_file (const char *);
+static void process_stream (FILE *, const char *);
+static size_t indent_length (const wchar_t *, size_t);
+static int might_be_header (const wchar_t *);
+static void new_paragraph (size_t, size_t);
+static void output_word (size_t, size_t, const wchar_t *, size_t,
+ size_t);
+static void output_indent (size_t);
+static void center_stream (FILE *, const char *);
+static wchar_t * get_line (FILE *, size_t *);
+static void * xrealloc (void *, size_t);
+
+#define XMALLOC(x) xrealloc(0,x)
+
+/* Here is perhaps the right place to mention that this code is
+ * all in top-down order. Hence, |main| comes first.
+ */
+int
+main(int argc, char *argv[]) {
+ int ch; /* used for |getopt| processing */
+ wchar_t *tmp;
+ size_t len;
+ const char *src;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ /* 1. Grok parameters. */
+
+ while ((ch = getopt(argc, argv, "0123456789cd:hl:mnpst:w:")) != -1)
+ switch(ch) {
+ case 'c':
+ centerP = 1;
+ format_troff = 1;
+ continue;
+ case 'd':
+ src = optarg;
+ len = mbsrtowcs(NULL, &src, 0, NULL);
+ if (len == (size_t)-1)
+ err(EX_USAGE, "bad sentence-ending character set");
+ tmp = XMALLOC((len + 1) * sizeof(wchar_t));
+ mbsrtowcs(tmp, &src, len + 1, NULL);
+ sentence_enders = tmp;
+ continue;
+ case 'l':
+ output_tab_width
+ = get_nonnegative(optarg, "output tab width must be non-negative", 1);
+ continue;
+ case 'm':
+ grok_mail_headers = 1;
+ continue;
+ case 'n':
+ format_troff = 1;
+ continue;
+ case 'p':
+ allow_indented_paragraphs = 1;
+ continue;
+ case 's':
+ coalesce_spaces_P = 1;
+ continue;
+ case 't':
+ tab_width = get_positive(optarg, "tab width must be positive", 1);
+ continue;
+ case 'w':
+ goal_length = get_positive(optarg, "width must be positive", 1);
+ max_length = goal_length;
+ continue;
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ /* XXX this is not a stylistically approved use of getopt() */
+ if (goal_length==0) {
+ char *p;
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ goal_length = get_positive(++p, "width must be nonzero", 1);
+ else
+ goal_length = get_positive(argv[optind]+1,
+ "width must be nonzero", 1);
+ max_length = goal_length;
+ }
+ continue;
+ case 'h': default:
+ fprintf(stderr,
+"usage: fmt [-cmps] [-d chars] [-l num] [-t num]\n"
+" [-w width | -width | goal [maximum]] [file ...]\n"
+"Options: -c center each line instead of formatting\n"
+" -d <chars> double-space after <chars> at line end\n"
+" -l <n> turn each <n> spaces at start of line into a tab\n"
+" -m try to make sure mail header lines stay separate\n"
+" -n format lines beginning with a dot\n"
+" -p allow indented paragraphs\n"
+" -s coalesce whitespace inside lines\n"
+" -t <n> have tabs every <n> columns\n"
+" -w <n> set maximum width to <n>\n"
+" goal set target width to goal\n");
+ exit(ch=='h' ? 0 : EX_USAGE);
+ }
+ argc -= optind; argv += optind;
+
+ /* [ goal [ maximum ] ] */
+
+ if (argc>0 && goal_length==0
+ && (goal_length=get_positive(*argv,"goal length must be positive", 0))
+ != 0) {
+ --argc; ++argv;
+ if (argc>0
+ && (max_length=get_positive(*argv,"max length must be positive", 0))
+ != 0) {
+ --argc; ++argv;
+ if (max_length<goal_length)
+ errx(EX_USAGE, "max length must be >= goal length");
+ }
+ }
+ if (goal_length==0) goal_length = 65;
+ if (max_length==0) max_length = goal_length+10;
+ /* really needn't be longer */
+ output_buffer = XMALLOC((max_length+1) * sizeof(wchar_t));
+
+ /* 2. Process files. */
+
+ if (argc>0) {
+ while (argc-->0) process_named_file(*argv++);
+ }
+ else {
+ process_stream(stdin, "standard input");
+ }
+
+ /* We're done. */
+
+ return n_errors ? EX_NOINPUT : 0;
+
+}
+
+/* Process a single file, given its name.
+ */
+static void
+process_named_file(const char *name) {
+ FILE *f=fopen(name, "r");
+ if (!f) { warn("%s", name); ++n_errors; }
+ else {
+ process_stream(f, name);
+ if (ferror(f)) { warn("%s", name); ++n_errors; }
+ fclose(f);
+ }
+}
+
+/* Types of mail header continuation lines:
+ */
+typedef enum {
+ hdr_ParagraphStart = -1,
+ hdr_NonHeader = 0,
+ hdr_Header = 1,
+ hdr_Continuation = 2
+} HdrType;
+
+/* Process a stream. This is where the real work happens,
+ * except that centering is handled separately.
+ */
+static void
+process_stream(FILE *stream, const char *name) {
+ size_t last_indent=SILLY; /* how many spaces in last indent? */
+ size_t para_line_number=0; /* how many lines already read in this para? */
+ size_t first_indent=SILLY; /* indentation of line 0 of paragraph */
+ HdrType prev_header_type=hdr_ParagraphStart;
+ /* ^-- header_type of previous line; -1 at para start */
+ wchar_t *line;
+ size_t length;
+
+ if (centerP) { center_stream(stream, name); return; }
+ while ((line=get_line(stream,&length)) != NULL) {
+ size_t np=indent_length(line, length);
+ { HdrType header_type=hdr_NonHeader;
+ if (grok_mail_headers && prev_header_type!=hdr_NonHeader) {
+ if (np==0 && might_be_header(line))
+ header_type = hdr_Header;
+ else if (np>0 && prev_header_type>hdr_NonHeader)
+ header_type = hdr_Continuation;
+ }
+ /* We need a new paragraph if and only if:
+ * this line is blank,
+ * OR it's a troff request (and we don't format troff),
+ * OR it's a mail header,
+ * OR it's not a mail header AND the last line was one,
+ * OR the indentation has changed
+ * AND the line isn't a mail header continuation line
+ * AND this isn't the second line of an indented paragraph.
+ */
+ if ( length==0
+ || (line[0]=='.' && !format_troff)
+ || header_type==hdr_Header
+ || (header_type==hdr_NonHeader && prev_header_type>hdr_NonHeader)
+ || (np!=last_indent
+ && header_type != hdr_Continuation
+ && (!allow_indented_paragraphs || para_line_number != 1)) ) {
+ new_paragraph(output_in_paragraph ? last_indent : first_indent, np);
+ para_line_number = 0;
+ first_indent = np;
+ last_indent = np;
+ if (header_type==hdr_Header) last_indent=2; /* for cont. lines */
+ if (length==0 || (line[0]=='.' && !format_troff)) {
+ if (length==0)
+ putwchar('\n');
+ else
+ wprintf(L"%.*ls\n", (int)length, line);
+ prev_header_type=hdr_ParagraphStart;
+ continue;
+ }
+ }
+ else {
+ /* If this is an indented paragraph other than a mail header
+ * continuation, set |last_indent|.
+ */
+ if (np != last_indent && header_type != hdr_Continuation)
+ last_indent=np;
+ }
+ prev_header_type = header_type;
+ }
+
+ { size_t n=np;
+ while (n<length) {
+ /* Find word end and count spaces after it */
+ size_t word_length=0, space_length=0;
+ while (n+word_length < length && line[n+word_length] != ' ')
+ ++word_length;
+ space_length = word_length;
+ while (n+space_length < length && line[n+space_length] == ' ')
+ ++space_length;
+ /* Send the word to the output machinery. */
+ output_word(first_indent, last_indent,
+ line+n, word_length, space_length-word_length);
+ n += space_length;
+ }
+ }
+ ++para_line_number;
+ }
+ new_paragraph(output_in_paragraph ? last_indent : first_indent, 0);
+ if (ferror(stream)) { warn("%s", name); ++n_errors; }
+}
+
+/* How long is the indent on this line?
+ */
+static size_t
+indent_length(const wchar_t *line, size_t length) {
+ size_t n=0;
+ while (n<length && *line++ == ' ') ++n;
+ return n;
+}
+
+/* Might this line be a mail header?
+ * We deem a line to be a possible header if it matches the
+ * Perl regexp /^[A-Z][-A-Za-z0-9]*:\s/. This is *not* the same
+ * as in RFC whatever-number-it-is; we want to be gratuitously
+ * conservative to avoid mangling ordinary civilised text.
+ */
+static int
+might_be_header(const wchar_t *line) {
+ if (!iswupper(*line++)) return 0;
+ while (*line && (iswalnum(*line) || *line=='-')) ++line;
+ return (*line==':' && iswspace(line[1]));
+}
+
+/* Begin a new paragraph with an indent of |indent| spaces.
+ */
+static void
+new_paragraph(size_t old_indent, size_t indent) {
+ if (output_buffer_length) {
+ if (old_indent>0) output_indent(old_indent);
+ wprintf(L"%.*ls\n", (int)output_buffer_length, output_buffer);
+ }
+ x=indent; x0=0; output_buffer_length=0; pending_spaces=0;
+ output_in_paragraph = 0;
+}
+
+/* Output spaces or tabs for leading indentation.
+ */
+static void
+output_indent(size_t n_spaces) {
+ if (output_tab_width) {
+ while (n_spaces >= output_tab_width) {
+ putwchar('\t');
+ n_spaces -= output_tab_width;
+ }
+ }
+ while (n_spaces-- > 0) putwchar(' ');
+}
+
+/* Output a single word, or add it to the buffer.
+ * indent0 and indent1 are the indents to use on the first and subsequent
+ * lines of a paragraph. They'll often be the same, of course.
+ */
+static void
+output_word(size_t indent0, size_t indent1, const wchar_t *word, size_t length, size_t spaces) {
+ size_t new_x;
+ size_t indent = output_in_paragraph ? indent1 : indent0;
+ size_t width;
+ const wchar_t *p;
+ int cwidth;
+
+ for (p = word, width = 0; p < &word[length]; p++)
+ width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1;
+
+ new_x = x + pending_spaces + width;
+
+ /* If either |spaces==0| (at end of line) or |coalesce_spaces_P|
+ * (squashing internal whitespace), then add just one space;
+ * except that if the last character was a sentence-ender we
+ * actually add two spaces.
+ */
+ if (coalesce_spaces_P || spaces==0)
+ spaces = wcschr(sentence_enders, word[length-1]) ? 2 : 1;
+
+ if (new_x<=goal_length) {
+ /* After adding the word we still aren't at the goal length,
+ * so clearly we add it to the buffer rather than outputing it.
+ */
+ wmemset(output_buffer+output_buffer_length, L' ', pending_spaces);
+ x0 += pending_spaces; x += pending_spaces;
+ output_buffer_length += pending_spaces;
+ wmemcpy(output_buffer+output_buffer_length, word, length);
+ x0 += width; x += width; output_buffer_length += length;
+ pending_spaces = spaces;
+ }
+ else {
+ /* Adding the word takes us past the goal. Print the line-so-far,
+ * and the word too iff either (1) the lsf is empty or (2) that
+ * makes us nearer the goal but doesn't take us over the limit,
+ * or (3) the word on its own takes us over the limit.
+ * In case (3) we put a newline in between.
+ */
+ if (indent>0) output_indent(indent);
+ wprintf(L"%.*ls", (int)output_buffer_length, output_buffer);
+ if (x0==0 || (new_x <= max_length && new_x-goal_length <= goal_length-x)) {
+ wprintf(L"%*ls", (int)pending_spaces, L"");
+ goto write_out_word;
+ }
+ else {
+ /* If the word takes us over the limit on its own, just
+ * spit it out and don't bother buffering it.
+ */
+ if (indent+width > max_length) {
+ putwchar('\n');
+ if (indent>0) output_indent(indent);
+write_out_word:
+ wprintf(L"%.*ls", (int)length, word);
+ x0 = 0; x = indent1; pending_spaces = 0;
+ output_buffer_length = 0;
+ }
+ else {
+ wmemcpy(output_buffer, word, length);
+ x0 = width; x = width+indent1; pending_spaces = spaces;
+ output_buffer_length = length;
+ }
+ }
+ putwchar('\n');
+ output_in_paragraph = 1;
+ }
+}
+
+/* Process a stream, but just center its lines rather than trying to
+ * format them neatly.
+ */
+static void
+center_stream(FILE *stream, const char *name) {
+ wchar_t *line, *p;
+ size_t length;
+ size_t width;
+ int cwidth;
+ while ((line=get_line(stream, &length)) != 0) {
+ size_t l=length;
+ while (l>0 && iswspace(*line)) { ++line; --l; }
+ length=l;
+ for (p = line, width = 0; p < &line[length]; p++)
+ width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1;
+ l = width;
+ while (l<goal_length) { putwchar(' '); l+=2; }
+ wprintf(L"%.*ls\n", (int)length, line);
+ }
+ if (ferror(stream)) { warn("%s", name); ++n_errors; }
+}
+
+/* Get a single line from a stream. Expand tabs, strip control
+ * characters and trailing whitespace, and handle backspaces.
+ * Return the address of the buffer containing the line, and
+ * put the length of the line in |lengthp|.
+ * This can cope with arbitrarily long lines, and with lines
+ * without terminating \n.
+ * If there are no characters left or an error happens, we
+ * return 0.
+ * Don't confuse |spaces_pending| here with the global
+ * |pending_spaces|.
+ */
+static wchar_t *
+get_line(FILE *stream, size_t *lengthp) {
+ static wchar_t *buf=NULL;
+ static size_t length=0;
+ size_t len=0;
+ wint_t ch;
+ size_t spaces_pending=0;
+ int troff=0;
+ size_t col=0;
+ int cwidth;
+
+ if (buf==NULL) { length=100; buf=XMALLOC(length * sizeof(wchar_t)); }
+ while ((ch=getwc(stream)) != '\n' && ch != WEOF) {
+ if (len+spaces_pending==0 && ch=='.' && !format_troff) troff=1;
+ if (ch==' ') ++spaces_pending;
+ else if (troff || iswprint(ch)) {
+ while (len+spaces_pending >= length) {
+ length*=2; buf=xrealloc(buf, length * sizeof(wchar_t));
+ }
+ while (spaces_pending > 0) { --spaces_pending; buf[len++]=' '; col++; }
+ buf[len++] = ch;
+ col += (cwidth = wcwidth(ch)) > 0 ? cwidth : 1;
+ }
+ else if (ch=='\t')
+ spaces_pending += tab_width - (col+spaces_pending)%tab_width;
+ else if (ch=='\b') { if (len) --len; if (col) --col; }
+ }
+ *lengthp=len;
+ return (len>0 || ch!=WEOF) ? buf : 0;
+}
+
+/* (Re)allocate some memory, exiting with an error if we can't.
+ */
+static void *
+xrealloc(void *ptr, size_t nbytes) {
+ void *p = realloc(ptr, nbytes);
+ if (p == NULL) errx(EX_OSERR, "out of memory");
+ return p;
+}
diff --git a/text_cmds/fold/fold.1 b/text_cmds/fold/fold.1
new file mode 100644
index 0000000..b9f206d
--- /dev/null
+++ b/text_cmds/fold/fold.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1980, 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fold.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/fold/fold.1,v 1.12 2004/08/02 11:15:01 tjr Exp $
+.\"
+.Dd August 2, 2004
+.Dt FOLD 1
+.Os
+.Sh NAME
+.Nm fold
+.Nd "fold long lines for finite width output device"
+.Sh SYNOPSIS
+.Nm
+.Op Fl bs
+.Op Fl w Ar width
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is a filter which folds the contents of the specified files,
+or the standard input if no files are specified,
+breaking the lines to have a maximum of 80 columns.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Count
+.Ar width
+in bytes rather than column positions.
+.It Fl s
+Fold line after the last blank character within the first
+.Ar width
+column positions (or bytes).
+.It Fl w Ar width
+Specify a line width to use instead of the default 80 columns.
+.Ar Width
+should be a multiple of 8 if tabs are present, or the tabs should
+be expanded using
+.Xr expand 1
+before using
+.Nm .
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh SEE ALSO
+.Xr expand 1 ,
+.Xr fmt 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh BUGS
+If underlining is present it may be messed up by folding.
diff --git a/text_cmds/fold/fold.c b/text_cmds/fold/fold.c
new file mode 100644
index 0000000..e26356c
--- /dev/null
+++ b/text_cmds/fold/fold.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Ruddy.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)fold.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/fold/fold.c,v 1.13 2004/06/24 15:12:29 tjr Exp $");
+
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define DEFLINEWIDTH 80
+
+void fold(int);
+static int newpos(int, wint_t);
+static void usage(void);
+
+int bflag; /* Count bytes, not columns */
+int sflag; /* Split on word boundaries */
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int rval, width;
+ char *p;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ width = -1;
+ while ((ch = getopt(argc, argv, "0123456789bsw:")) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'w':
+ if ((width = atoi(optarg)) <= 0) {
+ errx(1, "illegal width value");
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (width == -1) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ width = atoi(++p);
+ else
+ width = atoi(argv[optind] + 1);
+ }
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (width == -1)
+ width = DEFLINEWIDTH;
+ rval = 0;
+ if (!*argv)
+ fold(width);
+ else for (; *argv; ++argv)
+ if (!freopen(*argv, "r", stdin)) {
+ warn("%s", *argv);
+ rval = 1;
+ } else
+ fold(width);
+ exit(rval);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: fold [-bs] [-w width] [file ...]\n");
+ exit(1);
+}
+
+/*
+ * Fold the contents of standard input to fit within WIDTH columns (or bytes)
+ * and write to standard output.
+ *
+ * If sflag is set, split the line at the last space character on the line.
+ * This flag necessitates storing the line in a buffer until the current
+ * column > width, or a newline or EOF is read.
+ *
+ * The buffer can grow larger than WIDTH due to backspaces and carriage
+ * returns embedded in the input stream.
+ */
+void
+fold(int width)
+{
+ static wchar_t *buf;
+ static int buf_max;
+ int col, i, indx, space;
+ wint_t ch;
+
+ col = indx = 0;
+ while ((ch = getwchar()) != WEOF) {
+ if (ch == '\n') {
+ wprintf(L"%.*ls\n", indx, buf);
+ col = indx = 0;
+ continue;
+ }
+ if ((col = newpos(col, ch)) > width) {
+ if (sflag) {
+ i = indx;
+ while (--i >= 0 && !iswblank(buf[i]))
+ ;
+ space = i;
+ }
+ if (sflag && space != -1) {
+ space++;
+ wprintf(L"%.*ls\n", space, buf);
+ wmemmove(buf, buf + space, indx - space);
+ indx -= space;
+ col = 0;
+ for (i = 0; i < indx; i++)
+ col = newpos(col, buf[i]);
+ } else {
+ wprintf(L"%.*ls\n", indx, buf);
+ col = indx = 0;
+ }
+ col = newpos(col, ch);
+ }
+ if (indx + 1 > buf_max) {
+ buf_max += LINE_MAX;
+ buf = realloc(buf, sizeof(*buf) * buf_max);
+ if (buf == NULL)
+ err(1, "realloc()");
+ }
+ buf[indx++] = ch;
+ }
+
+ if (ferror(stdin)) {
+ fprintf(stderr, "Input error\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (indx != 0)
+ wprintf(L"%.*ls", indx, buf);
+}
+
+/*
+ * Update the current column position for a character.
+ */
+static int
+newpos(int col, wint_t ch)
+{
+ char buf[MB_LEN_MAX];
+ size_t len;
+ int w;
+
+ if (bflag) {
+ len = wcrtomb(buf, ch, NULL);
+ col += len;
+ } else
+ switch (ch) {
+ case '\b':
+ if (col > 0)
+ --col;
+ break;
+ case '\r':
+ col = 0;
+ break;
+ case '\t':
+ col = (col + 8) & ~7;
+ break;
+ default:
+ if ((w = wcwidth(ch)) > 0)
+ col += w;
+ break;
+ }
+
+ return (col);
+}
diff --git a/text_cmds/grep/file.c b/text_cmds/grep/file.c
new file mode 100644
index 0000000..54d8772
--- /dev/null
+++ b/text_cmds/grep/file.c
@@ -0,0 +1,347 @@
+/* $NetBSD: file.c,v 1.5 2011/02/16 18:35:39 joerg Exp $ */
+/* $FreeBSD: src/usr.bin/grep/file.c,v 1.7 2011/10/11 22:27:23 gabor Exp $ */
+/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/grep/file.c,v 1.7 2011/10/11 22:27:23 gabor Exp $");
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifndef WITHOUT_LZMA
+#include <lzma.h>
+#endif
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <zlib.h>
+
+#ifndef WITHOUT_BZIP2
+#include <bzlib.h>
+#endif
+
+#include "grep.h"
+
+#define MAXBUFSIZ (32 * 1024)
+#define LNBUFBUMP 80
+
+static gzFile gzbufdesc;
+#ifndef WITHOUT_LZMA
+static lzma_stream lstrm = LZMA_STREAM_INIT;
+#endif
+#ifndef WITHOUT_BZIP2
+static BZFILE* bzbufdesc;
+#endif
+
+static unsigned char *buffer;
+static unsigned char *bufpos;
+static size_t bufrem;
+static size_t fsiz;
+
+static unsigned char *lnbuf;
+static size_t lnbuflen;
+
+static inline int
+grep_refill(struct file *f)
+{
+ ssize_t nr;
+
+ if (filebehave == FILE_MMAP)
+ return (0);
+
+ bufpos = buffer;
+ bufrem = 0;
+
+ if (filebehave == FILE_GZIP) {
+ nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
+#ifndef WITHOUT_BZIP2
+ } else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
+ int bzerr;
+
+ nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
+ switch (bzerr) {
+ case BZ_OK:
+ case BZ_STREAM_END:
+ /* No problem, nr will be okay */
+ break;
+ case BZ_DATA_ERROR_MAGIC:
+ /*
+ * As opposed to gzread(), which simply returns the
+ * plain file data, if it is not in the correct
+ * compressed format, BZ2_bzRead() instead aborts.
+ *
+ * So, just restart at the beginning of the file again,
+ * and use plain reads from now on.
+ */
+ BZ2_bzReadClose(&bzerr, bzbufdesc);
+ bzbufdesc = NULL;
+ if (lseek(f->fd, 0, SEEK_SET) == -1)
+ return (-1);
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+ break;
+ default:
+ /* Make sure we exit with an error */
+ nr = -1;
+ }
+#endif
+#ifndef WITHOUT_LZMA
+ } else if ((filebehave == FILE_XZ) || (filebehave == FILE_LZMA)) {
+ lzma_action action = LZMA_RUN;
+ uint8_t in_buf[MAXBUFSIZ];
+ lzma_ret ret;
+
+ ret = (filebehave == FILE_XZ) ?
+ lzma_stream_decoder(&lstrm, UINT64_MAX,
+ LZMA_CONCATENATED) :
+ lzma_alone_decoder(&lstrm, UINT64_MAX);
+
+ if (ret != LZMA_OK)
+ return (-1);
+
+ lstrm.next_out = buffer;
+ lstrm.avail_out = MAXBUFSIZ;
+ lstrm.next_in = in_buf;
+ nr = read(f->fd, in_buf, MAXBUFSIZ);
+
+ if (nr < 0)
+ return (-1);
+ else if (nr == 0)
+ action = LZMA_FINISH;
+
+ lstrm.avail_in = nr;
+ ret = lzma_code(&lstrm, action);
+
+ if (ret != LZMA_OK && ret != LZMA_STREAM_END)
+ return (-1);
+ bufrem = MAXBUFSIZ - lstrm.avail_out;
+ return (0);
+#endif
+ } else
+ nr = read(f->fd, buffer, MAXBUFSIZ);
+
+ if (nr < 0)
+ return (-1);
+
+ bufrem = nr;
+ return (0);
+}
+
+static inline int
+grep_lnbufgrow(size_t newlen)
+{
+
+ if (lnbuflen < newlen) {
+ lnbuf = grep_realloc(lnbuf, newlen);
+ lnbuflen = newlen;
+ }
+
+ return (0);
+}
+
+char *
+grep_fgetln(struct file *f, size_t *lenp)
+{
+ unsigned char *p;
+ char *ret;
+ size_t len;
+ size_t off;
+ ptrdiff_t diff;
+
+ /* Fill the buffer, if necessary */
+ if (bufrem == 0 && grep_refill(f) != 0)
+ goto error;
+
+ if (bufrem == 0) {
+ /* Return zero length to indicate EOF */
+ *lenp = 0;
+#ifdef __APPLE__
+ return (char *)(bufpos);
+#else
+ return (bufpos);
+#endif
+ }
+
+ /* Look for a newline in the remaining part of the buffer */
+ if ((p = memchr(bufpos, '\n', bufrem)) != NULL) {
+ ++p; /* advance over newline */
+#ifdef __APPLE__
+ ret = (char *)bufpos;
+#else
+ ret = bufpos;
+#endif
+ len = p - bufpos;
+ bufrem -= len;
+ bufpos = p;
+ *lenp = len;
+ return (ret);
+ }
+
+ /* We have to copy the current buffered data to the line buffer */
+ for (len = bufrem, off = 0; ; len += bufrem) {
+ /* Make sure there is room for more data */
+ if (grep_lnbufgrow(len + LNBUFBUMP))
+ goto error;
+ memcpy(lnbuf + off, bufpos, len - off);
+ off = len;
+ if (grep_refill(f) != 0)
+ goto error;
+ if (bufrem == 0)
+ /* EOF: return partial line */
+ break;
+ if ((p = memchr(bufpos, '\n', bufrem)) == NULL)
+ continue;
+ /* got it: finish up the line (like code above) */
+ ++p;
+ diff = p - bufpos;
+ len += diff;
+ if (grep_lnbufgrow(len))
+ goto error;
+ memcpy(lnbuf + off, bufpos, diff);
+ bufrem -= diff;
+ bufpos = p;
+ break;
+ }
+ *lenp = len;
+#ifdef __APPLE__
+ return (char *)(lnbuf);
+#else
+ return (lnbuf);
+#endif
+
+error:
+ *lenp = 0;
+ return (NULL);
+}
+
+/*
+ * Opens a file for processing.
+ */
+struct file *
+grep_open(const char *path)
+{
+ struct file *f;
+
+ f = grep_malloc(sizeof *f);
+ memset(f, 0, sizeof *f);
+ if (path == NULL) {
+ /* Processing stdin implies --line-buffered. */
+ lbflag = true;
+ f->fd = STDIN_FILENO;
+ } else if ((f->fd = open(path, O_RDONLY)) == -1)
+ goto error1;
+
+ if (filebehave == FILE_MMAP) {
+ struct stat st;
+
+ if ((fstat(f->fd, &st) == -1) || (st.st_size > OFF_MAX) ||
+ (!S_ISREG(st.st_mode)))
+ filebehave = FILE_STDIO;
+ else {
+#ifdef __APPLE__
+ int flags = MAP_PRIVATE | MAP_NOCACHE;
+#else
+ int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC;
+#endif
+#ifdef MAP_PREFAULT_READ
+ flags |= MAP_PREFAULT_READ;
+#endif
+ fsiz = st.st_size;
+ buffer = mmap(NULL, fsiz, PROT_READ, flags,
+ f->fd, (off_t)0);
+ if (buffer == MAP_FAILED)
+ filebehave = FILE_STDIO;
+ else {
+ bufrem = st.st_size;
+ bufpos = buffer;
+ madvise(buffer, st.st_size, MADV_SEQUENTIAL);
+ }
+ }
+ }
+
+ if ((buffer == NULL) || (buffer == MAP_FAILED))
+ buffer = grep_malloc(MAXBUFSIZ);
+
+ if (filebehave == FILE_GZIP &&
+ (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
+ goto error2;
+
+#ifndef WITHOUT_BZIP2
+ if (filebehave == FILE_BZIP &&
+ (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
+ goto error2;
+#endif
+
+ /* Fill read buffer, also catches errors early */
+ if (bufrem == 0 && grep_refill(f) != 0)
+ goto error2;
+
+ /* Check for binary stuff, if necessary */
+ if (binbehave != BINFILE_TEXT && memchr(bufpos, '\0', bufrem) != NULL)
+ f->binary = true;
+
+ return (f);
+
+error2:
+ close(f->fd);
+error1:
+ free(f);
+ return (NULL);
+}
+
+/*
+ * Closes a file.
+ */
+void
+grep_close(struct file *f)
+{
+
+ close(f->fd);
+
+ /* Reset read buffer and line buffer */
+ if (filebehave == FILE_MMAP) {
+ munmap(buffer, fsiz);
+ buffer = NULL;
+ }
+ bufpos = buffer;
+ bufrem = 0;
+
+ free(lnbuf);
+ lnbuf = NULL;
+ lnbuflen = 0;
+}
diff --git a/text_cmds/grep/grep.1 b/text_cmds/grep/grep.1
new file mode 100644
index 0000000..a1d056e
--- /dev/null
+++ b/text_cmds/grep/grep.1
@@ -0,0 +1,500 @@
+.\" $NetBSD: grep.1,v 1.2 2011/02/16 01:31:33 joerg Exp $
+.\" $FreeBSD: src/usr.bin/grep/grep.1,v 1.5 2011/04/07 13:03:35 gabor Exp $
+.\" $OpenBSD: grep.1,v 1.38 2010/04/05 06:30:59 jmc Exp $
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)grep.1 8.3 (Berkeley) 4/18/94
+.\"
+.Dd July 28, 2010
+.Dt GREP 1
+.Os
+.Sh NAME
+.Nm grep , egrep , fgrep ,
+.Nm zgrep , zegrep , zfgrep
+.Nd file pattern searcher
+.Sh SYNOPSIS
+.Nm grep
+.Bk -words
+.Op Fl abcdDEFGHhIiJLlmnOopqRSsUVvwxZ
+.Op Fl A Ar num
+.Op Fl B Ar num
+.Op Fl C Ns Op Ar num
+.Op Fl e Ar pattern
+.Op Fl f Ar file
+.Op Fl Fl binary-files Ns = Ns Ar value
+.Op Fl Fl color Ns Op = Ns Ar when
+.Op Fl Fl colour Ns Op = Ns Ar when
+.Op Fl Fl context Ns Op = Ns Ar num
+.Op Fl Fl label
+.Op Fl Fl line-buffered
+.Op Fl Fl null
+.Op Ar pattern
+.Op Ar
+.Ek
+.Sh DESCRIPTION
+The
+.Nm grep
+utility searches any given input files,
+selecting lines that match one or more patterns.
+By default, a pattern matches an input line if the regular expression
+(RE) in the pattern matches the input line
+without its trailing newline.
+An empty expression matches every line.
+Each input line that matches at least one of the patterns is written
+to the standard output.
+.Pp
+.Nm grep
+is used for simple patterns and
+basic regular expressions
+.Pq BREs ;
+.Nm egrep
+can handle extended regular expressions
+.Pq EREs .
+See
+.Xr re_format 7
+for more information on regular expressions.
+.Nm fgrep
+is quicker than both
+.Nm grep
+and
+.Nm egrep ,
+but can only handle fixed patterns
+(i.e. it does not interpret regular expressions).
+Patterns may consist of one or more lines,
+allowing any of the pattern lines to match a portion of the input.
+.Pp
+.Nm zgrep ,
+.Nm zegrep ,
+and
+.Nm zfgrep
+act like
+.Nm grep ,
+.Nm egrep ,
+and
+.Nm fgrep ,
+respectively, but accept input files compressed with the
+.Xr compress 1
+or
+.Xr gzip 1
+compression utilities.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl A Ar num , Fl Fl after-context Ns = Ns Ar num
+Print
+.Ar num
+lines of trailing context after each match.
+See also the
+.Fl B
+and
+.Fl C
+options.
+.It Fl a , Fl Fl text
+Treat all files as ASCII text.
+Normally
+.Nm
+will simply print
+.Dq Binary file ... matches
+if files contain binary characters.
+Use of this option forces
+.Nm
+to output lines matching the specified pattern.
+.It Fl B Ar num , Fl Fl before-context Ns = Ns Ar num
+Print
+.Ar num
+lines of leading context before each match.
+See also the
+.Fl A
+and
+.Fl C
+options.
+.It Fl b , Fl Fl byte-offset
+The offset in bytes of a matched pattern is
+displayed in front of the respective matched line.
+.It Fl C Ns Op Ar num , Fl Fl context Ns = Ns Ar num
+Print
+.Ar num
+lines of leading and trailing context surrounding each match.
+The default is 2 and is equivalent to
+.Fl A
+.Ar 2
+.Fl B
+.Ar 2 .
+Note:
+no whitespace may be given between the option and its argument.
+.It Fl c , Fl Fl count
+Only a count of selected lines is written to standard output.
+.It Fl Fl colour Ns = Ns Op Ar when , Fl Fl color Ns = Ns Op Ar when
+Mark up the matching text with the expression stored in
+.Ev GREP_COLOR
+environment variable.
+The possible values of when can be `never', `always' or `auto'.
+.It Fl D Ar action , Fl Fl devices Ns = Ns Ar action
+Specify the demanded action for devices, FIFOs and sockets.
+The default action is `read', which means, that they are read
+as if they were normal files.
+If the action is set to `skip', devices will be silently skipped.
+.It Fl d Ar action , Fl Fl directories Ns = Ns Ar action
+Specify the demanded action for directories.
+It is `read' by default, which means that the directories
+are read in the same manner as normal files.
+Other possible values are `skip' to silently ignore the
+directories, and `recurse' to read them recursively, which
+has the same effect as the
+.Fl R
+and
+.Fl r
+option.
+.It Fl E , Fl Fl extended-regexp
+Interpret
+.Ar pattern
+as an extended regular expression
+(i.e. force
+.Nm grep
+to behave as
+.Nm egrep ) .
+.It Fl e Ar pattern , Fl Fl regexp Ns = Ns Ar pattern
+Specify a pattern used during the search of the input:
+an input line is selected if it matches any of the specified patterns.
+This option is most useful when multiple
+.Fl e
+options are used to specify multiple patterns,
+or when a pattern begins with a dash
+.Pq Sq - .
+.It Fl Fl exclude
+If specified, it excludes files matching the given
+filename pattern from the search.
+Note that
+.Fl Fl exclude
+patterns take priority over
+.Fl Fl include
+patterns, and if no
+.Fl Fl include
+pattern is specified, all files are searched that are
+not excluded.
+Patterns are matched to the full path specified,
+not only to the filename component.
+.It Fl Fl exclude-dir
+If
+.Fl R
+is specified, it excludes directories matching the
+given filename pattern from the search.
+Note that
+.Fl Fl exclude-dir
+patterns take priority over
+.Fl Fl include-dir
+patterns, and if no
+.Fl Fl include-dir
+pattern is specified, all directories are searched that are
+not excluded.
+.It Fl F , Fl Fl fixed-strings
+Interpret
+.Ar pattern
+as a set of fixed strings
+(i.e. force
+.Nm grep
+to behave as
+.Nm fgrep ) .
+.It Fl f Ar file , Fl Fl file Ns = Ns Ar file
+Read one or more newline separated patterns from
+.Ar file .
+Empty pattern lines match every input line.
+Newlines are not considered part of a pattern.
+If
+.Ar file
+is empty, nothing is matched.
+.It Fl G , Fl Fl basic-regexp
+Interpret
+.Ar pattern
+as a basic regular expression
+(i.e. force
+.Nm grep
+to behave as traditional
+.Nm grep ) .
+.It Fl H
+Always print filename headers with output lines.
+.It Fl h , Fl Fl no-filename
+Never print filename headers
+.Pq i.e. filenames
+with output lines.
+.It Fl Fl help
+Print a brief help message.
+.It Fl I
+Ignore binary files.
+This option is equivalent to
+.Fl Fl binary-file Ns = Ns Ar without-match
+option.
+.It Fl i , Fl Fl ignore-case
+Perform case insensitive matching.
+By default,
+.Nm grep
+is case sensitive.
+.It Fl Fl include
+If specified, only files matching the
+given filename pattern are searched.
+Note that
+.Fl Fl exclude
+patterns take priority over
+.Fl Fl include
+patterns.
+Patterns are matched to the full path specified,
+not only to the filename component.
+.It Fl Fl include-dir
+If
+.Fl R
+is specified, only directories matching the
+given filename pattern are searched.
+Note that
+.Fl Fl exclude-dir
+patterns take priority over
+.Fl Fl include-dir
+patterns.
+.It Fl J, Fl Fl bz2decompress
+Decompress the
+.Xr bzip2 1
+compressed file before looking for the text.
+.It Fl L , Fl Fl files-without-match
+Only the names of files not containing selected lines are written to
+standard output.
+Pathnames are listed once per file searched.
+If the standard input is searched, the string
+.Dq (standard input)
+is written.
+.It Fl l , Fl Fl files-with-matches
+Only the names of files containing selected lines are written to
+standard output.
+.Nm grep
+will only search a file until a match has been found,
+making searches potentially less expensive.
+Pathnames are listed once per file searched.
+If the standard input is searched, the string
+.Dq (standard input)
+is written.
+.It Fl Fl mmap
+Use
+.Xr mmap 2
+instead of
+.Xr read 2
+to read input, which can result in better performance under some
+circumstances but can cause undefined behaviour.
+.It Fl m Ar num, Fl Fl max-count Ns = Ns Ar num
+Stop reading the file after
+.Ar num
+matches.
+.It Fl n , Fl Fl line-number
+Each output line is preceded by its relative line number in the file,
+starting at line 1.
+The line number counter is reset for each file processed.
+This option is ignored if
+.Fl c ,
+.Fl L ,
+.Fl l ,
+or
+.Fl q
+is
+specified.
+.It Fl Fl null
+Prints a zero-byte after the file name.
+.It Fl O
+If
+.Fl R
+is specified, follow symbolic links only if they were explicitly listed
+on the command line.
+The default is not to follow symbolic links.
+.It Fl o, Fl Fl only-matching
+Prints only the matching part of the lines.
+.It Fl p
+If
+.Fl R
+is specified, no symbolic links are followed.
+This is the default.
+.It Fl q , Fl Fl quiet , Fl Fl silent
+Quiet mode:
+suppress normal output.
+.Nm grep
+will only search a file until a match has been found,
+making searches potentially less expensive.
+.It Fl R , Fl r , Fl Fl recursive
+Recursively search subdirectories listed.
+.It Fl S
+If
+.Fl R
+is specified, all symbolic links are followed.
+The default is not to follow symbolic links.
+.It Fl s , Fl Fl no-messages
+Silent mode.
+Nonexistent and unreadable files are ignored
+(i.e. their error messages are suppressed).
+.It Fl U , Fl Fl binary
+Search binary files, but do not attempt to print them.
+.It Fl V , Fl Fl version
+Display version information and exit.
+.It Fl v , Fl Fl invert-match
+Selected lines are those
+.Em not
+matching any of the specified patterns.
+.It Fl w , Fl Fl word-regexp
+The expression is searched for as a word (as if surrounded by
+.Sq [[:<:]]
+and
+.Sq [[:>:]] ;
+see
+.Xr re_format 7 ) .
+.It Fl x , Fl Fl line-regexp
+Only input lines selected against an entire fixed string or regular
+expression are considered to be matching lines.
+.It Fl y
+Equivalent to
+.Fl i .
+Obsoleted.
+.It Fl Z , Fl z , Fl Fl decompress
+Force
+.Nm grep
+to behave as
+.Nm zgrep .
+.It Fl Fl binary-files Ns = Ns Ar value
+Controls searching and printing of binary files.
+Options are
+.Ar binary ,
+the default: search binary files but do not print them;
+.Ar without-match :
+do not search binary files;
+and
+.Ar text :
+treat all files as text.
+.Sm off
+.It Fl Fl context Op = Ar num
+.Sm on
+Print
+.Ar num
+lines of leading and trailing context.
+The default is 2.
+.It Fl Fl line-buffered
+Force output to be line buffered.
+By default, output is line buffered when standard output is a terminal
+and block buffered otherwise.
+.Pp
+.El
+If no file arguments are specified, the standard input is used.
+.Sh ENVIRONMENT
+.Bl -tag -width "GREP_OPTIONS"
+.It Ev GREP_OPTIONS
+May be used to specify default options that will be placed at the beginning
+of the argument list.
+Backslash-escaping is not supported, unlike the behavior in GNU grep.
+.El
+.Sh EXIT STATUS
+The
+.Nm grep
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width flag -compact
+.It Li 0
+One or more lines were selected.
+.It Li 1
+No lines were selected.
+.It Li \*(Gt1
+An error occurred.
+.El
+.Sh EXAMPLES
+To find all occurrences of the word
+.Sq patricia
+in a file:
+.Pp
+.Dl $ grep 'patricia' myfile
+.Pp
+To find all occurrences of the pattern
+.Ql .Pp
+at the beginning of a line:
+.Pp
+.Dl $ grep '^\e.Pp' myfile
+.Pp
+The apostrophes ensure the entire expression is evaluated by
+.Nm grep
+instead of by the user's shell.
+The caret
+.Ql ^
+matches the null string at the beginning of a line,
+and the
+.Ql \e
+escapes the
+.Ql \&. ,
+which would otherwise match any character.
+.Pp
+To find all lines in a file which do not contain the words
+.Sq foo
+or
+.Sq bar :
+.Pp
+.Dl $ grep -v -e 'foo' -e 'bar' myfile
+.Pp
+A simple example of an extended regular expression:
+.Pp
+.Dl $ egrep '19|20|25' calendar
+.Pp
+Peruses the file
+.Sq calendar
+looking for either 19, 20, or 25.
+.Sh SEE ALSO
+.Xr ed 1 ,
+.Xr ex 1 ,
+.Xr gzip 1 ,
+.Xr sed 1 ,
+.Xr re_format 7
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl AaBbCDdGHhIJLmoPRSUVwZ
+are extensions to that specification, and the behaviour of the
+.Fl f
+flag when used with an empty pattern file is left undefined.
+.Pp
+All long options are provided for compatibility with
+GNU versions of this utility.
+.Pp
+Historic versions of the
+.Nm grep
+utility also supported the flags
+.Op Fl ruy .
+This implementation supports those options;
+however, their use is strongly discouraged.
+.Sh HISTORY
+The
+.Nm grep
+command first appeared in
+.At v6 .
+.Sh BUGS
+The
+.Nm
+utility does not normalize Unicode input, so a pattern containing composed
+characters will not match decomposed input, and vice versa.
diff --git a/text_cmds/grep/grep.c b/text_cmds/grep/grep.c
new file mode 100644
index 0000000..38f50ea
--- /dev/null
+++ b/text_cmds/grep/grep.c
@@ -0,0 +1,783 @@
+/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */
+/* $FreeBSD: src/usr.bin/grep/grep.c,v 1.16 2012/01/15 17:01:28 eadler Exp $ */
+/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/grep/grep.c,v 1.16 2012/01/15 17:01:28 eadler Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <libgen.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef WITHOUT_FASTMATCH
+#include "fastmatch.h"
+#endif
+#include "grep.h"
+
+#ifndef WITHOUT_NLS
+#include <nl_types.h>
+nl_catd catalog;
+#endif
+
+/*
+ * Default messags to use when NLS is disabled or no catalogue
+ * is found.
+ */
+const char *errstr[] = {
+ "",
+/* 1*/ "(standard input)",
+/* 2*/ "cannot read bzip2 compressed file",
+/* 3*/ "unknown %s option",
+#ifdef __APPLE__
+/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
+#else
+/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
+#endif
+/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
+/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
+/* 7*/ "\t[--null] [pattern] [file ...]\n",
+/* 8*/ "Binary file %s matches\n",
+/* 9*/ "%s (BSD grep) %s\n",
+};
+
+/* Flags passed to regcomp() and regexec() */
+int cflags = REG_NOSUB;
+int eflags = REG_STARTEND;
+
+/* Shortcut for matching all cases like empty regex */
+bool matchall;
+
+/* Searching patterns */
+unsigned int patterns, pattern_sz;
+struct pat *pattern;
+regex_t *r_pattern;
+#ifndef WITHOUT_FASTMATCH
+fastmatch_t *fg_pattern;
+#endif
+
+/* Filename exclusion/inclusion patterns */
+unsigned int fpatterns, fpattern_sz;
+unsigned int dpatterns, dpattern_sz;
+struct epat *dpattern, *fpattern;
+
+/* For regex errors */
+char re_error[RE_ERROR_BUF + 1];
+
+/* Command-line flags */
+unsigned long long Aflag; /* -A x: print x lines trailing each match */
+unsigned long long Bflag; /* -B x: print x lines leading each match */
+bool Hflag; /* -H: always print file name */
+bool Lflag; /* -L: only show names of files with no matches */
+bool bflag; /* -b: show block numbers for each match */
+bool cflag; /* -c: only show a count of matching lines */
+bool hflag; /* -h: don't print filename headers */
+bool iflag; /* -i: ignore case */
+bool lflag; /* -l: only show names of files with matches */
+bool mflag; /* -m x: stop reading the files after x matches */
+long long mcount; /* count for -m */
+bool nflag; /* -n: show line numbers in front of matching lines */
+bool oflag; /* -o: print only matching part */
+bool qflag; /* -q: quiet mode (don't output anything) */
+bool sflag; /* -s: silent mode (ignore errors) */
+bool vflag; /* -v: only show non-matching lines */
+bool wflag; /* -w: pattern must start and end on word boundaries */
+bool xflag; /* -x: pattern must match entire line */
+bool lbflag; /* --line-buffered */
+bool nullflag; /* --null */
+char *label; /* --label */
+const char *color; /* --color */
+int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
+int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
+int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
+int devbehave = DEV_READ; /* -D: handling of devices */
+int dirbehave = DIR_READ; /* -dRr: handling of directories */
+int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
+
+bool dexclude, dinclude; /* --exclude-dir and --include-dir */
+bool fexclude, finclude; /* --exclude and --include */
+
+enum {
+ BIN_OPT = CHAR_MAX + 1,
+ COLOR_OPT,
+ HELP_OPT,
+ MMAP_OPT,
+ LINEBUF_OPT,
+ LABEL_OPT,
+ NULL_OPT,
+ R_EXCLUDE_OPT,
+ R_INCLUDE_OPT,
+ R_DEXCLUDE_OPT,
+ R_DINCLUDE_OPT
+};
+
+static inline const char *init_color(const char *);
+
+/* Housekeeping */
+bool first = true; /* flag whether we are processing the first match */
+bool prev; /* flag whether or not the previous line matched */
+int tail; /* lines left to print */
+bool file_err; /* file reading error */
+
+/*
+ * Prints usage information and returns 2.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, getstr(4), getprogname());
+ fprintf(stderr, "%s", getstr(5));
+ fprintf(stderr, "%s", getstr(6));
+ fprintf(stderr, "%s", getstr(7));
+ exit(2);
+}
+
+static const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
+
+static const struct option long_options[] =
+{
+ {"binary-files", required_argument, NULL, BIN_OPT},
+ {"help", no_argument, NULL, HELP_OPT},
+ {"mmap", no_argument, NULL, MMAP_OPT},
+ {"line-buffered", no_argument, NULL, LINEBUF_OPT},
+ {"label", required_argument, NULL, LABEL_OPT},
+ {"null", no_argument, NULL, NULL_OPT},
+ {"color", optional_argument, NULL, COLOR_OPT},
+ {"colour", optional_argument, NULL, COLOR_OPT},
+ {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
+ {"include", required_argument, NULL, R_INCLUDE_OPT},
+ {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
+ {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
+ {"after-context", required_argument, NULL, 'A'},
+ {"text", no_argument, NULL, 'a'},
+ {"before-context", required_argument, NULL, 'B'},
+ {"byte-offset", no_argument, NULL, 'b'},
+ {"context", optional_argument, NULL, 'C'},
+ {"count", no_argument, NULL, 'c'},
+ {"devices", required_argument, NULL, 'D'},
+ {"directories", required_argument, NULL, 'd'},
+ {"extended-regexp", no_argument, NULL, 'E'},
+ {"regexp", required_argument, NULL, 'e'},
+ {"fixed-strings", no_argument, NULL, 'F'},
+ {"file", required_argument, NULL, 'f'},
+ {"basic-regexp", no_argument, NULL, 'G'},
+ {"no-filename", no_argument, NULL, 'h'},
+ {"with-filename", no_argument, NULL, 'H'},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"bz2decompress", no_argument, NULL, 'J'},
+ {"files-with-matches", no_argument, NULL, 'l'},
+ {"files-without-match", no_argument, NULL, 'L'},
+ {"max-count", required_argument, NULL, 'm'},
+ {"lzma", no_argument, NULL, 'M'},
+ {"line-number", no_argument, NULL, 'n'},
+ {"only-matching", no_argument, NULL, 'o'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"silent", no_argument, NULL, 'q'},
+ {"recursive", no_argument, NULL, 'r'},
+ {"no-messages", no_argument, NULL, 's'},
+ {"binary", no_argument, NULL, 'U'},
+ {"unix-byte-offsets", no_argument, NULL, 'u'},
+ {"invert-match", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"word-regexp", no_argument, NULL, 'w'},
+ {"line-regexp", no_argument, NULL, 'x'},
+ {"xz", no_argument, NULL, 'X'},
+ {"decompress", no_argument, NULL, 'Z'},
+ {NULL, no_argument, NULL, 0}
+};
+
+/*
+ * Adds a searching pattern to the internal array.
+ */
+static void
+add_pattern(char *pat, size_t len)
+{
+
+ /* Do not add further pattern is we already match everything */
+ if (matchall)
+ return;
+
+ /* Check if we can do a shortcut */
+ if (len == 0) {
+ matchall = true;
+#ifndef WITHOUT_FASTMATCH
+ for (unsigned int i = 0; i < patterns; i++) {
+ free(pattern[i].pat);
+ }
+ pattern = grep_realloc(pattern, sizeof(struct pat));
+ pattern[0].pat = NULL;
+ pattern[0].len = 0;
+ patterns = 1;
+#endif
+ return;
+ }
+ /* Increase size if necessary */
+ if (patterns == pattern_sz) {
+ pattern_sz *= 2;
+ pattern = grep_realloc(pattern, ++pattern_sz *
+ sizeof(struct pat));
+ }
+ if (len > 0 && pat[len - 1] == '\n')
+ --len;
+ /* pat may not be NUL-terminated */
+ pattern[patterns].pat = grep_malloc(len + 1);
+ memcpy(pattern[patterns].pat, pat, len);
+ pattern[patterns].len = len;
+ pattern[patterns].pat[len] = '\0';
+ ++patterns;
+}
+
+/*
+ * Adds a file include/exclude pattern to the internal array.
+ */
+static void
+add_fpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (fpatterns == fpattern_sz) {
+ fpattern_sz *= 2;
+ fpattern = grep_realloc(fpattern, ++fpattern_sz *
+ sizeof(struct epat));
+ }
+ fpattern[fpatterns].pat = grep_strdup(pat);
+ fpattern[fpatterns].mode = mode;
+ ++fpatterns;
+}
+
+/*
+ * Adds a directory include/exclude pattern to the internal array.
+ */
+static void
+add_dpattern(const char *pat, int mode)
+{
+
+ /* Increase size if necessary */
+ if (dpatterns == dpattern_sz) {
+ dpattern_sz *= 2;
+ dpattern = grep_realloc(dpattern, ++dpattern_sz *
+ sizeof(struct epat));
+ }
+ dpattern[dpatterns].pat = grep_strdup(pat);
+ dpattern[dpatterns].mode = mode;
+ ++dpatterns;
+}
+
+/*
+ * Adds search patterns from arguments.
+ */
+static void
+add_arg_patterns(const char *arg)
+{
+ char *argcopy, *pattern;
+
+ argcopy = grep_strdup(arg);
+ while ((pattern = strsep(&argcopy, "\n")) != NULL) {
+ add_pattern(pattern, strlen(pattern));
+ }
+ free(argcopy);
+}
+
+/*
+ * Reads searching patterns from a file and adds them with add_pattern().
+ */
+static void
+read_patterns(const char *fn)
+{
+ struct stat st;
+ FILE *f;
+ char *line;
+ size_t len;
+
+ if ((f = fopen(fn, "r")) == NULL)
+ err(2, "%s", fn);
+ if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
+ fclose(f);
+ return;
+ }
+ while ((line = fgetln(f, &len)) != NULL)
+ add_pattern(line, line[0] == '\n' ? 0 : len);
+ if (ferror(f))
+ err(2, "%s", fn);
+ fclose(f);
+}
+
+static inline const char *
+init_color(const char *d)
+{
+ char *c;
+
+ c = getenv("GREP_COLOR");
+ return (c != NULL && c[0] != '\0' ? c : d);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char **aargv, **eargv, *eopts;
+ char *ep;
+ const char *pn;
+ unsigned long long l;
+ unsigned int aargc, eargc, i;
+ int c, lastc, needpattern, newarg, prevoptind;
+
+ setlocale(LC_ALL, "");
+
+#ifndef WITHOUT_NLS
+ catalog = catopen("grep", NL_CAT_LOCALE);
+#endif
+
+ /* Check what is the program name of the binary. In this
+ way we can have all the funcionalities in one binary
+ without the need of scripting and using ugly hacks. */
+ pn = getprogname();
+ if (pn[0] == 'b' && pn[1] == 'z') {
+ filebehave = FILE_BZIP;
+ pn += 2;
+ } else if (pn[0] == 'x' && pn[1] == 'z') {
+ filebehave = FILE_XZ;
+ pn += 2;
+ } else if (pn[0] == 'l' && pn[1] == 'z') {
+ filebehave = FILE_LZMA;
+ pn += 2;
+ } else if (pn[0] == 'z') {
+ filebehave = FILE_GZIP;
+ pn += 1;
+ }
+ switch (pn[0]) {
+ case 'e':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'f':
+ grepbehave = GREP_FIXED;
+ break;
+ }
+
+ lastc = '\0';
+ newarg = 1;
+ prevoptind = 1;
+ needpattern = 1;
+
+ eopts = getenv("GREP_OPTIONS");
+
+ /* support for extra arguments in GREP_OPTIONS */
+ eargc = 0;
+ if (eopts != NULL && eopts[0] != '\0') {
+ char *str;
+
+ /* make an estimation of how many extra arguments we have */
+ for (unsigned int j = 0; j < strlen(eopts); j++)
+ if (eopts[j] == ' ')
+ eargc++;
+
+ eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
+
+ eargc = 0;
+ /* parse extra arguments */
+ while ((str = strsep(&eopts, " ")) != NULL)
+ if (str[0] != '\0')
+ eargv[eargc++] = grep_strdup(str);
+
+ aargv = (char **)grep_calloc(eargc + argc + 1,
+ sizeof(char *));
+
+ aargv[0] = argv[0];
+ for (i = 0; i < eargc; i++)
+ aargv[i + 1] = eargv[i];
+ for (int j = 1; j < argc; j++, i++)
+ aargv[i + 1] = argv[j];
+
+ aargc = eargc + argc;
+ } else {
+ aargv = argv;
+ aargc = argc;
+ }
+
+ while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
+ -1)) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (newarg || !isdigit(lastc))
+ Aflag = 0;
+ else if (Aflag > LLONG_MAX / 10) {
+ errno = ERANGE;
+ err(2, NULL);
+ }
+ Aflag = Bflag = (Aflag * 10) + (c - '0');
+ break;
+ case 'C':
+ if (optarg == NULL) {
+ Aflag = Bflag = 2;
+ break;
+ }
+ /* FALLTHROUGH */
+ case 'A':
+ /* FALLTHROUGH */
+ case 'B':
+ errno = 0;
+ l = strtoull(optarg, &ep, 10);
+ if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
+ ((errno == EINVAL) && (l == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ if (c == 'A')
+ Aflag = l;
+ else if (c == 'B')
+ Bflag = l;
+ else
+ Aflag = Bflag = l;
+ break;
+ case 'a':
+ binbehave = BINFILE_TEXT;
+ break;
+ case 'b':
+ bflag = true;
+ break;
+ case 'c':
+ cflag = true;
+ break;
+ case 'D':
+ if (strcasecmp(optarg, "skip") == 0)
+ devbehave = DEV_SKIP;
+ else if (strcasecmp(optarg, "read") == 0)
+ devbehave = DEV_READ;
+ else
+ errx(2, getstr(3), "--devices");
+ break;
+ case 'd':
+ if (strcasecmp("recurse", optarg) == 0) {
+ Hflag = true;
+ dirbehave = DIR_RECURSE;
+ } else if (strcasecmp("skip", optarg) == 0)
+ dirbehave = DIR_SKIP;
+ else if (strcasecmp("read", optarg) == 0)
+ dirbehave = DIR_READ;
+ else
+ errx(2, getstr(3), "--directories");
+ break;
+ case 'E':
+ grepbehave = GREP_EXTENDED;
+ break;
+ case 'e':
+ add_arg_patterns(optarg);
+ needpattern = 0;
+ break;
+ case 'F':
+ grepbehave = GREP_FIXED;
+ break;
+ case 'f':
+ read_patterns(optarg);
+ needpattern = 0;
+ break;
+ case 'G':
+ grepbehave = GREP_BASIC;
+ break;
+ case 'H':
+ Hflag = true;
+ break;
+ case 'h':
+ Hflag = false;
+ hflag = true;
+ break;
+ case 'I':
+ binbehave = BINFILE_SKIP;
+ break;
+ case 'i':
+ case 'y':
+ iflag = true;
+ cflags |= REG_ICASE;
+ break;
+ case 'J':
+#ifdef WITHOUT_BZIP2
+ errno = EOPNOTSUPP;
+ err(2, "bzip2 support was disabled at compile-time");
+#endif
+ filebehave = FILE_BZIP;
+ break;
+ case 'L':
+ lflag = false;
+ Lflag = true;
+ break;
+ case 'l':
+ Lflag = false;
+ lflag = true;
+ break;
+ case 'm':
+ mflag = true;
+ errno = 0;
+ mcount = strtoll(optarg, &ep, 10);
+ if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
+ ((errno == EINVAL) && (mcount == 0)))
+ err(2, NULL);
+ else if (ep[0] != '\0') {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ break;
+ case 'M':
+#ifdef WITHOUT_LZMA
+ errno = EOPNOTSUPP;
+ err(2, "lzma support was disabled at compile-time");
+#endif
+ filebehave = FILE_LZMA;
+ break;
+ case 'n':
+ nflag = true;
+ break;
+ case 'O':
+ linkbehave = LINK_EXPLICIT;
+ break;
+ case 'o':
+ oflag = true;
+ cflags &= ~REG_NOSUB;
+ break;
+ case 'p':
+ linkbehave = LINK_SKIP;
+ break;
+ case 'q':
+ qflag = true;
+ break;
+ case 'S':
+ linkbehave = LINK_READ;
+ break;
+ case 'R':
+ case 'r':
+ dirbehave = DIR_RECURSE;
+ Hflag = true;
+ break;
+ case 's':
+ sflag = true;
+ break;
+ case 'U':
+ binbehave = BINFILE_BIN;
+ break;
+ case 'u':
+ case MMAP_OPT:
+#ifndef __APPLE__
+ /* mmap mode is buggy (10789286) */
+ filebehave = FILE_MMAP;
+#endif
+ break;
+ case 'V':
+ printf(getstr(9), getprogname(), VERSION);
+ exit(0);
+ case 'v':
+ vflag = true;
+ break;
+ case 'w':
+ wflag = true;
+ cflags &= ~REG_NOSUB;
+ break;
+ case 'x':
+ xflag = true;
+ cflags &= ~REG_NOSUB;
+ break;
+ case 'X':
+#ifdef WITHOUT_LZMA
+ errno = EOPNOTSUPP;
+ err(2, "xz support was disabled at compile-time");
+#endif
+ filebehave = FILE_XZ;
+ break;
+ case 'Z':
+ filebehave = FILE_GZIP;
+ break;
+ case BIN_OPT:
+ if (strcasecmp("binary", optarg) == 0)
+ binbehave = BINFILE_BIN;
+ else if (strcasecmp("without-match", optarg) == 0)
+ binbehave = BINFILE_SKIP;
+ else if (strcasecmp("text", optarg) == 0)
+ binbehave = BINFILE_TEXT;
+ else
+ errx(2, getstr(3), "--binary-files");
+ break;
+ case COLOR_OPT:
+ color = NULL;
+ if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
+ strcasecmp("tty", optarg) == 0 ||
+ strcasecmp("if-tty", optarg) == 0) {
+ char *term;
+
+ term = getenv("TERM");
+ if (isatty(STDOUT_FILENO) && term != NULL &&
+ strcasecmp(term, "dumb") != 0)
+ color = init_color("01;31");
+ } else if (strcasecmp("always", optarg) == 0 ||
+ strcasecmp("yes", optarg) == 0 ||
+ strcasecmp("force", optarg) == 0) {
+ color = init_color("01;31");
+ } else if (strcasecmp("never", optarg) != 0 &&
+ strcasecmp("none", optarg) != 0 &&
+ strcasecmp("no", optarg) != 0)
+ errx(2, getstr(3), "--color");
+ cflags &= ~REG_NOSUB;
+ break;
+ case LABEL_OPT:
+ label = optarg;
+ break;
+ case LINEBUF_OPT:
+ lbflag = true;
+ break;
+ case NULL_OPT:
+ nullflag = true;
+ break;
+ case R_INCLUDE_OPT:
+ finclude = true;
+ add_fpattern(optarg, INCL_PAT);
+ break;
+ case R_EXCLUDE_OPT:
+ fexclude = true;
+ add_fpattern(optarg, EXCL_PAT);
+ break;
+ case R_DINCLUDE_OPT:
+ dinclude = true;
+ add_dpattern(optarg, INCL_PAT);
+ break;
+ case R_DEXCLUDE_OPT:
+ dexclude = true;
+ add_dpattern(optarg, EXCL_PAT);
+ break;
+ case HELP_OPT:
+ default:
+ usage();
+ }
+ lastc = c;
+ newarg = optind != prevoptind;
+ prevoptind = optind;
+ }
+ aargc -= optind;
+ aargv += optind;
+
+ /* Empty pattern file matches nothing */
+#ifndef WITHOUT_FASTMATCH
+ if (!needpattern && (patterns == 0))
+#else
+ if (!needpattern && (patterns == 0) && !matchall)
+#endif
+ exit(1);
+
+ /* Fail if we don't have any pattern */
+ if (aargc == 0 && needpattern)
+ usage();
+
+ /* Process patterns from command line */
+ if (aargc != 0 && needpattern) {
+ add_arg_patterns(*aargv);
+ --aargc;
+ ++aargv;
+ }
+
+ switch (grepbehave) {
+ case GREP_BASIC:
+#ifdef __APPLE__
+ cflags |= REG_ENHANCED;
+#endif
+ break;
+ case GREP_FIXED:
+ /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
+ cflags |= 0020;
+ break;
+ case GREP_EXTENDED:
+ cflags |= REG_EXTENDED;
+#ifdef __APPLE__
+ cflags |= REG_ENHANCED;
+#endif
+ break;
+ default:
+ /* NOTREACHED */
+ usage();
+ }
+
+#ifndef WITHOUT_FASTMATCH
+ fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
+#endif
+ r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
+
+ /* Check if cheating is allowed (always is for fgrep). */
+ for (i = 0; i < patterns; ++i) {
+#ifndef WITHOUT_FASTMATCH
+ if (fastncomp(&fg_pattern[i], pattern[i].pat,
+ pattern[i].len, cflags) != 0) {
+#endif
+ /* Fall back to full regex library */
+ c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
+ if (c != 0) {
+ regerror(c, &r_pattern[i], re_error,
+ RE_ERROR_BUF);
+ errx(2, "%s", re_error);
+ }
+#ifndef WITHOUT_FASTMATCH
+ }
+#endif
+ }
+
+ if (lbflag)
+ setlinebuf(stdout);
+
+ if ((aargc == 0 || aargc == 1) && !Hflag)
+ hflag = true;
+
+ if (aargc == 0)
+ exit(!procfile("-"));
+
+ if (dirbehave == DIR_RECURSE)
+ c = grep_tree(aargv);
+ else
+ for (c = 0; aargc--; ++aargv) {
+ if ((finclude || fexclude) && !file_matching(*aargv))
+ continue;
+ c+= procfile(*aargv);
+ }
+
+#ifndef WITHOUT_NLS
+ catclose(catalog);
+#endif
+
+ /* Find out the correct return value according to the
+ results and the command line option. */
+ exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
+}
diff --git a/text_cmds/grep/grep.h b/text_cmds/grep/grep.h
new file mode 100644
index 0000000..c46e172
--- /dev/null
+++ b/text_cmds/grep/grep.h
@@ -0,0 +1,161 @@
+/* $NetBSD: grep.h,v 1.5 2011/02/27 17:33:37 joerg Exp $ */
+/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
+/* $FreeBSD: src/usr.bin/grep/grep.h,v 1.10 2011/12/07 12:25:28 gabor Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <bzlib.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <zlib.h>
+
+#ifndef WITHOUT_FASTMATCH
+#include "fastmatch.h"
+#endif
+
+#ifdef WITHOUT_NLS
+#define getstr(n) errstr[n]
+#else
+#include <nl_types.h>
+
+extern nl_catd catalog;
+#define getstr(n) catgets(catalog, 1, n, errstr[n])
+#endif
+
+extern const char *errstr[];
+
+#define VERSION "2.5.1-FreeBSD"
+
+#define GREP_FIXED 0
+#define GREP_BASIC 1
+#define GREP_EXTENDED 2
+
+#define BINFILE_BIN 0
+#define BINFILE_SKIP 1
+#define BINFILE_TEXT 2
+
+#define FILE_STDIO 0
+#define FILE_MMAP 1
+#define FILE_GZIP 2
+#define FILE_BZIP 3
+#define FILE_XZ 4
+#define FILE_LZMA 5
+
+#define DIR_READ 0
+#define DIR_SKIP 1
+#define DIR_RECURSE 2
+
+#define DEV_READ 0
+#define DEV_SKIP 1
+
+#define LINK_READ 0
+#define LINK_EXPLICIT 1
+#define LINK_SKIP 2
+
+#define EXCL_PAT 0
+#define INCL_PAT 1
+
+#ifdef __APPLE__
+#define MIN_LINE_MATCHES 32
+#define MAX_LINE_MATCHES 1073741824
+#else
+#define MAX_LINE_MATCHES 32
+#endif
+
+struct file {
+ int fd;
+ bool binary;
+};
+
+struct str {
+ off_t off;
+ size_t len;
+ char *dat;
+ char *file;
+ int line_no;
+};
+
+struct pat {
+ char *pat;
+ int len;
+};
+
+struct epat {
+ char *pat;
+ int mode;
+};
+
+/* Flags passed to regcomp() and regexec() */
+extern int cflags, eflags;
+
+/* Command line flags */
+extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
+ bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
+ qflag, sflag, vflag, wflag, xflag;
+extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag;
+extern unsigned long long Aflag, Bflag;
+extern long long mcount;
+extern char *label;
+extern const char *color;
+extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
+
+extern bool file_err, first, matchall, prev;
+extern int tail;
+extern unsigned int dpatterns, fpatterns, patterns;
+extern struct pat *pattern;
+extern struct epat *dpattern, *fpattern;
+extern regex_t *er_pattern, *r_pattern;
+#ifndef WITHOUT_FASTMATCH
+extern fastmatch_t *fg_pattern;
+#endif
+
+/* For regex errors */
+#define RE_ERROR_BUF 512
+extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
+
+/* util.c */
+bool file_matching(const char *fname);
+int procfile(const char *fn);
+int grep_tree(char **argv);
+void *grep_malloc(size_t size);
+void *grep_calloc(size_t nmemb, size_t size);
+void *grep_realloc(void *ptr, size_t size);
+char *grep_strdup(const char *str);
+void printline(struct str *line, int sep, regmatch_t *matches, int m);
+
+/* queue.c */
+void enqueue(struct str *x);
+void printqueue(void);
+void clearqueue(void);
+
+/* file.c */
+void grep_close(struct file *f);
+struct file *grep_open(const char *path);
+char *grep_fgetln(struct file *f, size_t *len);
diff --git a/text_cmds/grep/queue.c b/text_cmds/grep/queue.c
new file mode 100644
index 0000000..474b4c5
--- /dev/null
+++ b/text_cmds/grep/queue.c
@@ -0,0 +1,107 @@
+/* $NetBSD: queue.c,v 1.2 2011/02/16 01:31:33 joerg Exp $ */
+/* $FreeBSD: src/usr.bin/grep/queue.c,v 1.5 2011/04/07 13:03:35 gabor Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * A really poor man's queue. It does only what it has to and gets out of
+ * Dodge. It is used in place of <sys/queue.h> to get a better performance.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/grep/queue.c,v 1.5 2011/04/07 13:03:35 gabor Exp $");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "grep.h"
+
+struct qentry {
+ STAILQ_ENTRY(qentry) list;
+ struct str data;
+};
+
+static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
+static unsigned long long count;
+
+static struct qentry *dequeue(void);
+
+void
+enqueue(struct str *x)
+{
+ struct qentry *item;
+
+ item = grep_malloc(sizeof(struct qentry));
+ item->data.dat = grep_malloc(sizeof(char) * x->len);
+ item->data.len = x->len;
+ item->data.line_no = x->line_no;
+ item->data.off = x->off;
+ memcpy(item->data.dat, x->dat, x->len);
+ item->data.file = x->file;
+
+ STAILQ_INSERT_TAIL(&queue, item, list);
+
+ if (++count > Bflag)
+ free(dequeue());
+}
+
+static struct qentry *
+dequeue(void)
+{
+ struct qentry *item;
+
+ item = STAILQ_FIRST(&queue);
+ if (item == NULL)
+ return (NULL);
+
+ STAILQ_REMOVE_HEAD(&queue, list);
+ --count;
+ return (item);
+}
+
+void
+printqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL) {
+ printline(&item->data, '-', (regmatch_t *)NULL, 0);
+ free(item);
+ }
+}
+
+void
+clearqueue(void)
+{
+ struct qentry *item;
+
+ while ((item = dequeue()) != NULL)
+ free(item);
+}
diff --git a/text_cmds/grep/util.c b/text_cmds/grep/util.c
new file mode 100644
index 0000000..fa42d55
--- /dev/null
+++ b/text_cmds/grep/util.c
@@ -0,0 +1,599 @@
+/* $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $ */
+/* $FreeBSD: src/usr.bin/grep/util.c,v 1.19 2011/12/07 12:25:28 gabor Exp $ */
+/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
+ * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/grep/util.c,v 1.19 2011/12/07 12:25:28 gabor Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef __APPLE__
+#include <sys/param.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <libgen.h>
+#ifdef __APPLE__
+#include <locale.h>
+#endif /* __APPLE__ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifndef WITHOUT_FASTMATCH
+#include "fastmatch.h"
+#endif
+#include "grep.h"
+
+static int linesqueued;
+static int procline(struct str *l, int);
+
+bool
+file_matching(const char *fname)
+{
+ char *fname_base;
+ bool ret;
+
+ ret = finclude ? false : true;
+#ifdef __APPLE__
+ fname_base = basename((char *)fname);
+#else
+ fname_base = basename(fname);
+#endif
+
+ for (unsigned int i = 0; i < fpatterns; ++i) {
+ if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
+ fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
+ if (fpattern[i].mode == EXCL_PAT)
+ return (false);
+ else
+ ret = true;
+ }
+ }
+ return (ret);
+}
+
+static inline bool
+dir_matching(const char *dname)
+{
+ bool ret;
+
+ ret = dinclude ? false : true;
+
+ for (unsigned int i = 0; i < dpatterns; ++i) {
+ if (dname != NULL &&
+ fnmatch(dpattern[i].pat, dname, 0) == 0) {
+ if (dpattern[i].mode == EXCL_PAT)
+ return (false);
+ else
+ ret = true;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Processes a directory when a recursive search is performed with
+ * the -R option. Each appropriate file is passed to procfile().
+ */
+int
+grep_tree(char **argv)
+{
+ FTS *fts;
+ FTSENT *p;
+ int c, fts_flags;
+ bool ok;
+
+ c = fts_flags = 0;
+
+ switch(linkbehave) {
+ case LINK_EXPLICIT:
+ fts_flags = FTS_COMFOLLOW;
+ break;
+ case LINK_SKIP:
+ fts_flags = FTS_PHYSICAL;
+ break;
+ default:
+ fts_flags = FTS_LOGICAL;
+
+ }
+
+ fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
+
+ if (!(fts = fts_open(argv, fts_flags, NULL)))
+ err(2, "fts_open");
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_DNR:
+ /* FALLTHROUGH */
+ case FTS_ERR:
+ file_err = true;
+ if(!sflag)
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ case FTS_D:
+ /* FALLTHROUGH */
+ case FTS_DP:
+ if (dexclude || dinclude)
+ if (!dir_matching(p->fts_name) ||
+ !dir_matching(p->fts_path))
+ fts_set(fts, p, FTS_SKIP);
+ break;
+ case FTS_DC:
+ /* Print a warning for recursive directory loop */
+ warnx("warning: %s: recursive directory loop",
+ p->fts_path);
+ break;
+ default:
+ /* Check for file exclusion/inclusion */
+ ok = true;
+ if (fexclude || finclude)
+ ok &= file_matching(p->fts_path);
+
+ if (ok)
+ c += procfile(p->fts_path);
+ break;
+ }
+ }
+
+ fts_close(fts);
+ return (c);
+}
+
+/*
+ * Opens a file and processes it. Each file is processed line-by-line
+ * passing the lines to procline().
+ */
+int
+procfile(const char *fn)
+{
+ struct file *f;
+ struct stat sb;
+ struct str ln;
+ mode_t s;
+ int c, t;
+
+ if (mflag && (mcount <= 0))
+ return (0);
+
+ if (strcmp(fn, "-") == 0) {
+#ifdef __APPLE__
+ /* 4053512, 10290183 */
+ if (dirbehave == DIR_RECURSE && isatty(STDIN_FILENO)) {
+ warnx("warning: recursive search of stdin");
+ }
+#endif
+ fn = label != NULL ? label : getstr(1);
+ f = grep_open(NULL);
+ } else {
+ if (!stat(fn, &sb)) {
+ /* Check if we need to process the file */
+ s = sb.st_mode & S_IFMT;
+ if (s == S_IFDIR && dirbehave == DIR_SKIP)
+ return (0);
+ if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
+ || s == S_IFSOCK) && devbehave == DEV_SKIP)
+ return (0);
+ }
+ f = grep_open(fn);
+ }
+ if (f == NULL) {
+ file_err = true;
+ if (!sflag)
+ warn("%s", fn);
+ return (0);
+ }
+
+ ln.file = grep_malloc(strlen(fn) + 1);
+ strcpy(ln.file, fn);
+ ln.line_no = 0;
+ ln.len = 0;
+ linesqueued = 0;
+ tail = 0;
+ ln.off = -1;
+
+ for (c = 0; c == 0 || !(lflag || qflag); ) {
+ ln.off += ln.len + 1;
+ if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) {
+ if (ln.line_no == 0 && matchall)
+ exit(0);
+ else
+ break;
+ }
+ if (ln.len > 0 && ln.dat[ln.len - 1] == '\n')
+ --ln.len;
+ ln.line_no++;
+
+ /* Return if we need to skip a binary file */
+ if (f->binary && binbehave == BINFILE_SKIP) {
+ grep_close(f);
+ free(ln.file);
+ free(f);
+ return (0);
+ }
+ /* Process the file line-by-line */
+ if ((t = procline(&ln, f->binary)) == 0 && Bflag > 0) {
+ enqueue(&ln);
+ linesqueued++;
+ }
+ c += t;
+ if (mflag && mcount <= 0)
+ break;
+ }
+ if (Bflag > 0)
+ clearqueue();
+ grep_close(f);
+
+#ifdef __APPLE__
+ /* 10680370 */
+ if (cflag && !qflag) {
+#else
+ if (cflag) {
+#endif
+ if (!hflag)
+ printf("%s:", ln.file);
+ printf("%u\n", c);
+ }
+ if (lflag && !qflag && c != 0)
+ printf("%s%c", fn, nullflag ? 0 : '\n');
+ if (Lflag && !qflag && c == 0)
+ printf("%s%c", fn, nullflag ? 0 : '\n');
+ if (c && !cflag && !lflag && !Lflag &&
+ binbehave == BINFILE_BIN && f->binary && !qflag)
+ printf(getstr(8), fn);
+
+ free(ln.file);
+ free(f);
+ return (c);
+}
+
+#ifdef __APPLE__
+static int
+mbtowc_reverse(wchar_t *pwc, const char *s, size_t n)
+{
+ int result;
+ size_t i;
+
+ result = -1;
+ for (i = 1; i <= n; i++) {
+ result = mbtowc(pwc, s - i, i);
+ if (result != -1) {
+ break;
+ }
+ }
+
+ return result;
+}
+#endif
+
+#define iswword(x) (iswalnum((x)) || (x) == L'_')
+
+/*
+ * Processes a line comparing it with the specified patterns. Each pattern
+ * is looped to be compared along with the full string, saving each and every
+ * match, which is necessary to colorize the output and to count the
+ * matches. The matching lines are passed to printline() to display the
+ * appropriate output.
+ */
+static int
+procline(struct str *l, int nottext)
+{
+#ifdef __APPLE__
+ int nmatches;
+ regmatch_t *matches;
+#else
+ regmatch_t matches[MAX_LINE_MATCHES];
+#endif
+ regmatch_t pmatch;
+ size_t st = 0;
+ unsigned int i;
+ int c = 0, m = 0, r = 0;
+
+#ifdef __APPLE__
+ nmatches = MIN_LINE_MATCHES;
+ matches = grep_malloc(nmatches * sizeof(regmatch_t));
+#endif
+
+ /* Loop to process the whole line */
+ do {
+#ifdef WITHOUT_FASTMATCH
+ if (matchall) {
+ c = !vflag;
+ break;
+ }
+#endif
+
+ pmatch.rm_so = st;
+ pmatch.rm_eo = l->len;
+
+ /* Loop to compare with all the patterns */
+ for (i = 0; i < patterns; i++) {
+#ifdef __APPLE__
+ /* 10462853: Treat binary files as binary. */
+ if (nottext) {
+ setlocale(LC_ALL, "C");
+ }
+#endif /* __APPLE__ */
+#ifndef WITHOUT_FASTMATCH
+ if (fg_pattern[i].pattern)
+ r = fastexec(&fg_pattern[i],
+ l->dat, 1, &pmatch, eflags);
+ else
+#endif
+ r = regexec(&r_pattern[i], l->dat, 1,
+ &pmatch, eflags);
+#ifdef __APPLE__
+ if (nottext) {
+ setlocale(LC_ALL, "");
+ }
+#endif /* __APPLE__ */
+ r = (r == 0) ? 0 : REG_NOMATCH;
+ st = (cflags & REG_NOSUB)
+ ? (size_t)l->len
+ : (size_t)pmatch.rm_eo;
+ if (r == REG_NOMATCH)
+ continue;
+ /* Check for full match */
+ if (r == 0 && xflag)
+ if (pmatch.rm_so != 0 ||
+ (size_t)pmatch.rm_eo != l->len)
+ r = REG_NOMATCH;
+ /* Check for whole word match */
+#ifndef WITHOUT_FASTMATCH
+ if (r == 0 && (wflag || fg_pattern[i].word)) {
+#else
+ if (r == 0 && (wflag)) {
+#endif
+ wchar_t wbegin, wend;
+
+ wbegin = wend = L' ';
+ if (pmatch.rm_so != 0 &&
+#ifdef __APPLE__
+ mbtowc_reverse(&wbegin, &l->dat[pmatch.rm_so], MAX(MB_CUR_MAX, pmatch.rm_so)) == -1)
+#else
+ sscanf(&l->dat[pmatch.rm_so - 1],
+ "%lc", &wbegin) != 1)
+#endif
+ r = REG_NOMATCH;
+ else if ((size_t)pmatch.rm_eo !=
+ l->len &&
+#ifdef __APPLE__
+ mbtowc(&wend, &l->dat[pmatch.rm_eo], MAX(MB_CUR_MAX, l->len - (size_t)pmatch.rm_eo)) == -1)
+#else
+ sscanf(&l->dat[pmatch.rm_eo],
+ "%lc", &wend) != 1)
+#endif
+ r = REG_NOMATCH;
+ else if (iswword(wbegin) ||
+ iswword(wend))
+ r = REG_NOMATCH;
+ }
+ if (r == 0) {
+ if (m == 0)
+ c++;
+#ifdef __APPLE__
+ if (m < MAX_LINE_MATCHES) {
+ if (m >= nmatches) {
+ nmatches += MIN_LINE_MATCHES;
+ matches = grep_realloc(matches, nmatches * sizeof(regmatch_t));
+ }
+ matches[m++] = pmatch;
+ }
+#else
+ if (m < MAX_LINE_MATCHES)
+ matches[m++] = pmatch;
+#endif
+ /* matches - skip further patterns */
+ if ((color == NULL && !oflag) ||
+ qflag || lflag)
+ break;
+ }
+ }
+
+ if (vflag) {
+ c = !c;
+ break;
+ }
+
+ /* One pass if we are not recording matches */
+#ifdef __APPLE__
+ /* 10593340: If -w didn't match, keep going. */
+ if (!(wflag && r == REG_NOMATCH) && ((color == NULL && !oflag) || qflag || lflag))
+#else
+ if ((color == NULL && !oflag) || qflag || lflag)
+#endif
+ break;
+
+ if (st == (size_t)pmatch.rm_so)
+ break; /* No matches */
+ } while (st < l->len);
+
+
+ /* Count the matches if we have a match limit */
+ if (mflag)
+ mcount -= c;
+
+ if (c && binbehave == BINFILE_BIN && nottext) {
+#ifdef __APPLE__
+ free(matches);
+#endif
+ return (c); /* Binary file */
+ }
+
+ /* Dealing with the context */
+ if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
+ if (c) {
+ if (!first && !prev && !tail && Aflag)
+ printf("--\n");
+ tail = Aflag;
+ if (Bflag > 0) {
+ if (!first && !prev)
+ printf("--\n");
+ printqueue();
+ }
+ linesqueued = 0;
+ printline(l, ':', matches, m);
+ } else {
+ printline(l, '-', matches, m);
+ tail--;
+ }
+ }
+
+ if (c) {
+ prev = true;
+ first = false;
+ } else
+ prev = false;
+
+#ifdef __APPLE__
+ free(matches);
+#endif
+ return (c);
+}
+
+/*
+ * Safe malloc() for internal use.
+ */
+void *
+grep_malloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(2, "malloc");
+ return (ptr);
+}
+
+/*
+ * Safe calloc() for internal use.
+ */
+void *
+grep_calloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if ((ptr = calloc(nmemb, size)) == NULL)
+ err(2, "calloc");
+ return (ptr);
+}
+
+/*
+ * Safe realloc() for internal use.
+ */
+void *
+grep_realloc(void *ptr, size_t size)
+{
+
+ if ((ptr = realloc(ptr, size)) == NULL)
+ err(2, "realloc");
+ return (ptr);
+}
+
+/*
+ * Safe strdup() for internal use.
+ */
+char *
+grep_strdup(const char *str)
+{
+ char *ret;
+
+ if ((ret = strdup(str)) == NULL)
+ err(2, "strdup");
+ return (ret);
+}
+
+/*
+ * Prints a matching line according to the command line options.
+ */
+void
+printline(struct str *line, int sep, regmatch_t *matches, int m)
+{
+ size_t a = 0;
+ int i, n = 0;
+
+ if (!hflag) {
+ if (!nullflag) {
+ fputs(line->file, stdout);
+ ++n;
+ } else {
+ printf("%s", line->file);
+ putchar(0);
+ }
+ }
+ if (nflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%d", line->line_no);
+ ++n;
+ }
+ if (bflag) {
+ if (n > 0)
+ putchar(sep);
+ printf("%lld", (long long)line->off);
+ ++n;
+ }
+ if (n)
+ putchar(sep);
+ /* --color and -o */
+ if ((oflag || color) && m > 0) {
+ for (i = 0; i < m; i++) {
+ if (!oflag)
+ fwrite(line->dat + a, matches[i].rm_so - a, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[%sm\33[K", color);
+
+ fwrite(line->dat + matches[i].rm_so,
+ matches[i].rm_eo - matches[i].rm_so, 1,
+ stdout);
+ if (color)
+ fprintf(stdout, "\33[m\33[K");
+ a = matches[i].rm_eo;
+ if (oflag)
+ putchar('\n');
+ }
+ if (!oflag) {
+ if (line->len - a > 0)
+ fwrite(line->dat + a, line->len - a, 1, stdout);
+ putchar('\n');
+ }
+ } else {
+ fwrite(line->dat, line->len, 1, stdout);
+ putchar('\n');
+ }
+}
diff --git a/text_cmds/head/head.1 b/text_cmds/head/head.1
new file mode 100644
index 0000000..f0021c9
--- /dev/null
+++ b/text_cmds/head/head.1
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)head.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/head/head.1,v 1.13 2005/01/17 07:44:18 ru Exp $
+.\"
+.Dd June 6, 1993
+.Dt HEAD 1
+.Os
+.Sh NAME
+.Nm head
+.Nd display first lines of a file
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar count | Fl c Ar bytes
+.Op Ar
+.Sh DESCRIPTION
+This filter displays the first
+.Ar count
+lines or
+.Ar bytes
+of each of the specified files, or of the standard input if no
+files are specified.
+If
+.Ar count
+is omitted it defaults to 10.
+.Pp
+If more than a single file is specified, each file is preceded by a
+header consisting of the string
+.Dq ==> XXX <==
+where
+.Dq XXX
+is the name of the file.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr tail 1
+.Sh HISTORY
+The
+.Nm
+command appeared in PWB UNIX.
diff --git a/text_cmds/head/head.c b/text_cmds/head/head.c
new file mode 100644
index 0000000..d798e78
--- /dev/null
+++ b/text_cmds/head/head.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 1980, 1987, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)head.c 8.2 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/head/head.c,v 1.20 2007/01/11 20:23:01 brooks Exp $");
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * head - give the first few lines of a stream or of each of a set of files
+ *
+ * Bill Joy UCB August 24, 1977
+ */
+
+static void head(FILE *, int);
+static void head_bytes(FILE *, off_t);
+static void obsolete(char *[]);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ FILE *fp;
+ int first, linecnt = -1, eval = 0;
+ off_t bytecnt = -1;
+ char *ep;
+
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "n:c:")) != -1)
+ switch(ch) {
+ case 'c':
+ bytecnt = strtoimax(optarg, &ep, 10);
+ if (*ep || bytecnt <= 0)
+ errx(1, "illegal byte count -- %s", optarg);
+ break;
+ case 'n':
+ linecnt = strtol(optarg, &ep, 10);
+ if (*ep || linecnt <= 0)
+ errx(1, "illegal line count -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (linecnt != -1 && bytecnt != -1)
+ errx(1, "can't combine line and byte counts");
+ if (linecnt == -1 )
+ linecnt = 10;
+ if (*argv) {
+ for (first = 1; *argv; ++argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ warn("%s", *argv);
+ eval = 1;
+ continue;
+ }
+ if (argc > 1) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", *argv);
+ first = 0;
+ }
+ if (bytecnt == -1)
+ head(fp, linecnt);
+ else
+ head_bytes(fp, bytecnt);
+ if (ferror(fp)) {
+ warnx("Error reading %s", *argv);
+ eval = 1;
+ }
+ (void)fclose(fp);
+ }
+ } else {
+ if (bytecnt == -1) {
+ head(stdin, linecnt);
+ } else {
+ head_bytes(stdin, bytecnt);
+ }
+ if (ferror(stdin)) {
+ warnx("Error reading stdin");
+ eval = 1;
+ }
+ }
+
+ exit(eval);
+}
+
+static void
+head(FILE *fp, int cnt)
+{
+ char *cp;
+ size_t error, readlen;
+
+ while (cnt && (cp = fgetln(fp, &readlen)) != NULL) {
+ error = fwrite(cp, sizeof(char), readlen, stdout);
+ if (error != readlen)
+ err(1, "stdout");
+ cnt--;
+ }
+
+ fflush(fp);
+}
+
+static void
+head_bytes(FILE *fp, off_t cnt)
+{
+ char buf[4096];
+ size_t readlen;
+
+ while (cnt) {
+ if ((uintmax_t)cnt < sizeof(buf))
+ readlen = cnt;
+ else
+ readlen = sizeof(buf);
+ readlen = fread(buf, sizeof(char), readlen, fp);
+ if (readlen == 0)
+ break;
+ if (fwrite(buf, sizeof(char), readlen, stdout) != readlen)
+ err(1, "stdout");
+ cnt -= readlen;
+ }
+}
+
+static void
+obsolete(char *argv[])
+{
+ char *ap;
+
+ while ((ap = *++argv)) {
+ /* Return if "--" or not "-[0-9]*". */
+ if (ap[0] != '-' || ap[1] == '-' || !isdigit(ap[1]))
+ return;
+ if ((ap = malloc(strlen(*argv) + 2)) == NULL)
+ err(1, NULL);
+ ap[0] = '-';
+ ap[1] = 'n';
+ (void)strcpy(ap + 2, *argv + 1);
+ *argv = ap;
+ }
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: head [-n lines | -c bytes] [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/join/join.1 b/text_cmds/join/join.1
new file mode 100644
index 0000000..0ab7a93
--- /dev/null
+++ b/text_cmds/join/join.1
@@ -0,0 +1,235 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)join.1 8.3 (Berkeley) 4/28/95
+.\" $FreeBSD: src/usr.bin/join/join.1,v 1.20 2005/02/13 22:25:23 ru Exp $
+.\"
+.Dd July 5, 2004
+.Dt JOIN 1
+.Os
+.Sh NAME
+.Nm join
+.Nd relational database operator
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl a Ar file_number | Fl v Ar file_number
+.Oc
+.Op Fl e Ar string
+.Op Fl o Ar list
+.Bk -words
+.Ek
+.Op Fl t Ar char
+.Op Fl \&1 Ar field
+.Op Fl \&2 Ar field
+.Ar file1
+.Ar file2
+.Sh DESCRIPTION
+The
+.Nm
+utility performs an
+.Dq equality join
+on the specified files
+and writes the result to the standard output.
+The
+.Dq join field
+is the field in each file by which the files are compared.
+The first field in each line is used by default.
+There is one line in the output for each pair of lines in
+.Ar file1
+and
+.Ar file2
+which have identical join fields.
+Each output line consists of the join field, the remaining fields from
+.Ar file1
+and then the remaining fields from
+.Ar file2 .
+.Pp
+The default field separators are tab and space characters.
+In this case, multiple tabs and spaces count as a single field separator,
+and leading tabs and spaces are ignored.
+The default output field separator is a single space character.
+.Pp
+Many of the options use file and field numbers.
+Both file numbers and field numbers are 1 based, i.e., the first file on
+the command line is file number 1 and the first field is field number 1.
+The following options are available:
+.Bl -tag -width indent
+.It Fl a Ar file_number
+In addition to the default output, produce a line for each unpairable
+line in file
+.Ar file_number .
+.It Fl e Ar string
+Replace empty output fields with
+.Ar string .
+.It Fl o Ar list
+The
+.Fl o
+option specifies the fields that will be output from each file for
+each line with matching join fields.
+Each element of
+.Ar list
+has the either the form
+.Ql file_number.field ,
+where
+.Ar file_number
+is a file number and
+.Ar field
+is a field number, or the form
+.Ql 0
+.Pq zero ,
+representing the join field.
+The elements of list must be either comma
+.Pq Ql \&,
+or whitespace separated.
+(The latter requires quoting to protect it from the shell, or, a simpler
+approach is to use multiple
+.Fl o
+options.)
+.It Fl t Ar char
+Use character
+.Ar char
+as a field delimiter for both input and output.
+Every occurrence of
+.Ar char
+in a line is significant.
+.It Fl v Ar file_number
+Do not display the default output, but display a line for each unpairable
+line in file
+.Ar file_number .
+The options
+.Fl v Ar 1
+and
+.Fl v Ar 2
+may be specified at the same time.
+.It Fl 1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl 2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.El
+.Pp
+When the default field delimiter characters are used, the files to be joined
+should be ordered in the collating sequence of
+.Xr sort 1 ,
+using the
+.Fl b
+option, on the fields on which they are to be joined, otherwise
+.Nm
+may not report all field matches.
+When the field delimiter characters are specified by the
+.Fl t
+option, the collating sequence should be the same as
+.Xr sort 1
+without the
+.Fl b
+option.
+.Pp
+If one of the arguments
+.Ar file1
+or
+.Ar file2
+is
+.Dq - ,
+the standard input is used.
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+For compatibility with historic versions of
+.Nm ,
+the following options are available:
+.Bl -tag -width indent
+.It Fl a
+In addition to the default output, produce a line for each unpairable line
+in both file 1 and file 2.
+.It Fl j1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl j2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.It Fl j Ar field
+Join on the
+.Ar field Ns 'th
+field of both file 1 and file 2.
+.It Fl o Ar list ...
+Historical implementations of
+.Nm
+permitted multiple arguments to the
+.Fl o
+option.
+These arguments were of the form
+.Ql file_number.field_number
+as described
+for the current
+.Fl o
+option.
+This has obvious difficulties in the presence of files named
+.Ql 1.2 .
+.El
+.Pp
+These options are available only
+so historic shell scripts do not require modification.
+They should not be used in new code.
+.Sh LEGACY DESCRIPTION
+The
+.Fl e
+option causes a specified string to be substituted into empty fields,
+even if they are in the middle of a line.
+In legacy mode,
+the substitution only takes place at the end of a line.
+.Pp
+Only documented options are allowed.
+In legacy mode,
+some obsolete options are re-written into current options.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr comm 1 ,
+.Xr paste 1 ,
+.Xr sort 1 ,
+.Xr uniq 1 ,
+.Xr compat 5
+.Sh STANDARDS
+The
+.Nm
+command conforms to
+.St -p1003.1-2001 .
diff --git a/text_cmds/join/join.c b/text_cmds/join/join.c
new file mode 100644
index 0000000..45413e6
--- /dev/null
+++ b/text_cmds/join/join.c
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Steve Hayman of the Computer Science Department, Indiana University,
+ * Michiro Hikida and David Goodenough.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)join.c 8.6 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/join/join.c,v 1.20 2004/08/26 06:28:05 maxim Exp $");
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <sysexits.h>
+
+#ifdef __APPLE__
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
+/*
+ * There's a structure per input file which encapsulates the state of the
+ * file. We repeatedly read lines from each file until we've read in all
+ * the consecutive lines from the file with a common join field. Then we
+ * compare the set of lines with an equivalent set from the other file.
+ */
+typedef struct {
+ char *line; /* line */
+ u_long linealloc; /* line allocated count */
+ char **fields; /* line field(s) */
+ u_long fieldcnt; /* line field(s) count */
+ u_long fieldalloc; /* line field(s) allocated count */
+} LINE;
+
+typedef struct {
+ FILE *fp; /* file descriptor */
+ u_long joinf; /* join field (-1, -2, -j) */
+ int unpair; /* output unpairable lines (-a) */
+ u_long number; /* 1 for file 1, 2 for file 2 */
+
+ LINE *set; /* set of lines with same field */
+ int pushbool; /* if pushback is set */
+ u_long pushback; /* line on the stack */
+ u_long setcnt; /* set count */
+ u_long setalloc; /* set allocated count */
+} INPUT;
+INPUT input1 = { NULL, 0, 0, 1, NULL, 0, 0, 0, 0 },
+ input2 = { NULL, 0, 0, 2, NULL, 0, 0, 0, 0 };
+
+typedef struct {
+ u_long filenum; /* file number */
+ u_long fieldno; /* field number */
+} OLIST;
+OLIST *olist; /* output field list */
+u_long olistcnt; /* output field list count */
+u_long olistalloc; /* output field allocated count */
+
+int joinout = 1; /* show lines with matched join fields (-v) */
+int needsep; /* need separator character */
+int spans = 1; /* span multiple delimiters (-t) */
+char *empty; /* empty field replacement string (-e) */
+static wchar_t default_tabchar[] = L" \t";
+wchar_t *tabchar = default_tabchar;/* delimiter characters (-t) */
+
+int cmp(LINE *, u_long, LINE *, u_long);
+void fieldarg(char *);
+void joinlines(INPUT *, INPUT *);
+int mbscoll(const char *, const char *);
+char *mbssep(char **, const wchar_t *);
+void obsolete(char **);
+void outfield(LINE *, u_long, int);
+void outoneline(INPUT *, LINE *);
+void outtwoline(INPUT *, LINE *, INPUT *, LINE *);
+void slurp(INPUT *);
+wchar_t *towcs(const char *);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ INPUT *F1, *F2;
+ int aflag, ch, cval, vflag;
+ char *end;
+
+ setlocale(LC_ALL, "");
+
+ F1 = &input1;
+ F2 = &input2;
+
+ aflag = vflag = 0;
+ if (!COMPAT_MODE("bin/join", "Unix2003"))
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != -1) {
+ switch (ch) {
+ case '\01': /* See comment in obsolete(). */
+ aflag = 1;
+ F1->unpair = F2->unpair = 1;
+ break;
+ case '1':
+ if ((F1->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-1 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ break;
+ case '2':
+ if ((F2->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-2 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F2->joinf;
+ break;
+ case 'a':
+ aflag = 1;
+ switch(strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-a option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case 'e':
+ empty = optarg;
+ break;
+ case 'j':
+ if ((F1->joinf = F2->joinf =
+ strtol(optarg, &end, 10)) < 1)
+ errx(1, "-j option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ --F2->joinf;
+ break;
+ case 'o':
+ fieldarg(optarg);
+ break;
+ case 't':
+ spans = 0;
+ if (mbrtowc(&tabchar[0], optarg, MB_LEN_MAX, NULL) !=
+ strlen(optarg))
+ errx(1, "illegal tab character specification");
+ tabchar[1] = L'\0';
+ break;
+ case 'v':
+ vflag = 1;
+ joinout = 0;
+ switch (strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-v option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag && vflag)
+ errx(1, "the -a and -v options are mutually exclusive");
+
+ if (argc != 2 || argv[0]==NULL || argv[1]==NULL)
+ usage();
+
+ /* Open the files; "-" means stdin. */
+ if (!strcmp(*argv, "-"))
+ F1->fp = stdin;
+ else if ((F1->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ ++argv;
+ if (!strcmp(*argv, "-"))
+ F2->fp = stdin;
+ else if ((F2->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ if (F1->fp == stdin && F2->fp == stdin)
+ errx(1, "only one input file may be stdin");
+
+ slurp(F1);
+ slurp(F2);
+ while (F1->setcnt && F2->setcnt) {
+ cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf);
+ if (cval == 0) {
+ /* Oh joy, oh rapture, oh beauty divine! */
+ if (joinout)
+ joinlines(F1, F2);
+ slurp(F1);
+ slurp(F2);
+ } else if (cval < 0) {
+ /* File 1 takes the lead... */
+ if (F1->unpair)
+ joinlines(F1, NULL);
+ slurp(F1);
+ } else {
+ /* File 2 takes the lead... */
+ if (F2->unpair)
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ }
+
+ /*
+ * Now that one of the files is used up, optionally output any
+ * remaining lines from the other file.
+ */
+ if (F1->unpair)
+ while (F1->setcnt) {
+ joinlines(F1, NULL);
+ slurp(F1);
+ }
+ if (F2->unpair)
+ while (F2->setcnt) {
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ exit(0);
+}
+
+void
+slurp(INPUT *F)
+{
+ LINE *lp, *lastlp, tmp;
+ size_t len;
+ int cnt;
+ char *bp, *fieldp;
+
+ /*
+ * Read all of the lines from an input file that have the same
+ * join field.
+ */
+ F->setcnt = 0;
+ for (lastlp = NULL;; ++F->setcnt) {
+ /*
+ * If we're out of space to hold line structures, allocate
+ * more. Initialize the structure so that we know that this
+ * is new space.
+ */
+ if (F->setcnt == F->setalloc) {
+ cnt = F->setalloc;
+ F->setalloc += 50;
+ if ((F->set = realloc(F->set,
+ F->setalloc * sizeof(LINE))) == NULL)
+ err(1, NULL);
+ memset(F->set + cnt, 0, 50 * sizeof(LINE));
+
+ /* re-set lastlp in case it moved */
+ if (lastlp != NULL)
+ lastlp = &F->set[F->setcnt - 1];
+ }
+
+ /*
+ * Get any pushed back line, else get the next line. Allocate
+ * space as necessary. If taking the line from the stack swap
+ * the two structures so that we don't lose space allocated to
+ * either structure. This could be avoided by doing another
+ * level of indirection, but it's probably okay as is.
+ */
+ lp = &F->set[F->setcnt];
+ if (F->setcnt)
+ lastlp = &F->set[F->setcnt - 1];
+ if (F->pushbool) {
+ tmp = F->set[F->setcnt];
+ F->set[F->setcnt] = F->set[F->pushback];
+ F->set[F->pushback] = tmp;
+ F->pushbool = 0;
+ continue;
+ }
+ if ((bp = fgetln(F->fp, &len)) == NULL) {
+ if (ferror(F->fp)) {
+ err(EX_IOERR, NULL);
+ }
+ return;
+ }
+ if (lp->linealloc <= len + 1) {
+ lp->linealloc += MAX(100, len + 1 - lp->linealloc);
+ if ((lp->line =
+ realloc(lp->line, lp->linealloc)) == NULL)
+ err(1, NULL);
+ }
+ memmove(lp->line, bp, len);
+
+ /* Replace trailing newline, if it exists. */
+ if (bp[len - 1] == '\n')
+ lp->line[len - 1] = '\0';
+ else
+ lp->line[len] = '\0';
+ bp = lp->line;
+
+ /* Split the line into fields, allocate space as necessary. */
+ lp->fieldcnt = 0;
+ while ((fieldp = mbssep(&bp, tabchar)) != NULL) {
+ if (spans && *fieldp == '\0')
+ continue;
+ if (lp->fieldcnt == lp->fieldalloc) {
+ lp->fieldalloc += 50;
+ if ((lp->fields = realloc(lp->fields,
+ lp->fieldalloc * sizeof(char *))) == NULL)
+ err(1, NULL);
+ }
+ lp->fields[lp->fieldcnt++] = fieldp;
+ }
+
+ /* See if the join field value has changed. */
+ if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) {
+ F->pushbool = 1;
+ F->pushback = F->setcnt;
+ break;
+ }
+ }
+}
+
+char *
+mbssep(char **stringp, const wchar_t *delim)
+{
+ char *s, *tok;
+ const wchar_t *spanp;
+ wchar_t c, sc;
+ size_t n;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ n = mbrtowc(&c, s, MB_LEN_MAX, NULL);
+ if (n == (size_t)-1 || n == (size_t)-2)
+ errc(1, EILSEQ, NULL); /* XXX */
+ s += n;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-n] = '\0';
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+}
+
+int
+cmp(LINE *lp1, u_long fieldno1, LINE *lp2, u_long fieldno2)
+{
+ if (lp1->fieldcnt <= fieldno1)
+ return (lp2->fieldcnt <= fieldno2 ? 0 : 1);
+ if (lp2->fieldcnt <= fieldno2)
+ return (-1);
+ return (mbscoll(lp1->fields[fieldno1], lp2->fields[fieldno2]));
+}
+
+int
+mbscoll(const char *s1, const char *s2)
+{
+ wchar_t *w1, *w2;
+ int ret;
+
+ if (MB_CUR_MAX == 1)
+ return (strcoll(s1, s2));
+ if ((w1 = towcs(s1)) == NULL || (w2 = towcs(s2)) == NULL)
+ err(1, NULL); /* XXX */
+ ret = wcscoll(w1, w2);
+ free(w1);
+ free(w2);
+ return (ret);
+}
+
+wchar_t *
+towcs(const char *s)
+{
+ wchar_t *wcs;
+ size_t n;
+
+ if ((n = mbsrtowcs(NULL, &s, 0, NULL)) == (size_t)-1)
+ return (NULL);
+ if ((wcs = malloc((n + 1) * sizeof(*wcs))) == NULL)
+ return (NULL);
+ mbsrtowcs(wcs, &s, n + 1, NULL);
+ return (wcs);
+}
+
+void
+joinlines(INPUT *F1, INPUT *F2)
+{
+ u_long cnt1, cnt2;
+
+ /*
+ * Output the results of a join comparison. The output may be from
+ * either file 1 or file 2 (in which case the first argument is the
+ * file from which to output) or from both.
+ */
+ if (F2 == NULL) {
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ outoneline(F1, &F1->set[cnt1]);
+ return;
+ }
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2)
+ outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]);
+}
+
+void
+outoneline(INPUT *F, LINE *lp)
+{
+ u_long cnt;
+
+ /*
+ * Output a single line from one of the files, according to the
+ * join rules. This happens when we are writing unmatched single
+ * lines. Output empty fields in the right places.
+ */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt) {
+ if (olist[cnt].filenum == (unsigned)F->number)
+ outfield(lp, olist[cnt].fieldno, 0);
+ else if (olist[cnt].filenum == 0)
+ outfield(lp, F->joinf, 0);
+ else
+ outfield(lp, 0, 1);
+ }
+ else
+ for (cnt = 0; cnt < lp->fieldcnt; ++cnt)
+ outfield(lp, cnt, 0);
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outtwoline(INPUT *F1, LINE *lp1, INPUT *F2, LINE *lp2)
+{
+ u_long cnt;
+
+ /* Output a pair of lines according to the join list (if any). */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt)
+ if (olist[cnt].filenum == 0) {
+ if (lp1->fieldcnt >= F1->joinf)
+ outfield(lp1, F1->joinf, 0);
+ else
+ outfield(lp2, F2->joinf, 0);
+ } else if (olist[cnt].filenum == 1)
+ outfield(lp1, olist[cnt].fieldno, 0);
+ else /* if (olist[cnt].filenum == 2) */
+ outfield(lp2, olist[cnt].fieldno, 0);
+ else {
+ /*
+ * Output the join field, then the remaining fields from F1
+ * and F2.
+ */
+ outfield(lp1, F1->joinf, 0);
+ for (cnt = 0; cnt < lp1->fieldcnt; ++cnt)
+ if (F1->joinf != cnt)
+ outfield(lp1, cnt, 0);
+ for (cnt = 0; cnt < lp2->fieldcnt; ++cnt)
+ if (F2->joinf != cnt)
+ outfield(lp2, cnt, 0);
+ }
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outfield(LINE *lp, u_long fieldno, int out_empty)
+{
+ if (needsep++)
+ (void)printf("%lc", *tabchar);
+ if (!ferror(stdout)) {
+ if (lp->fieldcnt <= fieldno || out_empty) {
+ if (empty != NULL)
+ (void)printf("%s", empty);
+ } else {
+ if (*lp->fields[fieldno] == '\0') {
+ if (COMPAT_MODE("bin/join", "Unix2003") && empty != NULL)
+ (void)printf("%s", empty);
+ else
+ return;
+ } else
+ (void)printf("%s", lp->fields[fieldno]);
+ }
+ }
+ if (ferror(stdout))
+ err(1, "stdout");
+}
+
+/*
+ * Convert an output list argument "2.1, 1.3, 2.4" into an array of output
+ * fields.
+ */
+void
+fieldarg(char *option)
+{
+ u_long fieldno, filenum;
+ char *end, *token;
+
+ while ((token = strsep(&option, ", \t")) != NULL) {
+ if (*token == '\0')
+ continue;
+ if (token[0] == '0')
+ filenum = fieldno = 0;
+ else if ((token[0] == '1' || token[0] == '2') &&
+ token[1] == '.') {
+ filenum = token[0] - '0';
+ fieldno = strtol(token + 2, &end, 10);
+ if (*end)
+ errx(1, "malformed -o option field");
+ if (fieldno == 0)
+ errx(1, "field numbers are 1 based");
+ --fieldno;
+ } else
+ errx(1, "malformed -o option field");
+ if (olistcnt == olistalloc) {
+ olistalloc += 50;
+ if ((olist = realloc(olist,
+ olistalloc * sizeof(OLIST))) == NULL)
+ err(1, NULL);
+ }
+ olist[olistcnt].filenum = filenum;
+ olist[olistcnt].fieldno = fieldno;
+ ++olistcnt;
+ }
+}
+
+void
+obsolete(char **argv)
+{
+ size_t len;
+ char **p, *ap, *t;
+
+ while ((ap = *++argv) != NULL) {
+ /* Return if "--". */
+ if (ap[0] == '-' && ap[1] == '-')
+ return;
+ /* skip if not an option */
+ if (ap[0] != '-')
+ continue;
+ switch (ap[1]) {
+ case 'a':
+ /*
+ * The original join allowed "-a", which meant the
+ * same as -a1 plus -a2. POSIX 1003.2, Draft 11.2
+ * only specifies this as "-a 1" and "a -2", so we
+ * have to use another option flag, one that is
+ * unlikely to ever be used or accidentally entered
+ * on the command line. (Well, we could reallocate
+ * the argv array, but that hardly seems worthwhile.)
+ */
+ if (ap[2] == '\0' && (argv[1] == NULL ||
+ (strcmp(argv[1], "1") != 0 &&
+ strcmp(argv[1], "2") != 0))) {
+ ap[1] = '\01';
+ warnx("-a option used without an argument; "
+ "reverting to historical behavior");
+ }
+ break;
+ case 'j':
+ /*
+ * The original join allowed "-j[12] arg" and "-j arg".
+ * Convert the former to "-[12] arg". Don't convert
+ * the latter since getopt(3) can handle it.
+ */
+ switch(ap[2]) {
+ case '1':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '1';
+ ap[2] = '\0';
+ break;
+ case '2':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '2';
+ ap[2] = '\0';
+ break;
+ case '\0':
+ break;
+ default:
+jbad: errx(1, "illegal option -- %s", ap);
+ usage();
+ }
+ break;
+ case 'o':
+ /*
+ * The original join allowed "-o arg arg".
+ * Convert to "-o arg -o arg".
+ */
+ if (ap[2] != '\0')
+ break;
+ for (p = argv + 2; *p; ++p) {
+ if (p[0][0] == '0' || ((p[0][0] != '1' &&
+ p[0][0] != '2') || p[0][1] != '.'))
+ break;
+ len = strlen(*p);
+ if (len - 2 != strspn(*p + 2, "0123456789"))
+ break;
+ if ((t = malloc(len + 3)) == NULL)
+ err(1, NULL);
+ t[0] = '-';
+ t[1] = 'o';
+ memmove(t + 2, *p, len + 1);
+ *p = t;
+ }
+ argv = p - 1;
+ break;
+ }
+ }
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "%s %s\n%s\n",
+ "usage: join [-a fileno | -v fileno ] [-e string] [-1 field]",
+ "[-2 field]",
+ " [-o list] [-t char] file1 file2");
+ exit(1);
+}
diff --git a/text_cmds/lam/lam.1 b/text_cmds/lam/lam.1
new file mode 100644
index 0000000..9986577
--- /dev/null
+++ b/text_cmds/lam/lam.1
@@ -0,0 +1,141 @@
+.\" Copyright (c) 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lam.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/lam/lam.1,v 1.13 2004/08/12 11:34:34 tjr Exp $
+.\"
+.Dd August 12, 2004
+.Dt LAM 1
+.Os
+.Sh NAME
+.Nm lam
+.Nd laminate files
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar min . Ns Ar max
+.Op Fl s Ar sepstring
+.Op Fl t Ar c
+.Ar
+.Nm
+.Op Fl p Ar min . Ns Ar max
+.Op Fl s Ar sepstring
+.Op Fl t Ar c
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility copies the named files side by side onto the standard output.
+The
+.Em n-th
+input lines from the input
+.Ar files
+are considered fragments of the single long
+.Em n-th
+output line into which they are assembled.
+The name `\fB\-\fP' means the standard input, and may be repeated.
+.Pp
+Normally, each option affects only the
+.Ar file
+after it.
+If the option letter is capitalized it affects all subsequent files
+until it appears again uncapitalized.
+The options are described below:
+.Bl -tag -width indent
+.It Fl f Ar min . Ns Ar max
+Print line fragments according to the format string
+.Ar min . Ns Ar max ,
+where
+.Ar min
+is the minimum field width and
+.Ar max
+the maximum field width.
+If
+.Ar min
+begins with a zero, zeros will be added to make up the field width,
+and if it begins with a `\-', the fragment will be left-adjusted
+within the field.
+.It Fl p Ar min . Ns Ar max
+Like
+.Fl f ,
+but pad this file's field when end-of-file is reached
+and other files are still active.
+.It Fl s Ar sepstring
+Print
+.Ar sepstring
+before printing line fragments from the next file.
+This option may appear after the last file.
+.It Fl t Ar c
+The input line terminator is
+.Ar c
+instead of a newline.
+The newline normally appended to each output line is omitted.
+.El
+.Pp
+To print files simultaneously for easy viewing use
+.Xr pr 1 .
+.Sh EXAMPLES
+The command
+.Bd -literal
+lam file1 file2 file3 file4
+.Ed
+.Pp
+joins 4 files together along each line.
+To merge the lines from four different files use
+.Bd -literal
+lam file1 \-S "\\
+" file2 file3 file4
+.Ed
+.Pp
+Every 2 lines of a file may be joined on one line with
+.Bd -literal
+lam \- \- < file
+.Ed
+.Pp
+and a form letter with substitutions keyed by `@' can be done with
+.Bd -literal
+lam \-t @ letter changes
+.Ed
+.Sh SEE ALSO
+.Xr join 1 ,
+.Xr paste 1 ,
+.Xr pr 1 ,
+.Xr printf 3
+.Sh STANDARDS
+Some of the functionality of
+.Nm
+is standardized as the
+.Xr paste 1
+utility by
+.St -p1003.2 .
+.Sh BUGS
+The
+.Nm
+utility does not recognize multibyte characters.
diff --git a/text_cmds/lam/lam.c b/text_cmds/lam/lam.c
new file mode 100644
index 0000000..cd24920
--- /dev/null
+++ b/text_cmds/lam/lam.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lam.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/lam/lam.c,v 1.14 2005/08/05 01:04:36 jmallett Exp $");
+
+/*
+ * lam - laminate files
+ * Author: John Kunze, UCB
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define MAXOFILES 20
+#define BIGBUFSIZ 5 * BUFSIZ
+
+struct openfile { /* open file structure */
+ FILE *fp; /* file pointer */
+ short eof; /* eof flag */
+ short pad; /* pad flag for missing columns */
+ char eol; /* end of line character */
+ const char *sepstring; /* string to print before each line */
+ const char *format; /* printf(3) style string spec. */
+} input[MAXOFILES];
+
+int morefiles; /* set by getargs(), changed by gatherline() */
+int nofinalnl; /* normally append \n to each output line */
+char line[BIGBUFSIZ];
+char *linep;
+
+static char *gatherline(struct openfile *);
+static void getargs(char *[]);
+static char *pad(struct openfile *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct openfile *ip;
+
+ if (argc == 1)
+ usage();
+ getargs(argv);
+ if (!morefiles)
+ usage();
+ for (;;) {
+ linep = line;
+ for (ip = input; ip->fp != NULL; ip++)
+ linep = gatherline(ip);
+ if (!morefiles)
+ exit(0);
+ fputs(line, stdout);
+ fputs(ip->sepstring, stdout);
+ if (!nofinalnl)
+ putchar('\n');
+ }
+}
+
+static void
+getargs(char *av[])
+{
+ struct openfile *ip = input;
+ char *p, *c;
+ static char fmtbuf[BUFSIZ];
+ char *fmtp = fmtbuf;
+ int P, S, F, T;
+
+ P = S = F = T = 0; /* capitalized options */
+ while ((p = *++av) != NULL) {
+ if (*p != '-' || !p[1]) {
+ if (++morefiles >= MAXOFILES)
+ errx(1, "too many input files");
+ if (*p == '-')
+ ip->fp = stdin;
+ else if ((ip->fp = fopen(p, "r")) == NULL) {
+ err(1, "%s", p);
+ }
+ ip->pad = P;
+ if (!ip->sepstring)
+ ip->sepstring = (S ? (ip-1)->sepstring : "");
+ if (!ip->format)
+ ip->format = ((P || F) ? (ip-1)->format : "%s");
+ if (!ip->eol)
+ ip->eol = (T ? (ip-1)->eol : '\n');
+ ip++;
+ continue;
+ }
+ c = ++p;
+ switch (tolower((unsigned char)*c)) {
+ case 's':
+ if (*++p || (p = *++av))
+ ip->sepstring = p;
+ else
+ usage();
+ S = (*c == 'S' ? 1 : 0);
+ break;
+ case 't':
+ if (*++p || (p = *++av))
+ ip->eol = *p;
+ else
+ usage();
+ T = (*c == 'T' ? 1 : 0);
+ nofinalnl = 1;
+ break;
+ case 'p':
+ ip->pad = 1;
+ P = (*c == 'P' ? 1 : 0);
+ /* FALLTHROUGH */
+ case 'f':
+ F = (*c == 'F' ? 1 : 0);
+ if (*++p || (p = *++av)) {
+ fmtp += strlen(fmtp) + 1;
+ if (fmtp >= fmtbuf + sizeof(fmtbuf))
+ errx(1, "no more format space");
+ /* restrict format string to only valid width formatters */
+ if (strspn(p, "-.0123456789") != strlen(p))
+ errx(1, "invalid format string `%s'", p);
+ if (snprintf(fmtp, fmtbuf + sizeof(fmtbuf) - fmtp, "%%%ss", p)
+ >= fmtbuf + sizeof(fmtbuf) - fmtp)
+ errx(1, "no more format space");
+ ip->format = fmtp;
+ }
+ else
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ ip->fp = NULL;
+ if (!ip->sepstring)
+ ip->sepstring = "";
+}
+
+static char *
+pad(struct openfile *ip)
+{
+ char *lp = linep;
+
+ strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
+ lp += strlen(lp);
+ if (ip->pad) {
+ snprintf(lp, line + sizeof(line) - lp, ip->format, "");
+ lp += strlen(lp);
+ }
+ return (lp);
+}
+
+static char *
+gatherline(struct openfile *ip)
+{
+ char s[BUFSIZ];
+ int c;
+ char *p;
+ char *lp = linep;
+ char *end = s + sizeof(s) - 1;
+
+ if (ip->eof)
+ return (pad(ip));
+ for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
+ if ((*p = c) == ip->eol)
+ break;
+ *p = '\0';
+ if (c == EOF) {
+ ip->eof = 1;
+ if (ferror(ip->fp)) {
+ err(EX_IOERR, NULL);
+ }
+ if (ip->fp == stdin)
+ fclose(stdin);
+ morefiles--;
+ return (pad(ip));
+ }
+ strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
+ lp += strlen(lp);
+ snprintf(lp, line + sizeof(line) - lp, ip->format, s);
+ lp += strlen(lp);
+ return (lp);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: lam [ -f min.max ] [ -s sepstring ] [ -t c ] file ...",
+" lam [ -p min.max ] [ -s sepstring ] [ -t c ] file ...");
+ exit(1);
+}
diff --git a/text_cmds/look/look.1 b/text_cmds/look/look.1
new file mode 100644
index 0000000..fb8bc85
--- /dev/null
+++ b/text_cmds/look/look.1
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)look.1 8.1 (Berkeley) 6/14/93
+.\" $FreeBSD: src/usr.bin/look/look.1,v 1.17 2005/01/17 07:44:22 ru Exp $
+.\"
+.Dd July 17, 2004
+.Dt LOOK 1
+.Os
+.Sh NAME
+.Nm look
+.Nd display lines beginning with a given string
+.Sh SYNOPSIS
+.Nm
+.Op Fl df
+.Op Fl t Ar termchar
+.Ar string
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility displays any lines in
+.Ar file
+which contain
+.Ar string
+as a prefix.
+As
+.Nm
+performs a binary search, the lines in
+.Ar file
+must be sorted.
+.Pp
+If
+.Ar file
+is not specified, the file
+.Pa /usr/share/dict/words
+is used, only alphanumeric characters are compared and the case of
+alphabetic characters is ignored.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Dictionary character set and order, i.e., only alphanumeric characters
+are compared.
+.It Fl f
+Ignore the case of alphabetic characters.
+.It Fl t
+Specify a string termination character, i.e., only the characters
+in
+.Ar string
+up to and including the first occurrence of
+.Ar termchar
+are compared.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of the
+.Nm
+utility.
+Their effect is described in
+.Xr environ 7 .
+.Sh FILES
+.Bl -tag -width /usr/share/dict/words -compact
+.It Pa /usr/share/dict/words
+the dictionary
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 if one or more lines were found and displayed,
+1 if no lines were found, and >1 if an error occurred.
+.Sh COMPATIBILITY
+The original manual page stated that tabs and blank characters participated
+in comparisons when the
+.Fl d
+option was specified.
+This was incorrect and the current man page matches the historic
+implementation.
+.Sh SEE ALSO
+.Xr grep 1 ,
+.Xr sort 1
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v7 .
+.Sh BUGS
+Lines are not compared according to the current locale's collating
+order.
+Input files must be sorted with
+.Ev LC_COLLATE
+set to
+.Ql C .
diff --git a/text_cmds/look/look.c b/text_cmds/look/look.c
new file mode 100644
index 0000000..4f146fa
--- /dev/null
+++ b/text_cmds/look/look.c
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)look.c 8.2 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/look/look.c,v 1.18.10.2.4.1 2010/06/14 02:09:06 kensmith Exp $");
+
+/*
+ * look -- find lines in a sorted list.
+ *
+ * The man page said that TABs and SPACEs participate in -d comparisons.
+ * In fact, they were ignored. This implements historic practice, not
+ * the manual page.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "pathnames.h"
+
+static char _path_words[] = _PATH_WORDS;
+
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+
+int dflag, fflag;
+
+char *binary_search(wchar_t *, unsigned char *, unsigned char *);
+int compare(wchar_t *, unsigned char *, unsigned char *);
+char *linear_search(wchar_t *, unsigned char *, unsigned char *);
+int look(wchar_t *, unsigned char *, unsigned char *);
+wchar_t *prepkey(const char *, wchar_t);
+void print_from(wchar_t *, unsigned char *, unsigned char *);
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ int ch, fd, match;
+ wchar_t termchar;
+ unsigned char *back, *front;
+ unsigned const char *file;
+ wchar_t *key;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ file = _path_words;
+ termchar = L'\0';
+ while ((ch = getopt(argc, argv, "dft:")) != -1)
+ switch(ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 't':
+ if (mbrtowc(&termchar, optarg, MB_LEN_MAX, NULL) !=
+ strlen(optarg))
+ errx(2, "invalid termination character");
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+/* 4384130 */
+#ifdef __APPLE__
+ if (argc <= 0)
+#else
+ if (argc == 0)
+#endif
+ usage();
+ if (argc == 1) /* But set -df by default. */
+ dflag = fflag = 1;
+ key = prepkey(*argv++, termchar);
+ if (argc >= 2)
+ file = *argv++;
+
+ match = 1;
+
+ do {
+ if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ err(2, "%s", file);
+ if (sb.st_size > SIZE_T_MAX)
+ errx(2, "%s: %s", file, strerror(EFBIG));
+ if (sb.st_size == 0) {
+ close(fd);
+ continue;
+ }
+ if ((front = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_SHARED, fd, (off_t)0)) == MAP_FAILED)
+ err(2, "%s", file);
+ back = front + sb.st_size;
+ match *= (look(key, front, back));
+ close(fd);
+ } while (argc-- > 2 && (file = *argv++));
+
+ exit(match);
+}
+
+wchar_t *
+prepkey(const char *string, wchar_t termchar)
+{
+ const char *readp;
+ wchar_t *key, *writep;
+ wchar_t ch;
+ size_t clen;
+
+ /*
+ * Reformat search string and convert to wide character representation
+ * to avoid doing it multiple times later.
+ */
+ if ((key = malloc(sizeof(wchar_t) * (strlen(string) + 1))) == NULL)
+ err(2, NULL);
+ readp = string;
+ writep = key;
+ while ((clen = mbrtowc(&ch, readp, MB_LEN_MAX, NULL)) != 0) {
+ if (clen == (size_t)-1 || clen == (size_t)-2)
+ errc(2, EILSEQ, NULL);
+ if (fflag)
+ ch = towlower(ch);
+ if (!dflag || iswalnum(ch))
+ *writep++ = ch;
+ readp += clen;
+ }
+ *writep = L'\0';
+ if (termchar != L'\0' && (writep = wcschr(key, termchar)) != NULL)
+ *++writep = L'\0';
+ return (key);
+}
+
+int
+look(wchar_t *string, unsigned char *front, unsigned char *back)
+{
+
+ front = binary_search(string, front, back);
+ front = linear_search(string, front, back);
+
+ if (front)
+ print_from(string, front, back);
+ return (front ? 0 : 1);
+}
+
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Base of the Invariants.
+ * front = NULL;
+ * back = EOF;
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) \
+ while (p < back && *p++ != '\n');
+
+char *
+binary_search(wchar_t *string, unsigned char *front, unsigned char *back)
+{
+ unsigned char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ /*
+ * If the file changes underneath us, make sure we don't
+ * infinitely loop.
+ */
+ while (p < back && back > front) {
+ if (compare(string, p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+char *
+linear_search(wchar_t *string, unsigned char *front, unsigned char *back)
+{
+ while (front < back) {
+ switch (compare(string, front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ case LESS: /* No such string. */
+ return (NULL);
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Print as many lines as match string, starting at front.
+ */
+void
+print_from(wchar_t *string, unsigned char *front, unsigned char *back)
+{
+ for (; front < back && compare(string, front, back) == EQUAL; ++front) {
+ for (; front < back && *front != '\n'; ++front)
+ if (putchar(*front) == EOF)
+ err(2, "stdout");
+ if (putchar('\n') == EOF)
+ err(2, "stdout");
+ }
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares with
+ * string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * Compare understands about the -f and -d flags, and treats comparisons
+ * appropriately.
+ *
+ * The string "s1" is null terminated. The string s2 is '\n' terminated (or
+ * "back" terminated).
+ */
+int
+compare(wchar_t *s1, unsigned char *s2, unsigned char *back)
+{
+ wchar_t ch1, ch2;
+ size_t len2;
+
+ for (; *s1 && s2 < back && *s2 != '\n'; ++s1, s2 += len2) {
+ ch1 = *s1;
+ len2 = mbrtowc(&ch2, s2, back - s2, NULL);
+ if (len2 == (size_t)-1 || len2 == (size_t)-2) {
+ ch2 = *s2;
+ len2 = 1;
+ }
+ if (fflag)
+ ch2 = towlower(ch2);
+ if (dflag && !iswalnum(ch2)) {
+ /* Ignore character in comparison. */
+ --s1;
+ continue;
+ }
+ if (ch1 != ch2)
+ return (ch1 < ch2 ? LESS : GREATER);
+ }
+ return (*s1 ? GREATER : EQUAL);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: look [-df] [-t char] string [file ...]\n");
+ exit(2);
+}
diff --git a/text_cmds/look/pathnames.h b/text_cmds/look/pathnames.h
new file mode 100644
index 0000000..586e36a
--- /dev/null
+++ b/text_cmds/look/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ */
+
+#define _PATH_WORDS "/usr/share/dict/words"
diff --git a/text_cmds/md5/commoncrypto.c b/text_cmds/md5/commoncrypto.c
new file mode 100644
index 0000000..3551afe
--- /dev/null
+++ b/text_cmds/md5/commoncrypto.c
@@ -0,0 +1,108 @@
+/* Generic CommonDigest wrappers to match the semantics of libmd. */
+
+#include <dispatch/dispatch.h>
+#include <os/assumes.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "commoncrypto.h"
+
+#define CHUNK_SIZE (10 * 1024 * 1024)
+
+char *
+Digest_End(CCDigestRef ctx, char *buf)
+{
+ static const char hex[] = "0123456789abcdef";
+ uint8_t digest[32]; // SHA256 is the biggest
+ size_t i, length;
+
+ (void)os_assumes_zero(CCDigestFinal(ctx, digest));
+ length = CCDigestOutputSize(ctx);
+ os_assert(length <= sizeof(digest));
+ for (i = 0; i < length; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+
+char *
+Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf)
+{
+ CCDigestCtx ctx;
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, data, len));
+ return Digest_End(&ctx, buf);
+}
+
+char *
+Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
+{
+ int fd;
+ __block CCDigestCtx ctx;
+ dispatch_queue_t queue;
+ dispatch_semaphore_t sema;
+ dispatch_io_t io;
+ __block int s_error = 0;
+ __block bool eof = false;
+ off_t chunk_offset;
+
+ /* dispatch_io_create_with_path requires an absolute path */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ (void)fcntl(fd, F_NOCACHE, 1);
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+
+ queue = dispatch_queue_create("com.apple.mtree.io", NULL);
+ os_assert(queue);
+ sema = dispatch_semaphore_create(0);
+ os_assert(sema);
+
+ io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
+ if (error != 0) {
+ s_error = error;
+ }
+ (void)close(fd);
+ (void)dispatch_semaphore_signal(sema);
+ });
+ os_assert(io);
+ for (chunk_offset = 0; eof == false && s_error == 0; chunk_offset += CHUNK_SIZE) {
+ dispatch_io_read(io, chunk_offset, CHUNK_SIZE, queue, ^(bool done, dispatch_data_t data, int error) {
+ if (data != NULL) {
+ (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
+ return (bool)true;
+ });
+ }
+
+ if (error != 0) {
+ s_error = error;
+ }
+
+ if (done) {
+ eof = (data == dispatch_data_empty);
+ dispatch_semaphore_signal(sema);
+ }
+ });
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+ }
+ dispatch_release(io); // it will close on its own
+
+ (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ dispatch_release(queue);
+ dispatch_release(sema);
+
+ if (s_error != 0) {
+ errno = s_error;
+ return NULL;
+ }
+
+ return Digest_End(&ctx, buf);
+}
diff --git a/text_cmds/md5/commoncrypto.h b/text_cmds/md5/commoncrypto.h
new file mode 100644
index 0000000..ed10d9a
--- /dev/null
+++ b/text_cmds/md5/commoncrypto.h
@@ -0,0 +1,8 @@
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+char *Digest_End(CCDigestRef, char *);
+
+char *Digest_Data(CCDigestAlg, const void *, size_t, char *);
+
+char *Digest_File(CCDigestAlg, const char *, char *);
diff --git a/text_cmds/md5/md5.1 b/text_cmds/md5/md5.1
new file mode 100644
index 0000000..01df82b
--- /dev/null
+++ b/text_cmds/md5/md5.1
@@ -0,0 +1,95 @@
+.\" $FreeBSD: src/sbin/md5/md5.1,v 1.24 2005/03/10 09:56:39 cperciva Exp $
+.Dd June 6, 2004
+.Dt MD5 1
+.Os
+.Sh NAME
+.Nm md5
+.Nd calculate a message-digest fingerprint (checksum) for a file
+.Sh SYNOPSIS
+.Nm md5
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility takes as input a message of arbitrary length and produces as
+output a
+.Dq fingerprint
+or
+.Dq message digest
+of the input.
+It is conjectured that it is computationally infeasible to
+produce two messages having the same message digest, or to produce any
+message having a given prespecified target message digest.
+The
+.Tn MD5
+algorithm is intended for digital signature applications, where a
+large file must be
+.Dq compressed
+in a secure manner before being encrypted with a private
+(secret)
+key under a public-key cryptosystem such as
+.Tn RSA .
+.Pp
+.Tn MD5's
+designer Ron Rivest has stated "md5 and sha1 are both clearly broken (in terms
+of collision-resistance)".
+So
+.Tn MD5
+should be avoided when creating new protocols, or implementing protocols with better options.
+.Tn SHA256
+and
+.Tn SHA512
+are better options as they have been more resilient to attacks (as of 2009).
+.Pp
+The following options may be used in any combination and must
+precede any files named on the command line.
+The hexadecimal checksum of each file listed on the command line is printed
+after the options are processed.
+.Bl -tag -width indent
+.It Fl s Ar string
+Print a checksum of the given
+.Ar string .
+.It Fl p
+Echo stdin to stdout and append the checksum to stdout.
+.It Fl q
+Quiet mode - only the checksum is printed out.
+Overrides the
+.Fl r
+option.
+.It Fl r
+Reverses the format of the output.
+This helps with visual diffs.
+Does nothing
+when combined with the
+.Fl ptx
+options.
+.It Fl t
+Run a built-in time trial.
+.It Fl x
+Run a built-in test script.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success,
+and 1 if at least one of the input files could not be read.
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 3 ,
+.Xr ripemd 3 ,
+.Xr sha 3 ,
+.Xr CC_SHA256_Init 3
+.Rs
+.%A R. Rivest
+.%T The MD5 Message-Digest Algorithm
+.%O RFC1321
+.Rs
+.%A Vlastimil Klima
+.%T Finding MD5 Collisions - a Toy For a Notebook
+.%O Cryptology ePrint Archive: Report 2005/075
+.Re
+.Sh ACKNOWLEDGMENTS
+This program is placed in the public domain for free general use by
+RSA Data Security.
diff --git a/text_cmds/md5/md5.c b/text_cmds/md5/md5.c
new file mode 100644
index 0000000..9be53d3
--- /dev/null
+++ b/text_cmds/md5/md5.c
@@ -0,0 +1,419 @@
+/*
+ * Derived from:
+ *
+ * MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/*
+ * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+ * rights reserved.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sbin/md5/md5.c,v 1.34 2005/03/09 19:23:04 cperciva Exp $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <err.h>
+#ifndef __APPLE__
+#include <md5.h>
+#include <ripemd.h>
+#include <sha.h>
+#include <sha256.h>
+#endif /* !__APPLE__ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#ifdef __APPLE__
+#include "commoncrypto.h"
+#endif /* __APPLE__ */
+
+/*
+ * Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 10000
+#define TEST_BLOCK_COUNT 100000
+#define MDTESTCOUNT 8
+
+int qflag;
+int rflag;
+int sflag;
+
+typedef void (DIGEST_Init)(void *);
+typedef void (DIGEST_Update)(void *, const unsigned char *, size_t);
+typedef char *(DIGEST_End)(void *, char *);
+
+extern const char *MD5TestOutput[MDTESTCOUNT];
+extern const char *SHA1_TestOutput[MDTESTCOUNT];
+extern const char *SHA256_TestOutput[MDTESTCOUNT];
+extern const char *RIPEMD160_TestOutput[MDTESTCOUNT];
+
+typedef struct Algorithm_t {
+ const char *progname;
+ const char *name;
+ const char *(*TestOutput)[MDTESTCOUNT];
+#ifdef __APPLE__
+ CCDigestAlg algorithm;
+#else /* !__APPLE__ */
+ DIGEST_Init *Init;
+ DIGEST_Update *Update;
+ DIGEST_End *End;
+ char *(*Data)(const unsigned char *, unsigned int, char *);
+ char *(*File)(const char *, char *);
+#endif /* __APPLE__ */
+} Algorithm_t;
+
+#ifndef __APPLE__
+static void MD5_Update(MD5_CTX *, const unsigned char *, size_t);
+#endif /* !__APPLE__ */
+static void MDString(Algorithm_t *, const char *);
+static void MDTimeTrial(Algorithm_t *);
+static void MDTestSuite(Algorithm_t *);
+static void MDFilter(Algorithm_t *, int);
+static void usage(Algorithm_t *);
+
+#ifdef __APPLE__
+typedef CCDigestCtx DIGEST_CTX;
+#else /* !__APPLE__ */
+typedef union {
+ MD5_CTX md5;
+ SHA1_CTX sha1;
+ SHA256_CTX sha256;
+ RIPEMD160_CTX ripemd160;
+} DIGEST_CTX;
+#endif /* __APPLE__ */
+
+/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
+ SHA256_DIGEST_LENGTH, RIPEMD160_DIGEST_LENGTH)*2+1 */
+#define HEX_DIGEST_LENGTH 65
+
+/* algorithm function table */
+
+struct Algorithm_t Algorithm[] = {
+#ifdef __APPLE__
+ { "md5", "MD5", &MD5TestOutput, kCCDigestMD5, },
+ { "sha1", "SHA1", &SHA1_TestOutput, kCCDigestSHA1 },
+ { "sha256", "SHA256", &SHA256_TestOutput, kCCDigestSHA256 },
+ { "rmd160", "RMD160", &RIPEMD160_TestOutput, kCCDigestRMD160 },
+#else
+ { "md5", "MD5", &MD5TestOutput, (DIGEST_Init*)&MD5Init,
+ (DIGEST_Update*)&MD5_Update, (DIGEST_End*)&MD5End,
+ &MD5Data, &MD5File },
+ { "sha1", "SHA1", &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init,
+ (DIGEST_Update*)&SHA1_Update, (DIGEST_End*)&SHA1_End,
+ &SHA1_Data, &SHA1_File },
+ { "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
+ (DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End,
+ &SHA256_Data, &SHA256_File },
+ { "rmd160", "RMD160", &RIPEMD160_TestOutput,
+ (DIGEST_Init*)&RIPEMD160_Init, (DIGEST_Update*)&RIPEMD160_Update,
+ (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_File }
+#endif
+};
+
+#ifndef __APPLE__
+static void
+MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len)
+{
+ MD5Update(c, data, len);
+}
+#endif /* !__APPLE__ */
+
+/* Main driver.
+
+Arguments (may be any combination):
+ -sstring - digests string
+ -t - runs time trial
+ -x - runs test script
+ filename - digests file
+ (none) - digests standard input
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ char *p;
+ char buf[HEX_DIGEST_LENGTH];
+ int failed=0;
+ unsigned digest=0;
+ const char* progname;
+
+ if(*argv) {
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ for (digest = 0; digest < sizeof(Algorithm)/sizeof(*Algorithm); digest++)
+ if (strcasecmp(Algorithm[digest].progname, progname) == 0)
+ break;
+
+ if (digest == sizeof(Algorithm)/sizeof(*Algorithm))
+ digest = 0;
+ }
+
+ while ((ch = getopt(argc, argv, "pqrs:tx")) != -1)
+ switch (ch) {
+ case 'p':
+ MDFilter(&Algorithm[digest], 1);
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ MDString(&Algorithm[digest], optarg);
+ break;
+ case 't':
+ MDTimeTrial(&Algorithm[digest]);
+ break;
+ case 'x':
+ MDTestSuite(&Algorithm[digest]);
+ break;
+ default:
+ usage(&Algorithm[digest]);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv) {
+ do {
+#ifdef __APPLE__
+ p = Digest_File(Algorithm[digest].algorithm, *argv, buf);
+#else
+ p = Algorithm[digest].File(*argv, buf);
+#endif
+ if (!p) {
+ warn("%s", *argv);
+ failed++;
+ } else {
+ if (qflag)
+ printf("%s\n", p);
+ else if (rflag)
+ printf("%s %s\n", p, *argv);
+ else
+ printf("%s (%s) = %s\n", Algorithm[digest].name, *argv, p);
+ }
+ } while (*++argv);
+ } else if (!sflag && (optind == 1 || qflag || rflag))
+ MDFilter(&Algorithm[digest], 0);
+
+ if (failed != 0)
+ return (1);
+
+ return (0);
+}
+/*
+ * Digests a string and prints the result.
+ */
+static void
+MDString(Algorithm_t *alg, const char *string)
+{
+ size_t len = strlen(string);
+ char buf[HEX_DIGEST_LENGTH];
+
+ if (qflag)
+#ifdef __APPLE__
+ printf("%s\n", Digest_Data(alg->algorithm, string, len, buf));
+ else if (rflag)
+ printf("%s \"%s\"\n", Digest_Data(alg->algorithm, string, len, buf), string);
+ else
+ printf("%s (\"%s\") = %s\n", alg->name, string, Digest_Data(alg->algorithm, string, len, buf));
+#else /* !__APPLE__ */
+ printf("%s\n", alg->Data(string, len, buf));
+ else if (rflag)
+ printf("%s \"%s\"\n", alg->Data(string, len, buf), string);
+ else
+ printf("%s (\"%s\") = %s\n", alg->name, string, alg->Data(string, len, buf));
+#endif /* __APPLE__ */
+}
+/*
+ * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
+ */
+static void
+MDTimeTrial(Algorithm_t *alg)
+{
+ DIGEST_CTX context;
+ struct rusage before, after;
+ struct timeval total;
+ float seconds;
+ unsigned char block[TEST_BLOCK_LEN];
+ unsigned int i;
+ char *p, buf[HEX_DIGEST_LENGTH];
+
+ printf
+ ("%s time trial. Digesting %d %d-byte blocks ...",
+ alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
+ fflush(stdout);
+
+ /* Initialize block */
+ for (i = 0; i < TEST_BLOCK_LEN; i++)
+ block[i] = (unsigned char) (i & 0xff);
+
+ /* Start timer */
+ getrusage(0, &before);
+
+ /* Digest blocks */
+#ifdef __APPLE__
+ CCDigestInit(alg->algorithm, &context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ CCDigestUpdate(&context, block, TEST_BLOCK_LEN);
+ p = Digest_End(&context, buf);
+#else
+ alg->Init(&context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ alg->Update(&context, block, TEST_BLOCK_LEN);
+ p = alg->End(&context, buf);
+#endif
+
+ /* Stop timer */
+ getrusage(0, &after);
+ timersub(&after.ru_utime, &before.ru_utime, &total);
+ seconds = total.tv_sec + (float) total.tv_usec / 1000000;
+
+ printf(" done\n");
+ printf("Digest = %s", p);
+ printf("\nTime = %f seconds\n", seconds);
+ printf
+ ("Speed = %f bytes/second\n",
+ (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds);
+}
+/*
+ * Digests a reference suite of strings and prints the results.
+ */
+
+const char *MDTestInput[MDTESTCOUNT] = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made \
+that its security is in some doubt"
+};
+
+const char *MD5TestOutput[MDTESTCOUNT] = {
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "f96b697d7cb7938d525a2f31aaf161d0",
+ "c3fcd3d76192e4007dfb496cca67e13b",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "57edf4a22be3c955ac49da2e2107b67a",
+ "b50663f41d44d92171cb9976bc118538"
+};
+
+const char *SHA1_TestOutput[MDTESTCOUNT] = {
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "761c457bf73b14d27e9e9265c46f4b4dda11f940",
+ "50abf5706a150990a08b2c5ea40fa0e585554732",
+ "18eca4333979c4181199b7b4fab8786d16cf2846"
+};
+
+const char *SHA256_TestOutput[MDTESTCOUNT] = {
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73",
+ "db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0",
+ "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e",
+ "e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f"
+};
+
+const char *RIPEMD160_TestOutput[MDTESTCOUNT] = {
+ "9c1185a5c5e9fc54612808977ee8f548b2258d31",
+ "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
+ "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ "5d0689ef49d2fae572b881b123a85ffa21595f36",
+ "f71c27109c692c1b56bbdceb5b9d2865b3708dbc",
+ "b0e20b6e3116640286ed3a87a5713079b21f5189",
+ "9b752e45573d4b39f4dbd3323cab82bf63326bfb",
+ "5feb69c6bf7c29d95715ad55f57d8ac5b2b7dd32"
+};
+
+static void
+MDTestSuite(Algorithm_t *alg)
+{
+ int i;
+ char buffer[HEX_DIGEST_LENGTH];
+
+ printf("%s test suite:\n", alg->name);
+ for (i = 0; i < MDTESTCOUNT; i++) {
+#ifdef __APPLE__
+ Digest_Data(alg->algorithm, MDTestInput[i], strlen(MDTestInput[i]), buffer);
+#else
+ (*alg->Data)(MDTestInput[i], strlen(MDTestInput[i]), buffer);
+#endif
+ printf("%s (\"%s\") = %s", alg->name, MDTestInput[i], buffer);
+ if (strcmp(buffer, (*alg->TestOutput)[i]) == 0)
+ printf(" - verified correct\n");
+ else
+ printf(" - INCORRECT RESULT!\n");
+ }
+}
+
+/*
+ * Digests the standard input and prints the result.
+ */
+static void
+MDFilter(Algorithm_t *alg, int tee)
+{
+ DIGEST_CTX context;
+ unsigned int len;
+ unsigned char buffer[BUFSIZ];
+ char buf[HEX_DIGEST_LENGTH];
+
+#ifdef __APPLE__
+ CCDigestInit(alg->algorithm, &context);
+#else
+ alg->Init(&context);
+#endif
+ while ((len = fread(buffer, 1, BUFSIZ, stdin))) {
+ if (tee && len != fwrite(buffer, 1, len, stdout))
+ err(1, "stdout");
+#ifdef __APPLE__
+ CCDigestUpdate(&context, buffer, len);
+#else
+ alg->Update(&context, buffer, len);
+#endif
+ }
+ if (ferror(stdin)) {
+ errx(EX_IOERR, NULL);
+ }
+#ifdef __APPLE__
+ printf("%s\n", Digest_End(&context, buf));
+#else
+ printf("%s\n", alg->End(&context, buf));
+#endif
+}
+
+static void
+usage(Algorithm_t *alg)
+{
+
+ fprintf(stderr, "usage: %s [-pqrtx] [-s string] [files ...]\n", alg->progname);
+ exit(1);
+}
diff --git a/text_cmds/nl/nl.1 b/text_cmds/nl/nl.1
new file mode 100644
index 0000000..50708fb
--- /dev/null
+++ b/text_cmds/nl/nl.1
@@ -0,0 +1,253 @@
+.\" $FreeBSD: src/usr.bin/nl/nl.1,v 1.16 2005/01/25 22:32:48 tjr Exp $
+.\"
+.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Klaus Klein.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 26, 2005
+.Dt NL 1
+.Os
+.Sh NAME
+.Nm nl
+.Nd line numbering filter
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Bk -words
+.Op Fl b Ar type
+.Ek
+.Bk -words
+.Op Fl d Ar delim
+.Ek
+.Bk -words
+.Op Fl f Ar type
+.Ek
+.Bk -words
+.Op Fl h Ar type
+.Ek
+.Bk -words
+.Op Fl i Ar incr
+.Ek
+.Bk -words
+.Op Fl l Ar num
+.Ek
+.Bk -words
+.Op Fl n Ar format
+.Ek
+.Bk -words
+.Op Fl s Ar sep
+.Ek
+.Bk -words
+.Op Fl v Ar startnum
+.Ek
+.Bk -words
+.Op Fl w Ar width
+.Ek
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility reads lines from the named
+.Ar file
+or the standard input if the
+.Ar file
+argument is omitted,
+applies a configurable line numbering filter operation and writes the result
+to the standard output.
+.Pp
+The
+.Nm
+utility treats the text it reads in terms of logical pages.
+Unless specified otherwise, line numbering is reset at the start of each
+logical page.
+A logical page consists of a header, a body and a footer
+section; empty sections are valid.
+Different line numbering options are
+independently available for header, body and footer sections.
+.Pp
+The starts of logical page sections are signalled by input lines containing
+nothing but one of the following sequences of delimiter characters:
+.Pp
+.Bl -column "\e:\e:\e:" "Start of" -offset indent
+.Em "Line Start of"
+.It "\e:\e:\e: header"
+.It "\e:\e: body"
+.It "\e: footer"
+.El
+.Pp
+If the input does not contain any logical page section signalling directives,
+the text being read is assumed to consist of a single logical page body.
+.Pp
+The following options are available:
+.Bl -tag -width ".Fl v Ar startnum"
+.It Fl b Ar type
+Specify the logical page body lines to be numbered.
+Recognized
+.Ar type
+arguments are:
+.Bl -tag -width indent
+.It Cm a
+Number all lines.
+.It Cm t
+Number only non-empty lines.
+.It Cm n
+No line numbering.
+.It Cm p Ns Ar expr
+Number only those lines that contain the basic regular expression specified
+by
+.Ar expr .
+.El
+.Pp
+The default
+.Ar type
+for logical page body lines is
+.Cm t .
+.It Fl d Ar delim
+Specify the delimiter characters used to indicate the start of a logical
+page section in the input file.
+At most two characters may be specified;
+if only one character is specified, the first character is replaced and the
+second character remains unchanged.
+The default
+.Ar delim
+characters are
+.Dq Li \e: .
+.It Fl f Ar type
+Specify the same as
+.Fl b Ar type
+except for logical page footer lines.
+The default
+.Ar type
+for logical page footer lines is
+.Cm n .
+.It Fl h Ar type
+Specify the same as
+.Fl b Ar type
+except for logical page header lines.
+The default
+.Ar type
+for logical page header lines is
+.Cm n .
+.It Fl i Ar incr
+Specify the increment value used to number logical page lines.
+The default
+.Ar incr
+value is 1.
+.It Fl l Ar num
+If numbering of all lines is specified for the current logical section
+using the corresponding
+.Fl b Cm a ,
+.Fl f Cm a
+or
+.Fl h Cm a
+option,
+specify the number of adjacent blank lines to be considered as one.
+For example,
+.Fl l
+2 results in only the second adjacent blank line being numbered.
+The default
+.Ar num
+value is 1.
+.It Fl n Ar format
+Specify the line numbering output format.
+Recognized
+.Ar format
+arguments are:
+.Bl -tag -width indent -compact
+.It Cm ln
+Left justified.
+.It Cm rn
+Right justified, leading zeros suppressed.
+.It Cm rz
+Right justified, leading zeros kept.
+.El
+.Pp
+The default
+.Ar format
+is
+.Cm rn .
+.It Fl p
+Specify that line numbering should not be restarted at logical page delimiters.
+.It Fl s Ar sep
+Specify the characters used in separating the line number and the corresponding
+text line.
+The default
+.Ar sep
+setting is a single tab character.
+.It Fl v Ar startnum
+Specify the initial value used to number logical page lines; see also the
+description of the
+.Fl p
+option.
+The default
+.Ar startnum
+value is 1.
+.It Fl w Ar width
+Specify the number of characters to be occupied by the line number;
+in case the
+.Ar width
+is insufficient to hold the line number, it will be truncated to its
+.Ar width
+least significant digits.
+The default
+.Ar width
+is 6.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_CTYPE
+and
+.Ev LC_COLLATE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr jot 1 ,
+.Xr pr 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.At V.2 .
+.Sh BUGS
+Input lines are limited to
+.Dv LINE_MAX
+(2048) bytes in length.
diff --git a/text_cmds/nl/nl.c b/text_cmds/nl/nl.c
new file mode 100644
index 0000000..3de82f2
--- /dev/null
+++ b/text_cmds/nl/nl.c
@@ -0,0 +1,439 @@
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Klaus Klein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT(
+"@(#) Copyright (c) 1999\
+ The NetBSD Foundation, Inc. All rights reserved.");
+__RCSID("$FreeBSD: src/usr.bin/nl/nl.c,v 1.10 2005/04/09 14:31:41 stefanf Exp $");
+#endif
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+typedef enum {
+ number_all, /* number all lines */
+ number_nonempty, /* number non-empty lines */
+ number_none, /* no line numbering */
+ number_regex /* number lines matching regular expression */
+} numbering_type;
+
+struct numbering_property {
+ const char * const name; /* for diagnostics */
+ numbering_type type; /* numbering type */
+ regex_t expr; /* for type == number_regex */
+};
+
+/* line numbering formats */
+#define FORMAT_LN "%-*d" /* left justified, leading zeros suppressed */
+#define FORMAT_RN "%*d" /* right justified, leading zeros suppressed */
+#define FORMAT_RZ "%0*d" /* right justified, leading zeros kept */
+
+#define FOOTER 0
+#define BODY 1
+#define HEADER 2
+#define NP_LAST HEADER
+
+static struct numbering_property numbering_properties[NP_LAST + 1] = {
+ { "footer", number_none },
+ { "body", number_nonempty },
+ { "header", number_none }
+};
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+/*
+ * Maximum number of characters required for a decimal representation of a
+ * (signed) int; courtesy of tzcode.
+ */
+#define INT_STRLEN_MAXIMUM \
+ ((sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2)
+
+static void filter(void);
+static void parse_numbering(const char *, int);
+static void usage(void);
+
+/*
+ * Pointer to dynamically allocated input line buffer, and its size.
+ */
+static char *buffer;
+static size_t buffersize;
+
+/*
+ * Dynamically allocated buffer suitable for string representation of ints.
+ */
+static char *intbuffer;
+
+/* delimiter characters that indicate the start of a logical page section */
+static char delim[2 * MB_LEN_MAX];
+static int delimlen;
+
+/*
+ * Configurable parameters.
+ */
+
+/* line numbering format */
+static const char *format = FORMAT_RN;
+
+/* increment value used to number logical page lines */
+static int incr = 1;
+
+/* number of adjacent blank lines to be considered (and numbered) as one */
+static unsigned int nblank = 1;
+
+/* whether to restart numbering at logical page delimiters */
+static int restart = 1;
+
+/* characters used in separating the line number and the corrsp. text line */
+static const char *sep = "\t";
+
+/* initial value used to number logical page lines */
+static int startnum = 1;
+
+/* number of characters to be used for the line number */
+/* should be unsigned but required signed by `*' precision conversion */
+static int width = 6;
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ long val;
+ unsigned long uval;
+ char *ep;
+ size_t intbuffersize, clen;
+ char delim1[MB_LEN_MAX] = { '\\' }, delim2[MB_LEN_MAX] = { ':' };
+ size_t delim1len = 1, delim2len = 1;
+
+ (void)setlocale(LC_ALL, "");
+
+ while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
+ switch (c) {
+ case 'p':
+ restart = 0;
+ break;
+ case 'b':
+ parse_numbering(optarg, BODY);
+ break;
+ case 'd':
+ clen = mbrlen(optarg, MB_CUR_MAX, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2)
+ errc(EXIT_FAILURE, EILSEQ, NULL);
+ if (clen != 0) {
+ memcpy(delim1, optarg, delim1len = clen);
+ clen = mbrlen(optarg + delim1len,
+ MB_CUR_MAX, NULL);
+ if (clen == (size_t)-1 ||
+ clen == (size_t)-2)
+ errc(EXIT_FAILURE, EILSEQ, NULL);
+ if (clen != 0) {
+ memcpy(delim2, optarg + delim1len,
+ delim2len = clen);
+ if (optarg[delim1len + clen] != '\0')
+ errx(EXIT_FAILURE,
+ "invalid delim argument -- %s",
+ optarg);
+ }
+ }
+ break;
+ case 'f':
+ parse_numbering(optarg, FOOTER);
+ break;
+ case 'h':
+ parse_numbering(optarg, HEADER);
+ break;
+ case 'i':
+ errno = 0;
+ val = strtol(optarg, &ep, 10);
+ if ((ep != NULL && *ep != '\0') ||
+ ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
+ errx(EXIT_FAILURE,
+ "invalid incr argument -- %s", optarg);
+ incr = (int)val;
+ break;
+ case 'l':
+ errno = 0;
+ uval = strtoul(optarg, &ep, 10);
+ if ((ep != NULL && *ep != '\0') ||
+ (uval == ULONG_MAX && errno != 0))
+ errx(EXIT_FAILURE,
+ "invalid num argument -- %s", optarg);
+ nblank = (unsigned int)uval;
+ break;
+ case 'n':
+ if (strcmp(optarg, "ln") == 0) {
+ format = FORMAT_LN;
+ } else if (strcmp(optarg, "rn") == 0) {
+ format = FORMAT_RN;
+ } else if (strcmp(optarg, "rz") == 0) {
+ format = FORMAT_RZ;
+ } else
+ errx(EXIT_FAILURE,
+ "illegal format -- %s", optarg);
+ break;
+ case 's':
+ sep = optarg;
+ break;
+ case 'v':
+ errno = 0;
+ val = strtol(optarg, &ep, 10);
+ if ((ep != NULL && *ep != '\0') ||
+ ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
+ errx(EXIT_FAILURE,
+ "invalid startnum value -- %s", optarg);
+ startnum = (int)val;
+ break;
+ case 'w':
+ errno = 0;
+ val = strtol(optarg, &ep, 10);
+ if ((ep != NULL && *ep != '\0') ||
+ ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
+ errx(EXIT_FAILURE,
+ "invalid width value -- %s", optarg);
+ width = (int)val;
+ if (!(width > 0))
+ errx(EXIT_FAILURE,
+ "width argument must be > 0 -- %d",
+ width);
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 0:
+ break;
+ case 1:
+ if (!*argv) usage();
+ if (freopen(argv[0], "r", stdin) == NULL)
+ err(EXIT_FAILURE, "%s", argv[0]);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ /* Generate the delimiter sequence */
+ memcpy(delim, delim1, delim1len);
+ memcpy(delim + delim1len, delim2, delim2len);
+ delimlen = delim1len + delim2len;
+
+ /* Determine the maximum input line length to operate on. */
+ if ((val = sysconf(_SC_LINE_MAX)) == -1) /* ignore errno */
+ val = LINE_MAX;
+ /* Allocate sufficient buffer space (including the terminating NUL). */
+ buffersize = (size_t)val + 1;
+ if ((buffer = malloc(buffersize)) == NULL)
+ err(EXIT_FAILURE, "cannot allocate input line buffer");
+
+ /* Allocate a buffer suitable for preformatting line number. */
+ intbuffersize = max(INT_STRLEN_MAXIMUM, width) + 1; /* NUL */
+ if ((intbuffer = malloc(intbuffersize)) == NULL)
+ err(EXIT_FAILURE, "cannot allocate preformatting buffer");
+
+ /* Do the work. */
+ filter();
+
+ exit(EXIT_SUCCESS);
+ /* NOTREACHED */
+}
+
+static void
+filter()
+{
+ int line; /* logical line number */
+ int section; /* logical page section */
+ unsigned int adjblank; /* adjacent blank lines */
+ int consumed; /* intbuffer measurement */
+ int donumber, idx;
+
+ adjblank = 0;
+ line = startnum;
+ section = BODY;
+#ifdef __GNUC__
+ (void)&donumber; /* avoid bogus `uninitialized' warning */
+#endif
+
+ while (fgets(buffer, (int)buffersize, stdin) != NULL) {
+ for (idx = FOOTER; idx <= NP_LAST; idx++) {
+ /* Does it look like a delimiter? */
+ if (memcmp(buffer + delimlen * idx, delim,
+ delimlen) == 0) {
+ /* Was this the whole line? */
+ if (buffer[delimlen * (idx + 1)] == '\n') {
+#ifdef __APPLE__
+ /* if user wishes to restart line numbering on each logical page, AND
+ * the new section is logically "before", or the same as, the current
+ * section (thereby starting a new logical page), reset the line numbers.
+ */
+ if (restart && idx >= section)
+ line = startnum;
+#endif /* __APPLE __*/
+ section = idx;
+ adjblank = 0;
+#ifndef __APPLE__
+ if (restart)
+ line = startnum;
+#endif /* !__APPLE__ */
+ goto nextline;
+ }
+ } else {
+ break;
+ }
+ }
+
+ switch (numbering_properties[section].type) {
+ case number_all:
+ /*
+ * Doing this for number_all only is disputable, but
+ * the standard expresses an explicit dependency on
+ * `-b a' etc.
+ */
+ if (buffer[0] == '\n' && ++adjblank < nblank)
+ donumber = 0;
+ else
+ donumber = 1, adjblank = 0;
+ break;
+ case number_nonempty:
+ donumber = (buffer[0] != '\n');
+ break;
+ case number_none:
+ donumber = 0;
+ break;
+ case number_regex:
+ donumber =
+ (regexec(&numbering_properties[section].expr,
+ buffer, 0, NULL, 0) == 0);
+ break;
+ }
+
+ if (donumber) {
+ /* Note: sprintf() is safe here. */
+ consumed = sprintf(intbuffer, format, width, line);
+ (void)printf("%s",
+ intbuffer + max(0, consumed - width));
+ line += incr;
+ } else {
+ (void)printf("%*s", width, "");
+ }
+ (void)printf("%s%s", sep, buffer);
+
+ if (ferror(stdout))
+ err(EXIT_FAILURE, "output error");
+nextline:
+ ;
+ }
+
+ if (ferror(stdin))
+ err(EXIT_FAILURE, "input error");
+}
+
+/*
+ * Various support functions.
+ */
+
+static void
+parse_numbering(argstr, section)
+ const char *argstr;
+ int section;
+{
+ int error;
+ char errorbuf[NL_TEXTMAX];
+
+ switch (argstr[0]) {
+ case 'a':
+ numbering_properties[section].type = number_all;
+ break;
+ case 'n':
+ numbering_properties[section].type = number_none;
+ break;
+ case 't':
+ numbering_properties[section].type = number_nonempty;
+ break;
+ case 'p':
+ /* If there was a previous expression, throw it away. */
+ if (numbering_properties[section].type == number_regex)
+ regfree(&numbering_properties[section].expr);
+ else
+ numbering_properties[section].type = number_regex;
+
+ /* Compile/validate the supplied regular expression. */
+ if ((error = regcomp(&numbering_properties[section].expr,
+ &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) {
+ (void)regerror(error,
+ &numbering_properties[section].expr,
+ errorbuf, sizeof (errorbuf));
+ errx(EXIT_FAILURE,
+ "%s expr: %s -- %s",
+ numbering_properties[section].name, errorbuf,
+ &argstr[1]);
+ }
+ break;
+ default:
+ errx(EXIT_FAILURE,
+ "illegal %s line numbering type -- %s",
+ numbering_properties[section].name, argstr);
+ }
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+"usage: nl [-p] [-b type] [-d delim] [-f type] [-h type] [-i incr] [-l num]\n"
+" [-n format] [-s sep] [-v startnum] [-w width] [file]\n");
+ exit(EXIT_FAILURE);
+}
diff --git a/text_cmds/paste/paste.1 b/text_cmds/paste/paste.1
new file mode 100644
index 0000000..f5a4aa6
--- /dev/null
+++ b/text_cmds/paste/paste.1
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Adam S. Moskowitz and the Institute of Electrical and Electronics
+.\" Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)paste.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/paste/paste.1,v 1.19 2005/01/18 13:43:52 ru Exp $
+.\"
+.Dd June 25, 2004
+.Dt PASTE 1
+.Os
+.Sh NAME
+.Nm paste
+.Nd merge corresponding or subsequent lines of files
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Op Fl d Ar list
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility concatenates the corresponding lines of the given input files,
+replacing all but the last file's newline characters with a single tab
+character, and writes the resulting lines to standard output.
+If end-of-file is reached on an input file while other input files
+still contain data, the file is treated as if it were an endless source
+of empty lines.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d Ar list
+Use one or more of the provided characters to replace the newline
+characters instead of the default tab.
+The characters in
+.Ar list
+are used circularly, i.e., when
+.Ar list
+is exhausted the first character from
+.Ar list
+is reused.
+This continues until a line from the last input file (in default operation)
+or the last line in each file (using the
+.Fl s
+option) is displayed, at which
+time
+.Nm
+begins selecting characters from the beginning of
+.Ar list
+again.
+.Pp
+The following special characters can also be used in list:
+.Pp
+.Bl -tag -width flag -compact
+.It Li \en
+newline character
+.It Li \et
+tab character
+.It Li \e\e
+backslash character
+.It Li \e0
+Empty string (not a null character).
+.El
+.Pp
+Any other character preceded by a backslash is equivalent to the
+character itself.
+.It Fl s
+Concatenate all of the lines of each separate input file in command line
+order.
+The newline character of every line except the last line in each input
+file is replaced with the tab character, unless otherwise specified by
+the
+.Fl d
+option.
+.El
+.Pp
+If
+.Sq Fl
+is specified for one or more of the input files, the standard
+input is used; standard input is read one line at a time, circularly,
+for each instance of
+.Sq Fl .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+List the files in the current directory in three columns:
+.Pp
+.Dl "ls | paste - - -"
+.Pp
+Combine pairs of lines from a file into single lines:
+.Pp
+.Dl "paste -s -d '\et\en' myfile"
+.Pp
+Number the lines in a file, similar to
+.Xr nl 1 :
+.Pp
+.Dl "sed = myfile | paste -s -d '\et\en' - -"
+.Pp
+Create a colon-separated list of directories named
+.Pa bin ,
+suitable
+for use in the
+.Ev PATH
+environment variable:
+.Pp
+.Dl "find / -name bin -type d | paste -s -d : -"
+.Sh SEE ALSO
+.Xr cut 1 ,
+.Xr lam 1
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At 32v .
diff --git a/text_cmds/paste/paste.c b/text_cmds/paste/paste.c
new file mode 100644
index 0000000..20af451
--- /dev/null
+++ b/text_cmds/paste/paste.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)paste.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/paste/paste.c,v 1.14 2004/06/25 01:48:43 tjr Exp $");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <sysexits.h>
+
+wchar_t *delim;
+int delimcnt;
+
+int parallel(char **);
+int sequential(char **);
+int tr(wchar_t *);
+static void usage(void);
+
+wchar_t tab[] = L"\t";
+
+int
+main(int argc, char *argv[])
+{
+ int ch, rval, seq;
+ wchar_t *warg;
+ const char *arg;
+ size_t len;
+
+ setlocale(LC_CTYPE, "");
+
+ seq = 0;
+ while ((ch = getopt(argc, argv, "d:s")) != -1)
+ switch(ch) {
+ case 'd':
+ arg = optarg;
+ len = mbsrtowcs(NULL, &arg, 0, NULL);
+ if (len == (size_t)-1)
+ err(1, "delimiters");
+ warg = malloc((len + 1) * sizeof(*warg));
+ if (warg == NULL)
+ err(1, NULL);
+ arg = optarg;
+ len = mbsrtowcs(warg, &arg, len + 1, NULL);
+ if (len == (size_t)-1)
+ err(1, "delimiters");
+ delimcnt = tr(delim = warg);
+ break;
+ case 's':
+ seq = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+ if (!delim) {
+ delimcnt = 1;
+ delim = tab;
+ }
+
+ if (seq)
+ rval = sequential(argv);
+ else
+ rval = parallel(argv);
+ exit(rval);
+}
+
+typedef struct _list {
+ struct _list *next;
+ FILE *fp;
+ int cnt;
+ char *name;
+} LIST;
+
+int
+parallel(char **argv)
+{
+ LIST *lp;
+ int cnt;
+ wint_t ich;
+ wchar_t ch;
+ char *p;
+ LIST *head, *tmp;
+ int opencnt, output;
+
+ for (cnt = 0, head = NULL; (p = *argv); ++argv, ++cnt) {
+ if ((lp = malloc(sizeof(LIST))) == NULL)
+ err(1, NULL);
+ if (p[0] == '-' && !p[1])
+ lp->fp = stdin;
+ else if (!(lp->fp = fopen(p, "r")))
+ err(1, "%s", p);
+ lp->next = NULL;
+ lp->cnt = cnt;
+ lp->name = p;
+ if (!head)
+ head = tmp = lp;
+ else {
+ tmp->next = lp;
+ tmp = lp;
+ }
+ }
+
+ for (opencnt = cnt; opencnt;) {
+ for (output = 0, lp = head; lp; lp = lp->next) {
+ if (!lp->fp) {
+ if (output && lp->cnt &&
+ (ch = delim[(lp->cnt - 1) % delimcnt]))
+ putwchar(ch);
+ continue;
+ }
+ if ((ich = getwc(lp->fp)) == WEOF) {
+ if (!--opencnt)
+ break;
+ lp->fp = NULL;
+ if (output && lp->cnt &&
+ (ch = delim[(lp->cnt - 1) % delimcnt]))
+ putwchar(ch);
+ continue;
+ }
+ /*
+ * make sure that we don't print any delimiters
+ * unless there's a non-empty file.
+ */
+ if (!output) {
+ output = 1;
+ for (cnt = 0; cnt < lp->cnt; ++cnt)
+ if ((ch = delim[cnt % delimcnt]))
+ putwchar(ch);
+ } else if ((ch = delim[(lp->cnt - 1) % delimcnt]))
+ putwchar(ch);
+ if (ich == '\n')
+ continue;
+ do {
+ putwchar(ich);
+ } while ((ich = getwc(lp->fp)) != WEOF && ich != '\n');
+ if (ferror(lp->fp)) {
+ errx(EX_IOERR, "Error reading %s", lp->name);
+ }
+ }
+ if (output)
+ putwchar('\n');
+ }
+
+ return (0);
+}
+
+int
+sequential(char **argv)
+{
+ FILE *fp;
+ int cnt, failed, needdelim;
+ wint_t ch;
+ char *p;
+
+ failed = 0;
+ for (; (p = *argv); ++argv) {
+ if (p[0] == '-' && !p[1])
+ fp = stdin;
+ else if (!(fp = fopen(p, "r"))) {
+ warn("%s", p);
+ failed = 1;
+ continue;
+ }
+ cnt = needdelim = 0;
+ while ((ch = getwc(fp)) != WEOF) {
+ if (needdelim) {
+ needdelim = 0;
+ if (delim[cnt] != '\0')
+ putwchar(delim[cnt]);
+ if (++cnt == delimcnt)
+ cnt = 0;
+ }
+ if (ch != '\n')
+ putwchar(ch);
+ else
+ needdelim = 1;
+ }
+ if (needdelim)
+ putwchar('\n');
+ if (fp != stdin)
+ (void)fclose(fp);
+ }
+
+ return (failed != 0);
+}
+
+int
+tr(wchar_t *arg)
+{
+ int cnt;
+ wchar_t ch, *p;
+
+ for (p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
+ if (ch == '\\')
+ switch(ch = *p++) {
+ case 'n':
+ *arg = '\n';
+ break;
+ case 't':
+ *arg = '\t';
+ break;
+ case '0':
+ *arg = '\0';
+ break;
+ default:
+ *arg = ch;
+ break;
+ } else
+ *arg = ch;
+
+ if (!cnt)
+ errx(1, "no delimiters specified");
+ return(cnt);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: paste [-s] [-d delimiters] file ...\n");
+ exit(1);
+}
diff --git a/text_cmds/pr/egetopt.c b/text_cmds/pr/egetopt.c
new file mode 100644
index 0000000..46257f4
--- /dev/null
+++ b/text_cmds/pr/egetopt.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)egetopt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/pr/egetopt.c,v 1.3 2002/06/23 20:42:30 charnier Exp $");
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+/*
+ * egetopt: get option letter from argument vector (an extended
+ * version of getopt).
+ *
+ * Non standard additions to the ostr specs are:
+ * 1) '?': immediate value following arg is optional (no white space
+ * between the arg and the value)
+ * 2) '#': +/- followed by a number (with an optional sign but
+ * no white space between the arg and the number). The - may be
+ * combined with other options, but the + cannot.
+ */
+
+int eopterr = 1; /* if error message should be printed */
+int eoptind = 1; /* index into parent argv vector */
+int eoptopt; /* character checked for validity */
+char *eoptarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+
+static char emsg[] = "";
+
+int
+egetopt(int nargc, char * const *nargv, const char *ostr)
+{
+ static char *place = emsg; /* option letter processing */
+ char *oli; /* option letter list index */
+ static int delim; /* which option delimeter */
+ char *p;
+ static char savec = '\0';
+
+ if (savec != '\0') {
+ *place = savec;
+ savec = '\0';
+ }
+
+ if (!*place) {
+ /*
+ * update scanning pointer
+ */
+ if ((eoptind >= nargc) ||
+ ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) {
+ place = emsg;
+ return (-1);
+ }
+
+ delim = (int)*place;
+ if (place[1] && *++place == '-' && !place[1]) {
+ /*
+ * found "--"
+ */
+ ++eoptind;
+ place = emsg;
+ return (-1);
+ }
+ }
+
+ /*
+ * check option letter
+ */
+ if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') ||
+ !(oli = strchr(ostr, eoptopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1 when by itself.
+ */
+ if ((eoptopt == (int)'-') && !*place)
+ return (-1);
+ if (strchr(ostr, '#') && (isdigit(eoptopt) ||
+ (((eoptopt == (int)'-') || (eoptopt == (int)'+')) &&
+ isdigit(*place)))) {
+ /*
+ * # option: +/- with a number is ok
+ */
+ for (p = place; *p != '\0'; ++p) {
+ if (!isdigit(*p))
+ break;
+ }
+ eoptarg = place-1;
+
+ if (*p == '\0') {
+ place = emsg;
+ ++eoptind;
+ } else {
+ place = p;
+ savec = *p;
+ *place = '\0';
+ }
+ return (delim);
+ }
+
+ if (!*place)
+ ++eoptind;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr, "%s: illegal option -- %c\n",
+ p, eoptopt);
+ }
+ return (BADCH);
+ }
+ if (delim == (int)'+') {
+ /*
+ * '+' is only allowed with numbers
+ */
+ if (!*place)
+ ++eoptind;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr,
+ "%s: illegal '+' delimiter with option -- %c\n",
+ p, eoptopt);
+ }
+ return (BADCH);
+ }
+ ++oli;
+ if ((*oli != ':') && (*oli != '?')) {
+ /*
+ * don't need argument
+ */
+ eoptarg = NULL;
+ if (!*place)
+ ++eoptind;
+ return (eoptopt);
+ }
+
+ if (*place) {
+ /*
+ * no white space
+ */
+ eoptarg = place;
+ } else if (*oli == '?') {
+ /*
+ * no arg, but NOT required
+ */
+ eoptarg = NULL;
+ } else if (nargc <= ++eoptind) {
+ /*
+ * no arg, but IS required
+ */
+ place = emsg;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n", p,
+ eoptopt);
+ }
+ return (BADCH);
+ } else {
+ /*
+ * arg has white space
+ */
+ eoptarg = nargv[eoptind];
+ }
+ place = emsg;
+ ++eoptind;
+ return (eoptopt);
+}
diff --git a/text_cmds/pr/extern.h b/text_cmds/pr/extern.h
new file mode 100644
index 0000000..d3a999b
--- /dev/null
+++ b/text_cmds/pr/extern.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.bin/pr/extern.h,v 1.3 2002/09/04 23:29:05 dwmalone Exp $
+ */
+
+extern int eoptind;
+extern char *eoptarg;
+
+void addnum(char *, int, int);
+int egetopt(int, char * const *, const char *);
+void flsh_errs(void);
+int horzcol(int, char **);
+int inln(FILE *, char *, int, int *, int, int *);
+int inskip(FILE *, int, int);
+void mfail(void);
+int mulfile(int, char **);
+FILE *nxtfile(int, char **, const char **, char *, int);
+int onecol(int, char **);
+int otln(char *, int, int *, int *, int);
+void pfail(void);
+int prhead(char *, const char *, int);
+int prtail(int, int);
+int setup(int, char **);
+void terminate(int);
+void usage(void);
+int vertcol(int, char **);
diff --git a/text_cmds/pr/pr.1 b/text_cmds/pr/pr.1
new file mode 100644
index 0000000..d5b0884
--- /dev/null
+++ b/text_cmds/pr/pr.1
@@ -0,0 +1,402 @@
+.\" Copyright (c) 1991 Keith Muller.
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Keith Muller of the University of California, San Diego.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pr.1 8.3 (Berkeley) 4/18/94
+.\" $FreeBSD: src/usr.bin/pr/pr.1,v 1.22 2005/01/18 13:43:52 ru Exp $
+.\"
+.Dd July 3, 2004
+.Dt PR 1
+.Os
+.Sh NAME
+.Nm pr
+.Nd print files
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Ar \&+page
+.Ek
+.Bk -words
+.Op Fl Ar column
+.Ek
+.Op Fl adFfmprt
+.Bk -words
+.Oo
+.Op Fl e
+.Op Ar char
+.Op Ar gap
+.Oc
+.Ek
+.Bk -words
+.Op Fl L Ar locale
+.Ek
+.Bk -words
+.Op Fl h Ar header
+.Ek
+.Bk -words
+.Oo
+.Op Fl i
+.Op Ar char
+.Op Ar gap
+.Oc
+.Ek
+.Bk -words
+.Op Fl l Ar lines
+.Ek
+.Bk -words
+.Op Fl o Ar offset
+.Ek
+.Bk -words
+.Oo
+.Op Fl s
+.Op Ar char
+.Oc
+.Ek
+.Bk -words
+.Oo
+.Op Fl n
+.Op Ar char
+.Op Ar width
+.Oc
+.Ek
+.Bk -words
+.Op Fl w Ar width
+.Ek
+.Op -
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is a printing and pagination filter for text files.
+When multiple input files are specified, each is read, formatted,
+and written to standard output.
+By default, the input is separated into 66-line pages, each with
+.Bl -bullet
+.It
+A 5-line header with the page number, date, time, and
+the pathname of the file.
+.It
+A 5-line trailer consisting of blank lines.
+.El
+.Pp
+If standard output is associated with a terminal,
+diagnostic messages are suppressed until the
+.Nm
+utility has completed processing.
+.Pp
+When multiple column output is specified,
+text columns are of equal width.
+By default text columns are separated by at least one
+.Em <blank> .
+Input lines that do not fit into a text column are truncated.
+Lines are not truncated under single column output.
+.Sh OPTIONS
+In the following option descriptions, column, lines, offset, page, and
+width are positive decimal integers and gap is a nonnegative decimal integer.
+.Bl -tag -width 4n
+.It Ar \&+page
+Begin output at page number
+.Ar page
+of the formatted input.
+.It Fl Ar column
+Produce output that is
+.Ar columns
+wide (default is 1) that is written vertically
+down each column in the order in which the text
+is received from the input file.
+The options
+.Fl e
+and
+.Fl i
+are assumed.
+This option should not be used with
+.Fl m .
+When used with
+.Fl t ,
+the minimum number of lines is used to display the output.
+(To columnify and reshape text files more generally and without additional
+formatting, see the
+.Xr rs 1
+utility.)
+.It Fl a
+Modify the effect of the
+.Fl column
+option so that the columns are filled across the page in a round-robin order
+(e.g., when column is 2, the first input line heads column
+1, the second heads column 2, the third is the second line
+in column 1, etc.).
+This option requires the use of the
+.Fl column
+option.
+.It Fl d
+Produce output that is double spaced.
+An extra
+.Em <newline>
+character is output following every
+.Em <newline>
+found in the input.
+.It Fl e Xo
+.Op Ar char Ns
+.Op Ar gap
+.Xc
+Expand each input
+.Em <tab>
+to the next greater column
+position specified by the formula
+.Ar n*gap+1 ,
+where
+.Em n
+is an integer > 0.
+If
+.Ar gap
+is zero or is omitted the default is 8.
+All
+.Em <tab>
+characters in the input are expanded into the appropriate
+number of
+.Em <space>s .
+If any nondigit character,
+.Ar char ,
+is specified, it is used as the input tab character.
+.It Fl F
+Use a
+.Em <form-feed>
+character for new pages,
+instead of the default behavior that uses a
+sequence of
+.Em <newline>
+characters.
+.It Fl f
+Same as
+.Fl F
+but pause before beginning the first page if standard output is a terminal.
+.It Fl h Ar header
+Use the string
+.Ar header
+to replace the
+.Ar file name
+in the header line.
+.It Fl i Xo
+.Op Ar char Ns
+.Op Ar gap
+.Xc
+In output, replace multiple
+.Em <space>s
+with
+.Em <tab>s
+whenever two or more
+adjacent
+.Em <space>s
+reach column positions
+.Ar gap+1 ,
+.Ar 2*gap+1 ,
+etc.
+If
+.Ar gap
+is zero or omitted, default
+.Em <tab>
+settings at every eighth column position
+is used.
+If any nondigit character,
+.Ar char ,
+is specified, it is used as the output
+.Em <tab>
+character.
+.It Fl L Ar locale
+Use
+.Ar locale
+specified as argument instead of one found in environment.
+Use "C" to reset locale to default.
+.It Fl l Ar lines
+Override the 66 line default and reset the page length to
+.Ar lines .
+If
+.Ar lines
+is not greater than the sum of both the header and trailer
+depths (in lines), the
+.Nm
+utility suppresses output of both the header and trailer, as if the
+.Fl t
+option were in effect.
+.It Fl m
+Merge the contents of multiple files.
+One line from each file specified by a file operand is
+written side by side into text columns of equal fixed widths, in
+terms of the number of column positions.
+The number of text columns depends on the number of
+file operands successfully opened.
+The maximum number of files merged depends on page width and the
+per process open file limit.
+The options
+.Fl e
+and
+.Fl i
+are assumed.
+.It Fl n Xo
+.Op Ar char Ns
+.Op Ar width
+.Xc
+Provide
+.Ar width
+digit line numbering.
+The default for
+.Ar width ,
+if not specified, is 5.
+The number occupies the first
+.Ar width
+column positions of each text column or each line of
+.Fl m
+output.
+If
+.Ar char
+(any nondigit character) is given, it is appended to the line number to
+separate it from whatever follows.
+The default for
+.Ar char
+is a
+.Em <tab> .
+Line numbers longer than
+.Ar width
+columns are truncated.
+.It Fl o Ar offset
+Each line of output is preceded by
+.Ar offset
+.Em <spaces>s .
+If the
+.Fl o
+option is not specified, the default is zero.
+The space taken is in addition to the output line width.
+.It Fl p
+Pause before each page if the standard output is a terminal.
+.Nm
+will write an alert character to standard error and wait for a carriage
+return to be read on the terminal.
+.It Fl r
+Write no diagnostic reports on failure to open a file.
+.It Fl s Ar char
+Separate text columns by the single character
+.Ar char
+instead of by the appropriate number of
+.Em <space>s
+(default for
+.Ar char
+is the
+.Em <tab>
+character).
+.It Fl t
+Print neither the five-line identifying
+header nor the five-line trailer usually supplied for each page.
+Quit printing after the last line of each file without spacing to the
+end of the page.
+.It Fl w Ar width
+Set the width of the line to
+.Ar width
+column positions for multiple text-column output only.
+If the
+.Fl w
+option is not specified and the
+.Fl s
+option is not specified, the default width is 72.
+If the
+.Fl w
+option is not specified and the
+.Fl s
+option is specified, the default width is 512.
+.It Ar file
+A pathname of a file to be printed.
+If no
+.Ar file
+operands are specified, or if a
+.Ar file
+operand is
+.Sq Fl ,
+the standard input is used.
+The standard input is used only if no
+.Ar file
+operands are specified, or if a
+.Ar file
+operand is
+.Sq Fl .
+.El
+.Pp
+The
+.Fl s
+option does not allow the option letter to be separated from its
+argument, and the options
+.Fl e ,
+.Fl i ,
+and
+.Fl n
+require that both arguments, if present, not be separated from the option
+letter.
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and 1 if an error occurs.
+.Sh DIAGNOSTICS
+If
+.Nm
+receives an interrupt while printing to a terminal, it
+flushes all accumulated error messages to the screen before
+terminating.
+.Pp
+Error messages are written to standard error during the printing
+process (if output is redirected) or after all successful
+file printing is complete (when printing to a terminal).
+.Sh LEGACY DESCRIPTION
+The last space before the tab stop is replaced with a tab character.
+In legacy mode, it is not.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr cat 1 ,
+.Xr more 1 ,
+.Xr rs 1 ,
+.Xr compat 5
+.Sh STANDARDS
+The
+.Nm
+utility is
+.St -p1003.1-2001
+compatible.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
+.Sh BUGS
+The
+.Nm
+utility does not recognize multibyte characters.
diff --git a/text_cmds/pr/pr.c b/text_cmds/pr/pr.c
new file mode 100644
index 0000000..f2d40e3
--- /dev/null
+++ b/text_cmds/pr/pr.c
@@ -0,0 +1,1856 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)pr.c 8.2 (Berkeley) 4/16/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/pr/pr.c,v 1.18 2004/07/26 20:24:59 charnier Exp $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#include "pr.h"
+#include "extern.h"
+
+#ifdef __APPLE__
+#include <get_compat.h>
+/* err.h defines err(1) which conflicts with the global below */
+extern void errx(int, const char *, ...) __dead2 __printflike(2, 3);
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+/*
+ * pr: a printing and pagination filter. If multiple input files
+ * are specified, each is read, formatted, and written to standard
+ * output. By default, input is separated into 66-line pages, each
+ * with a header that includes the page number, date, time and the
+ * files pathname.
+ *
+ * Complies with posix P1003.2/D11
+ */
+
+/*
+ * parameter variables
+ */
+int pgnm; /* starting page number */
+int clcnt; /* number of columns */
+int colwd; /* column data width - multiple columns */
+int across; /* mult col flag; write across page */
+int dspace; /* double space flag */
+char inchar; /* expand input char */
+int ingap; /* expand input gap */
+int pausefst; /* Pause before first page */
+int pauseall; /* Pause before each page */
+int formfeed; /* use formfeed as trailer */
+char *header; /* header name instead of file name */
+char ochar; /* contract output char */
+int ogap; /* contract output gap */
+int lines; /* number of lines per page */
+int merge; /* merge multiple files in output */
+char nmchar; /* line numbering append char */
+int nmwd; /* width of line number field */
+int offst; /* number of page offset spaces */
+int nodiag; /* do not report file open errors */
+char schar; /* text column separation character */
+int sflag; /* -s option for multiple columns */
+int nohead; /* do not write head and trailer */
+int pgwd; /* page width with multiple col output */
+const char *timefrmt; /* time conversion string */
+
+/*
+ * misc globals
+ */
+FILE *err; /* error message file pointer */
+int addone; /* page length is odd with double space */
+int errcnt; /* error count on file processing */
+char digs[] = "0123456789"; /* page number translation map */
+
+char fnamedefault[] = FNAME;
+
+static char first_char; /* first fill character */
+
+int
+main(int argc, char *argv[])
+{
+ int ret_val;
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, terminate);
+ ret_val = setup(argc, argv);
+ first_char = (COMPAT_MODE("bin/pr", "Unix2003") ? ochar : ' ');
+ if (!ret_val) {
+ /*
+ * select the output format based on options
+ */
+ if (merge)
+ ret_val = mulfile(argc, argv);
+ else if (clcnt == 1)
+ ret_val = onecol(argc, argv);
+ else if (across)
+ ret_val = horzcol(argc, argv);
+ else
+ ret_val = vertcol(argc, argv);
+ } else
+ usage();
+ flsh_errs();
+ if (errcnt || ret_val)
+ exit(1);
+ return(0);
+}
+
+/*
+ * Check if we should pause and write an alert character and wait for a
+ * carriage return on /dev/tty.
+ */
+static void
+ttypause(int pagecnt)
+{
+ int pch;
+ FILE *ttyfp;
+
+ if ((pauseall || (pausefst && pagecnt == 1)) &&
+ isatty(STDOUT_FILENO)) {
+ if ((ttyfp = fopen("/dev/tty", "r")) != NULL) {
+ (void)putc('\a', stderr);
+ while ((pch = getc(ttyfp)) != '\n' && pch != EOF)
+ ;
+ (void)fclose(ttyfp);
+ }
+ }
+}
+
+/*
+ * onecol: print files with only one column of output.
+ * Line length is unlimited.
+ */
+int
+onecol(int argc, char *argv[])
+{
+ int cnt = -1;
+ int off;
+ int lrgln;
+ int linecnt;
+ int num;
+ int lncnt;
+ int pagecnt;
+ int ips;
+ int ops;
+ int cps;
+ char *obuf;
+ char *lbuf;
+ char *nbuf;
+ char *hbuf;
+ char *ohbuf;
+ FILE *inf;
+ const char *fname;
+ int mor;
+
+ if (nmwd)
+ num = nmwd + 1;
+ else
+ num = 0;
+ off = num + offst;
+
+ /*
+ * allocate line buffer
+ */
+ if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ /*
+ * allocate header buffer
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ ohbuf = hbuf + offst;
+ nbuf = obuf + offst;
+ lbuf = nbuf + num;
+ if (num)
+ nbuf[--num] = nmchar;
+ if (offst) {
+ (void)memset(obuf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ /*
+ * skip to specified page
+ */
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ linecnt = 0;
+ lrgln = 0;
+ ops = 0;
+ ips = 0;
+ cps = 0;
+
+ ttypause(pagecnt);
+
+ /*
+ * loop by line
+ */
+ while (linecnt < lines) {
+ /*
+ * input next line
+ */
+ if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
+ break;
+ if (!linecnt && !nohead &&
+ prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * start of new line.
+ */
+ if (!lrgln) {
+ if (num)
+ addnum(nbuf, num, ++lncnt);
+ if (otln(obuf,cnt+off, &ips, &ops, mor))
+ return(1);
+ } else if (otln(lbuf, cnt, &ips, &ops, mor))
+ return(1);
+
+ /*
+ * if line bigger than buffer, get more
+ */
+ if (mor) {
+ lrgln = 1;
+ continue;
+ }
+
+ /*
+ * whole line rcvd. reset tab proc. state
+ */
+ ++linecnt;
+ lrgln = 0;
+ ops = 0;
+ ips = 0;
+ }
+
+ /*
+ * fill to end of page
+ */
+ if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
+ return(1);
+
+ /*
+ * On EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * vertcol: print files with more than one column of output down a page
+ */
+int
+vertcol(int argc, char *argv[])
+{
+ char *ptbf;
+ char **lstdat;
+ int i;
+ int j;
+ int cnt = -1;
+ int pln;
+ int *indy;
+ int cvc;
+ int *lindy;
+ int lncnt;
+ int stp;
+ int pagecnt;
+ int col = colwd + 1;
+ int mxlen = pgwd + offst + 1;
+ int mclcnt = clcnt - 1;
+ struct vcol *vc;
+ int mvc;
+ int tvc;
+ int cw = nmwd + 1;
+ int fullcol;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ const char *fname;
+ FILE *inf;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ /*
+ * allocate page buffer
+ */
+ if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * allocate page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+ if (offst)
+ (void)memset(hbuf, (int)' ', offst);
+
+ /*
+ * col pointers when no headers
+ */
+ mvc = lines * clcnt;
+ if ((vc =
+ (struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * pointer into page where last data per line is located
+ */
+ if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){
+ mfail();
+ return(1);
+ }
+
+ /*
+ * fast index lookups to locate start of lines
+ */
+ if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
+ mfail();
+ return(1);
+ }
+ if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ if (nmwd)
+ fullcol = col + cw;
+ else
+ fullcol = col;
+
+ /*
+ * initialize buffer lookup indexes and offset area
+ */
+ for (j = 0; j < lines; ++j) {
+ lindy[j] = j * mxlen;
+ indy[j] = lindy[j] + offst;
+ if (offst) {
+ ptbf = buf + lindy[j];
+ (void)memset(ptbf, (int)' ', offst);
+ ptbf += offst;
+ } else
+ ptbf = buf + indy[j];
+ lstdat[j] = ptbf;
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ /*
+ * skip to requested page
+ */
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ ttypause(pagecnt);
+
+ /*
+ * loop by column
+ */
+ cvc = 0;
+ for (i = 0; i < clcnt; ++i) {
+ j = 0;
+ /*
+ * if last column, do not pad
+ */
+ if (i == mclcnt)
+ stp = 1;
+ else
+ stp = 0;
+ /*
+ * loop by line
+ */
+ for(;;) {
+ /*
+ * is this first column
+ */
+ if (!i) {
+ ptbf = buf + indy[j];
+ lstdat[j] = ptbf;
+ } else
+ ptbf = lstdat[j];
+ vc[cvc].pt = ptbf;
+
+ /*
+ * add number
+ */
+ if (nmwd) {
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+
+ /*
+ * input next line
+ */
+ cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
+ vc[cvc++].cnt = cnt;
+ if (cnt < 0)
+ break;
+ ptbf += cnt;
+
+ /*
+ * pad all but last column on page
+ */
+ if (!stp) {
+ /*
+ * pad to end of column
+ */
+ if (sflag)
+ *ptbf++ = schar;
+ else if ((pln = col-cnt) > 0) {
+ (void)memset(ptbf,
+ (int)' ',pln);
+ ptbf += pln;
+ }
+ }
+ /*
+ * remember last char in line
+ */
+ lstdat[j] = ptbf;
+ if (++j >= lines)
+ break;
+ }
+ if (cnt < 0)
+ break;
+ }
+
+ /*
+ * when -t (no header) is specified the spec requires
+ * the min number of lines. The last page may not have
+ * balanced length columns. To fix this we must reorder
+ * the columns. This is a very slow technique so it is
+ * only used under limited conditions. Without -t, the
+ * balancing of text columns is unspecified. To NOT
+ * balance the last page, add the global variable
+ * nohead to the if statement below e.g.
+ *
+ * if ((cnt < 0) && nohead && cvc ......
+ */
+ --cvc;
+
+ /*
+ * check to see if last page needs to be reordered
+ */
+ if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
+ pln = cvc/clcnt;
+ if (cvc % clcnt)
+ ++pln;
+
+ /*
+ * print header
+ */
+ if (!nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+ for (i = 0; i < pln; ++i) {
+ ips = 0;
+ ops = 0;
+ if (offst&& otln(buf,offst,&ips,&ops,1))
+ return(1);
+ tvc = i;
+
+ for (j = 0; j < clcnt; ++j) {
+ /*
+ * determine column length
+ */
+ if (j == mclcnt) {
+ /*
+ * last column
+ */
+ cnt = vc[tvc].cnt;
+ if (nmwd)
+ cnt += cw;
+ } else if (sflag) {
+ /*
+ * single ch between
+ */
+ cnt = vc[tvc].cnt + 1;
+ if (nmwd)
+ cnt += cw;
+ } else
+ cnt = fullcol;
+ if (otln(vc[tvc].pt, cnt, &ips,
+ &ops, 1))
+ return(1);
+ tvc += pln;
+ if (tvc >= cvc)
+ break;
+ }
+ /*
+ * terminate line
+ */
+ if (otln(buf, 0, &ips, &ops, 0))
+ return(1);
+ }
+ /*
+ * pad to end of page
+ */
+ if (prtail((lines - pln), 0))
+ return(1);
+ /*
+ * done with output, go to next file
+ */
+ break;
+ }
+
+ /*
+ * determine how many lines to output
+ */
+ if (i > 0)
+ pln = lines;
+ else
+ pln = j;
+
+ /*
+ * print header
+ */
+ if (pln && !nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * output each line
+ */
+ for (i = 0; i < pln; ++i) {
+ ptbf = buf + lindy[i];
+ if ((j = lstdat[i] - ptbf) <= offst)
+ break;
+ if (otln(ptbf, j, &ips, &ops, 0))
+ return(1);
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (pln && prtail((lines - pln), 0))
+ return(1);
+
+ /*
+ * if EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * horzcol: print files with more than one column of output across a page
+ */
+int
+horzcol(int argc, char *argv[])
+{
+ char *ptbf;
+ int pln;
+ int cnt = -1;
+ char *lstdat;
+ int col = colwd + 1;
+ int j;
+ int i;
+ int lncnt;
+ int pagecnt;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ const char *fname;
+ FILE *inf;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+ if (offst) {
+ (void)memset(buf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ ttypause(pagecnt);
+
+ /*
+ * loop by line
+ */
+ for (i = 0; i < lines; ++i) {
+ ptbf = buf + offst;
+ lstdat = ptbf;
+ j = 0;
+ /*
+ * loop by col
+ */
+ for(;;) {
+ if (nmwd) {
+ /*
+ * add number to column
+ */
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+ /*
+ * input line
+ */
+ if ((cnt = inln(inf,ptbf,colwd,&cps,1,
+ &mor)) < 0)
+ break;
+ ptbf += cnt;
+ lstdat = ptbf;
+
+ /*
+ * if last line skip padding
+ */
+ if (++j >= clcnt)
+ break;
+
+ /*
+ * pad to end of column
+ */
+ if (sflag)
+ *ptbf++ = schar;
+ else if ((pln = col - cnt) > 0) {
+ (void)memset(ptbf,(int)' ',pln);
+ ptbf += pln;
+ }
+ }
+
+ /*
+ * determine line length
+ */
+ if ((j = lstdat - buf) <= offst)
+ break;
+ if (!i && !nohead &&
+ prhead(hbuf, fname, pagecnt))
+ return(1);
+ /*
+ * output line
+ */
+ if (otln(buf, j, &ips, &ops, 0))
+ return(1);
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (i && prtail(lines-i, 0))
+ return(1);
+
+ /*
+ * if EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * mulfile: print files with more than one column of output and
+ * more than one file concurrently
+ */
+int
+mulfile(int argc, char *argv[])
+{
+ char *ptbf;
+ int j;
+ int pln;
+ int cnt;
+ char *lstdat;
+ int i;
+ FILE **fbuf;
+ int actf;
+ int lncnt;
+ int col;
+ int pagecnt;
+ int fproc;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ const char *fname;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ /*
+ * array of FILE *, one for each operand
+ */
+ if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+
+ /*
+ * do not know how many columns yet. The number of operands provide an
+ * upper bound on the number of columns. We use the number of files
+ * we can open successfully to set the number of columns. The operation
+ * of the merge operation (-m) in relation to unsuccesful file opens
+ * is unspecified by posix.
+ */
+ j = 0;
+ while (j < clcnt) {
+ if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
+ break;
+ if (pgnm && (inskip(fbuf[j], pgnm, lines)))
+ fbuf[j] = NULL;
+ ++j;
+ }
+
+ /*
+ * if no files, exit
+ */
+ if (!j)
+ return(1);
+
+ /*
+ * calculate page boundries based on open file count
+ */
+ clcnt = j;
+ if (nmwd) {
+ colwd = (pgwd - clcnt - nmwd)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
+ } else {
+ colwd = (pgwd + 1 - clcnt)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - 1;
+ }
+ if (colwd < 1) {
+ (void)fprintf(err,
+ "pr: page width too small for %d columns\n", clcnt);
+ return(1);
+ }
+ actf = clcnt;
+ col = colwd + 1;
+
+ /*
+ * line buffer
+ */
+ if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ if (offst) {
+ (void)memset(buf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+ if (pgnm)
+ pagecnt = pgnm;
+ else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * continue to loop while any file still has data
+ */
+ while (actf > 0) {
+ ttypause(pagecnt);
+
+ /*
+ * loop by line
+ */
+ for (i = 0; i < lines; ++i) {
+ ptbf = buf + offst;
+ lstdat = ptbf;
+ if (nmwd) {
+ /*
+ * add line number to line
+ */
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+ j = 0;
+ fproc = 0;
+
+ /*
+ * loop by column
+ */
+ for (j = 0; j < clcnt; ++j) {
+ if (fbuf[j] == NULL) {
+ /*
+ * empty column; EOF
+ */
+ cnt = 0;
+ } else if ((cnt = inln(fbuf[j], ptbf, colwd,
+ &cps, 1, &mor)) < 0) {
+ /*
+ * EOF hit; no data
+ */
+ if (fbuf[j] != stdin)
+ (void)fclose(fbuf[j]);
+ fbuf[j] = NULL;
+ --actf;
+ cnt = 0;
+ } else {
+ /*
+ * process file data
+ */
+ ptbf += cnt;
+ lstdat = ptbf;
+ fproc++;
+ }
+
+ /*
+ * if last ACTIVE column, done with line
+ */
+ if (fproc >= actf)
+ break;
+
+ /*
+ * pad to end of column
+ */
+ if (sflag) {
+ *ptbf++ = schar;
+ } else if ((pln = col - cnt) > 0) {
+ (void)memset(ptbf, (int)' ', pln);
+ ptbf += pln;
+ }
+ }
+
+ /*
+ * calculate data in line
+ */
+ if ((j = lstdat - buf) <= offst)
+ break;
+
+ if (!i && !nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * output line
+ */
+ if (otln(buf, j, &ips, &ops, 0))
+ return(1);
+
+ /*
+ * if no more active files, done
+ */
+ if (actf <= 0) {
+ ++i;
+ break;
+ }
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (i && prtail(lines-i, 0))
+ return(1);
+ ++pagecnt;
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * inln(): input a line of data (unlimited length lines supported)
+ * Input is optionally expanded to spaces
+ *
+ * inf: file
+ * buf: buffer
+ * lim: buffer length
+ * cps: column positon 1st char in buffer (large line support)
+ * trnc: throw away data more than lim up to \n
+ * mor: set if more data in line (not truncated)
+ */
+int
+inln(FILE *inf, char *buf, int lim, int *cps, int trnc, int *mor)
+{
+ int col;
+ int gap = ingap;
+ int ch = EOF;
+ char *ptbuf;
+ int chk = (int)inchar;
+
+ ptbuf = buf;
+
+ if (gap) {
+ /*
+ * expanding input option
+ */
+ while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
+ /*
+ * is this the input "tab" char
+ */
+ if (ch == chk) {
+ /*
+ * expand to number of spaces
+ */
+ col = (ptbuf - buf) + *cps;
+ col = gap - (col % gap);
+
+ /*
+ * if more than this line, push back
+ */
+ if ((col > lim) && (ungetc(ch, inf) == EOF))
+ return(1);
+
+ /*
+ * expand to spaces
+ */
+ while ((--col >= 0) && (--lim >= 0))
+ *ptbuf++ = ' ';
+ continue;
+ }
+ if (ch == '\n')
+ break;
+ *ptbuf++ = ch;
+ }
+ } else {
+ /*
+ * no expansion
+ */
+ while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
+ if (ch == '\n')
+ break;
+ *ptbuf++ = ch;
+ }
+ }
+ col = ptbuf - buf;
+ if (ch == EOF) {
+ if (ferror(inf)) {
+ errx(EX_IOERR, NULL);
+ }
+ *mor = 0;
+ *cps = 0;
+ if (!col)
+ return(-1);
+ return(col);
+ }
+ if (ch == '\n') {
+ /*
+ * entire line processed
+ */
+ *mor = 0;
+ *cps = 0;
+ return(col);
+ }
+
+ /*
+ * line was larger than limit
+ */
+ if (trnc) {
+ /*
+ * throw away rest of line
+ */
+ while ((ch = getc(inf)) != EOF) {
+ if (ch == '\n')
+ break;
+ }
+ if (ferror(inf)) {
+ errx(EX_IOERR, NULL);
+ }
+ *cps = 0;
+ *mor = 0;
+ } else {
+ /*
+ * save column offset if not truncated
+ */
+ *cps += col;
+ *mor = 1;
+ }
+
+ return(col);
+}
+
+/*
+ * otln(): output a line of data. (Supports unlimited length lines)
+ * output is optionally contracted to tabs
+ *
+ * buf: output buffer with data
+ * cnt: number of chars of valid data in buf
+ * svips: buffer input column position (for large lines)
+ * svops: buffer output column position (for large lines)
+ * mor: output line not complete in this buf; more data to come.
+ * 1 is more, 0 is complete, -1 is no \n's
+ */
+int
+otln(char *buf, int cnt, int *svips, int *svops, int mor)
+{
+ int ops; /* last col output */
+ int ips; /* last col in buf examined */
+ int gap = ogap;
+ int tbps;
+ char *endbuf;
+
+ if (ogap) {
+ /*
+ * contracting on output
+ */
+ endbuf = buf + cnt;
+ ops = *svops;
+ ips = *svips;
+ while (buf < endbuf) {
+ /*
+ * count number of spaces and ochar in buffer
+ */
+ if (*buf == ' ') {
+ ++ips;
+ ++buf;
+ continue;
+ }
+
+ /*
+ * simulate ochar processing
+ */
+ if (*buf == ochar) {
+ ips += gap - (ips % gap);
+ ++buf;
+ continue;
+ }
+
+ /*
+ * got a non space char; contract out spaces
+ */
+ while (ips - ops > 1) {
+ /*
+ * use as many ochar as will fit
+ */
+ if ((tbps = ops + gap - (ops % gap)) > ips)
+ break;
+ if (gap - 1 == (ops % gap)) /* use space to get to immediately following tab stop */
+ putchar(first_char);
+ else if (putchar(ochar) == EOF) {
+ pfail();
+ return(1);
+ }
+ ops = tbps;
+ }
+
+ while (ops < ips) {
+ /*
+ * finish off with spaces
+ */
+ if (putchar(' ') == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ops;
+ }
+
+ /*
+ * output non space char
+ */
+ if (putchar(*buf++) == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ips;
+ ++ops;
+ }
+
+ if (mor > 0) {
+ /*
+ * if incomplete line, save position counts
+ */
+ *svops = ops;
+ *svips = ips;
+ return(0);
+ }
+
+ if (mor < 0) {
+ while (ips - ops > 1) {
+ /*
+ * use as many ochar as will fit
+ */
+ if ((tbps = ops + gap - (ops % gap)) > ips)
+ break;
+ if (putchar(ochar) == EOF) {
+ pfail();
+ return(1);
+ }
+ ops = tbps;
+ }
+ while (ops < ips) {
+ /*
+ * finish off with spaces
+ */
+ if (putchar(' ') == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ops;
+ }
+ return(0);
+ }
+ } else {
+ /*
+ * output is not contracted
+ */
+ if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
+ pfail();
+ return(1);
+ }
+ if (mor != 0)
+ return(0);
+ }
+
+ /*
+ * process line end and double space as required
+ */
+ if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
+ pfail();
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * inskip(): skip over pgcnt pages with lncnt lines per page
+ * file is closed at EOF (if not stdin).
+ *
+ * inf FILE * to read from
+ * pgcnt number of pages to skip
+ * lncnt number of lines per page
+ */
+int
+inskip(FILE *inf, int pgcnt, int lncnt)
+{
+ int c;
+ int cnt;
+
+ while(--pgcnt > 0) {
+ cnt = lncnt;
+ while ((c = getc(inf)) != EOF) {
+ if ((c == '\n') && (--cnt == 0))
+ break;
+ }
+ if (ferror(inf)) {
+ errx(EX_IOERR, NULL);
+ }
+ if (c == EOF) {
+ if (inf != stdin)
+ (void)fclose(inf);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * nxtfile: returns a FILE * to next file in arg list and sets the
+ * time field for this file (or current date).
+ *
+ * buf array to store proper date for the header.
+ * dt if set skips the date processing (used with -m)
+ */
+FILE *
+nxtfile(int argc, char **argv, const char **fname, char *buf, int dt)
+{
+ FILE *inf = NULL;
+ struct timeval tv;
+ time_t tv_sec;
+ struct timezone tz;
+ struct tm *timeptr = NULL;
+ struct stat statbuf;
+ static int twice = -1;
+
+ ++twice;
+ if (eoptind >= argc) {
+ /*
+ * no file listed; default, use standard input
+ */
+ if (twice)
+ return(NULL);
+ clearerr(stdin);
+ inf = stdin;
+ if (header != NULL)
+ *fname = header;
+ else
+ *fname = fnamedefault;
+ if (nohead)
+ return(inf);
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err, "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ eoptind = argc - 1;
+ return(NULL);
+ }
+ tv_sec = tv.tv_sec;
+ timeptr = localtime(&tv_sec);
+ }
+ for (; eoptind < argc && argv[eoptind]; ++eoptind) {
+ if (strcmp(argv[eoptind], "-") == 0) {
+ /*
+ * process a "-" for filename
+ */
+ clearerr(stdin);
+ inf = stdin;
+ if (header != NULL)
+ *fname = header;
+ else
+ *fname = fnamedefault;
+ ++eoptind;
+ if (nohead || (dt && twice))
+ return(inf);
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err,
+ "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ return(NULL);
+ }
+ tv_sec = tv.tv_sec;
+ timeptr = localtime(&tv_sec);
+ } else {
+ /*
+ * normal file processing
+ */
+ if ((inf = fopen(argv[eoptind], "r")) == NULL) {
+ ++errcnt;
+ if (nodiag)
+ continue;
+ (void)fprintf(err, "pr: cannot open %s, %s\n",
+ argv[eoptind], strerror(errno));
+ continue;
+ }
+ if (header != NULL)
+ *fname = header;
+ else if (dt)
+ *fname = fnamedefault;
+ else
+ *fname = argv[eoptind];
+ ++eoptind;
+ if (nohead || (dt && twice))
+ return(inf);
+
+ if (dt) {
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err,
+ "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ return(NULL);
+ }
+ tv_sec = tv.tv_sec;
+ timeptr = localtime(&tv_sec);
+ } else {
+ if (fstat(fileno(inf), &statbuf) < 0) {
+ ++errcnt;
+ (void)fclose(inf);
+ (void)fprintf(err,
+ "pr: cannot stat %s, %s\n",
+ argv[eoptind], strerror(errno));
+ return(NULL);
+ }
+ timeptr = localtime(&(statbuf.st_mtime));
+ }
+ }
+ break;
+ }
+ if (inf == NULL)
+ return(NULL);
+
+ /*
+ * set up time field used in header
+ */
+ if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
+ ++errcnt;
+ if (inf != stdin)
+ (void)fclose(inf);
+ (void)fputs("pr: time conversion failed\n", err);
+ return(NULL);
+ }
+ return(inf);
+}
+
+/*
+ * addnum(): adds the line number to the column
+ * Truncates from the front or pads with spaces as required.
+ * Numbers are right justified.
+ *
+ * buf buffer to store the number
+ * wdth width of buffer to fill
+ * line line number
+ *
+ * NOTE: numbers occupy part of the column. The posix
+ * spec does not specify if -i processing should or should not
+ * occur on number padding. The spec does say it occupies
+ * part of the column. The usage of addnum currently treats
+ * numbers as part of the column so spaces may be replaced.
+ */
+void
+addnum(char *buf, int wdth, int line)
+{
+ char *pt = buf + wdth;
+
+ do {
+ *--pt = digs[line % 10];
+ line /= 10;
+ } while (line && (pt > buf));
+
+ /*
+ * pad with space as required
+ */
+ while (pt > buf)
+ *--pt = ' ';
+}
+
+/*
+ * prhead(): prints the top of page header
+ *
+ * buf buffer with time field (and offset)
+ * cnt number of chars in buf
+ * fname fname field for header
+ * pagcnt page number
+ */
+int
+prhead(char *buf, const char *fname, int pagcnt)
+{
+ int ips = 0;
+ int ops = 0;
+
+ if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
+ pfail();
+ return(1);
+ }
+ /*
+ * posix is not clear if the header is subject to line length
+ * restrictions. The specification for header line format
+ * in the spec clearly does not limit length. No pr currently
+ * restricts header length. However if we need to truncate in
+ * a reasonable way, adjust the length of the printf by
+ * changing HDFMT to allow a length max as an arguement printf.
+ * buf (which contains the offset spaces and time field could
+ * also be trimmed
+ *
+ * note only the offset (if any) is processed for tab expansion
+ */
+ if (offst && otln(buf, offst, &ips, &ops, -1))
+ return(1);
+ (void)printf(HDFMT,buf+offst, fname, pagcnt);
+ return(0);
+}
+
+/*
+ * prtail(): pad page with empty lines (if required) and print page trailer
+ * if requested
+ *
+ * cnt number of lines of padding needed
+ * incomp was a '\n' missing from last line output
+ */
+int
+prtail(int cnt, int incomp)
+{
+ if (nohead) {
+ /*
+ * only pad with no headers when incomplete last line
+ */
+ if (incomp &&
+ ((dspace && (putchar('\n') == EOF)) ||
+ (putchar('\n') == EOF))) {
+ pfail();
+ return(1);
+ }
+ /*
+ * but honor the formfeed request
+ */
+ if (formfeed) {
+ if (putchar('\f') == EOF) {
+ pfail();
+ return(1);
+ }
+ }
+ return(0);
+ }
+ /*
+ * if double space output two \n
+ */
+ if (dspace)
+ cnt *= 2;
+
+ /*
+ * if an odd number of lines per page, add an extra \n
+ */
+ if (addone)
+ ++cnt;
+
+ /*
+ * pad page
+ */
+ if (formfeed) {
+ if ((incomp && (putchar('\n') == EOF)) ||
+ (putchar('\f') == EOF)) {
+ pfail();
+ return(1);
+ }
+ return(0);
+ }
+ cnt += TAILLEN;
+ while (--cnt >= 0) {
+ if (putchar('\n') == EOF) {
+ pfail();
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * terminate(): when a SIGINT is recvd
+ */
+void
+terminate(int which_sig __unused)
+{
+ flsh_errs();
+ exit(1);
+}
+
+
+/*
+ * flsh_errs(): output saved up diagnostic messages after all normal
+ * processing has completed
+ */
+void
+flsh_errs(void)
+{
+ char buf[BUFSIZ];
+
+ (void)fflush(stdout);
+ (void)fflush(err);
+ if (err == stderr)
+ return;
+ rewind(err);
+ while (fgets(buf, BUFSIZ, err) != NULL)
+ (void)fputs(buf, stderr);
+}
+
+void
+mfail(void)
+{
+ (void)fputs("pr: memory allocation failed\n", err);
+}
+
+void
+pfail(void)
+{
+ (void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
+}
+
+void
+usage(void)
+{
+ (void)fputs(
+ "usage: pr [+page] [-col] [-adFfmprt] [-e[ch][gap]] [-h header]\n",
+ err);
+ (void)fputs(
+ " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err);
+ (void)fputs(
+ " [-L locale] [-s[ch]] [-w width] [-] [file ...]\n", err);
+}
+
+/*
+ * setup: Validate command args, initialize and perform sanity
+ * checks on options
+ */
+int
+setup(int argc, char *argv[])
+{
+ int c;
+ int d_first;
+ int eflag = 0;
+ int iflag = 0;
+ int wflag = 0;
+ int cflag = 0;
+ char *Lflag = NULL;
+
+ if (isatty(fileno(stdout))) {
+ /*
+ * defer diagnostics until processing is done
+ */
+ if ((err = tmpfile()) == NULL) {
+ err = stderr;
+ (void)fputs("Cannot defer diagnostic messages\n",stderr);
+ return(1);
+ }
+ } else
+ err = stderr;
+ while ((c = egetopt(argc, argv, "#adFfmrte?h:i?L:l:n?o:ps?w:")) != -1) {
+ switch (c) {
+ case '+':
+ if ((pgnm = atoi(eoptarg)) < 1) {
+ (void)fputs("pr: +page number must be 1 or more\n",
+ err);
+ return(1);
+ }
+ break;
+ case '-':
+ if ((clcnt = atoi(eoptarg)) < 1) {
+ (void)fputs("pr: -columns must be 1 or more\n",err);
+ return(1);
+ }
+ if (clcnt > 1)
+ ++cflag;
+ break;
+ case 'a':
+ ++across;
+ break;
+ case 'd':
+ ++dspace;
+ break;
+ case 'e':
+ ++eflag;
+ if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg))
+ inchar = *eoptarg++;
+ else
+ inchar = INCHAR;
+ if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) {
+ if ((ingap = atoi(eoptarg)) < 0) {
+ (void)fputs(
+ "pr: -e gap must be 0 or more\n", err);
+ return(1);
+ }
+ if (ingap == 0)
+ ingap = INGAP;
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -e %s\n", eoptarg);
+ return(1);
+ } else
+ ingap = INGAP;
+ break;
+ case 'f':
+ ++pausefst;
+ /*FALLTHROUGH*/
+ case 'F':
+ ++formfeed;
+ break;
+ case 'h':
+ header = eoptarg;
+ break;
+ case 'i':
+ ++iflag;
+ if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg))
+ ochar = *eoptarg++;
+ else
+ ochar = OCHAR;
+ if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) {
+ if ((ogap = atoi(eoptarg)) < 0) {
+ (void)fputs(
+ "pr: -i gap must be 0 or more\n", err);
+ return(1);
+ }
+ if (ogap == 0)
+ ogap = OGAP;
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -i %s\n", eoptarg);
+ return(1);
+ } else
+ ogap = OGAP;
+ break;
+ case 'L':
+ Lflag = eoptarg;
+ break;
+ case 'l':
+ if (!isdigit((unsigned char)*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
+ (void)fputs(
+ "pr: number of lines must be 1 or more\n",err);
+ return(1);
+ }
+ break;
+ case 'm':
+ ++merge;
+ break;
+ case 'n':
+ if ((eoptarg != NULL) && !isdigit((unsigned char)*eoptarg))
+ nmchar = *eoptarg++;
+ else
+ nmchar = NMCHAR;
+ if ((eoptarg != NULL) && isdigit((unsigned char)*eoptarg)) {
+ if ((nmwd = atoi(eoptarg)) < 1) {
+ (void)fputs(
+ "pr: -n width must be 1 or more\n",err);
+ return(1);
+ }
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -n %s\n", eoptarg);
+ return(1);
+ } else
+ nmwd = NMWD;
+ break;
+ case 'o':
+ if (!isdigit((unsigned char)*eoptarg) || ((offst = atoi(eoptarg))< 1)){
+ (void)fputs("pr: -o offset must be 1 or more\n",
+ err);
+ return(1);
+ }
+ break;
+ case 'p':
+ ++pauseall;
+ break;
+ case 'r':
+ ++nodiag;
+ break;
+ case 's':
+ ++sflag;
+ if (eoptarg == NULL)
+ schar = SCHAR;
+ else {
+ schar = *eoptarg++;
+ if (*eoptarg != '\0') {
+ (void)fprintf(err,
+ "pr: invalid value for -s %s\n",
+ eoptarg);
+ return(1);
+ }
+ }
+ break;
+ case 't':
+ ++nohead;
+ break;
+ case 'w':
+ ++wflag;
+ if (!isdigit((unsigned char)*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
+ (void)fputs(
+ "pr: -w width must be 1 or more \n",err);
+ return(1);
+ }
+ break;
+ case '?':
+ default:
+ return(1);
+ }
+ }
+
+ /*
+ * default and sanity checks
+ */
+ if (!clcnt) {
+ if (merge) {
+ if ((clcnt = argc - eoptind) <= 1) {
+ clcnt = CLCNT;
+ merge = 0;
+ }
+ } else
+ clcnt = CLCNT;
+ }
+ if (across) {
+ if (clcnt == 1) {
+ (void)fputs("pr: -a flag requires multiple columns\n",
+ err);
+ return(1);
+ }
+ if (merge) {
+ (void)fputs("pr: -m cannot be used with -a\n", err);
+ return(1);
+ }
+ }
+ if (!wflag) {
+ if (sflag)
+ pgwd = SPGWD;
+ else
+ pgwd = PGWD;
+ }
+ if (cflag || merge) {
+ if (!eflag) {
+ inchar = INCHAR;
+ ingap = INGAP;
+ }
+ if (!iflag) {
+ ochar = OCHAR;
+ ogap = OGAP;
+ }
+ }
+ if (cflag) {
+ if (merge) {
+ (void)fputs(
+ "pr: -m cannot be used with multiple columns\n", err);
+ return(1);
+ }
+ if (nmwd) {
+ colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
+ pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
+ } else {
+ colwd = (pgwd + 1 - clcnt)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - 1;
+ }
+ if (colwd < 1) {
+ (void)fprintf(err,
+ "pr: page width is too small for %d columns\n",clcnt);
+ return(1);
+ }
+ }
+ if (!lines)
+ lines = LINES;
+
+ /*
+ * make sure long enough for headers. if not disable
+ */
+ if (lines <= HEADLEN + TAILLEN)
+ ++nohead;
+ else if (!nohead)
+ lines -= HEADLEN + TAILLEN;
+
+ /*
+ * adjust for double space on odd length pages
+ */
+ if (dspace) {
+ if (lines == 1)
+ dspace = 0;
+ else {
+ if (lines & 1)
+ ++addone;
+ lines /= 2;
+ }
+ }
+
+ (void) setlocale(LC_TIME, (Lflag != NULL) ? Lflag : "");
+
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ timefrmt = strdup(d_first ? TIMEFMTD : TIMEFMTM);
+
+ return(0);
+}
diff --git a/text_cmds/pr/pr.h b/text_cmds/pr/pr.h
new file mode 100644
index 0000000..d93fe8c
--- /dev/null
+++ b/text_cmds/pr/pr.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pr.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.bin/pr/pr.h,v 1.4 2001/03/21 14:32:02 ache Exp $
+ */
+
+/*
+ * parameter defaults
+ */
+#define CLCNT 1
+#define INCHAR '\t'
+#define INGAP 8
+#define OCHAR '\t'
+#define OGAP 8
+#define LINES 66
+#define NMWD 5
+#define NMCHAR '\t'
+#define SCHAR '\t'
+#define PGWD 72
+#define SPGWD 512
+
+/*
+ * misc default values
+ */
+#define HDFMT "%s %s Page %d\n\n\n"
+#define HEADLEN 5
+#define TAILLEN 5
+#define TIMEFMTD "%e %b %H:%M %Y"
+#define TIMEFMTM "%b %e %H:%M %Y"
+#define FNAME ""
+#define LBUF 8192
+#define HDBUF 512
+
+/*
+ * structure for vertical columns. Used to balance cols on last page
+ */
+struct vcol {
+ char *pt; /* ptr to col */
+ int cnt; /* char count */
+};
diff --git a/text_cmds/rev/rev.1 b/text_cmds/rev/rev.1
new file mode 100644
index 0000000..a1e9eb5
--- /dev/null
+++ b/text_cmds/rev/rev.1
@@ -0,0 +1,49 @@
+.\" Copyright (c) 1985, 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rev.1 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD: src/usr.bin/rev/rev.1,v 1.6 2001/02/13 09:55:09 ru Exp $
+.\"
+.Dd June 9, 1993
+.Dt REV 1
+.Os
+.Sh NAME
+.Nm rev
+.Nd reverse lines of a file
+.Sh SYNOPSIS
+.Nm
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility copies the specified files to the standard output, reversing the
+order of characters in every line.
+If no files are specified, the standard input is read.
diff --git a/text_cmds/rev/rev.c b/text_cmds/rev/rev.c
new file mode 100644
index 0000000..478f15f
--- /dev/null
+++ b/text_cmds/rev/rev.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1987, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)rev.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/rev/rev.c,v 1.12 2009/12/13 03:14:06 delphij Exp $");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ const char *filename;
+ wchar_t *p, *t;
+ FILE *fp;
+ size_t len;
+ int ch, rval;
+
+ setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ fp = stdin;
+ filename = "stdin";
+ rval = 0;
+ do {
+ if (*argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ warn("%s", *argv);
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ while ((p = fgetwln(fp, &len)) != NULL) {
+ if (p[len - 1] == '\n')
+ --len;
+ for (t = p + len - 1; t >= p; --t)
+ putwchar(*t);
+ putwchar('\n');
+ }
+ if (ferror(fp)) {
+ warn("%s", filename);
+ clearerr(fp);
+ rval = 1;
+ }
+ (void)fclose(fp);
+ } while(*argv);
+ exit(rval);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: rev [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/rs/rs.1 b/text_cmds/rs/rs.1
new file mode 100644
index 0000000..5e27815
--- /dev/null
+++ b/text_cmds/rs/rs.1
@@ -0,0 +1,239 @@
+.\" Copyright (c) 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rs.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD: src/usr.bin/rs/rs.1,v 1.11 2004/07/30 00:10:52 tjr Exp $
+.\"
+.Dd July 30, 2004
+.Dt RS 1
+.Os
+.Sh NAME
+.Nm rs
+.Nd reshape a data array
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl Oo Cm csCS Oc Ns Op Ar x
+.Oo Cm kKgGw Oc Ns Op Ar N
+.Cm tTeEnyjhHmz
+.Oc
+.Op Ar rows Op Ar cols
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the standard input, interpreting each line as a row
+of blank-separated entries in an array,
+transforms the array according to the options,
+and writes it on the standard output.
+With no arguments it transforms stream input into a columnar
+format convenient for terminal viewing.
+.Pp
+The shape of the input array is deduced from the number of lines
+and the number of columns on the first line.
+If that shape is inconvenient, a more useful one might be
+obtained by skipping some of the input with the
+.Fl k
+option.
+Other options control interpretation of the input columns.
+.Pp
+The shape of the output array is influenced by the
+.Ar rows
+and
+.Ar cols
+specifications, which should be positive integers.
+If only one of them is a positive integer,
+.Nm
+computes a value for the other which will accommodate
+all of the data.
+When necessary, missing data are supplied in a manner
+specified by the options and surplus data are deleted.
+There are options to control presentation of the output columns,
+including transposition of the rows and columns.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ns Ar x
+Input columns are delimited by the single character
+.Ar x .
+A missing
+.Ar x
+is taken to be `^I'.
+.It Fl s Ns Ar x
+Like
+.Fl c ,
+but maximal strings of
+.Ar x
+are delimiters.
+.It Fl C Ns Ar x
+Output columns are delimited by the single character
+.Ar x .
+A missing
+.Ar x
+is taken to be `^I'.
+.It Fl S Ns Ar x
+Like
+.Fl C ,
+but padded strings of
+.Ar x
+are delimiters.
+.It Fl t
+Fill in the rows of the output array using the columns of the
+input array, that is, transpose the input while honoring any
+.Ar rows
+and
+.Ar cols
+specifications.
+.It Fl T
+Print the pure transpose of the input, ignoring any
+.Ar rows
+or
+.Ar cols
+specification.
+.It Fl k Ns Ar N
+Ignore the first
+.Ar N
+lines of input.
+.It Fl K Ns Ar N
+Like
+.Fl k ,
+but print the ignored lines.
+.It Fl g Ns Ar N
+The gutter width (inter-column space), normally 2, is taken to be
+.Ar N .
+.It Fl G Ns Ar N
+The gutter width has
+.Ar N
+percent of the maximum column width added to it.
+.It Fl e
+Consider each line of input as an array entry.
+.It Fl n
+On lines having fewer entries than the first line,
+use null entries to pad out the line.
+Normally, missing entries are taken from the next line of input.
+.It Fl y
+If there are too few entries to make up the output dimensions,
+pad the output by recycling the input from the beginning.
+Normally, the output is padded with blanks.
+.It Fl h
+Print the shape of the input array and do nothing else.
+The shape is just the number of lines and the number of
+entries on the first line.
+.It Fl H
+Like
+.Fl h ,
+but also print the length of each line.
+.It Fl j
+Right adjust entries within columns.
+.It Fl w Ns Ar N
+The width of the display, normally 80, is taken to be the positive
+integer
+.Ar N .
+.It Fl m
+Do not trim excess delimiters from the ends of the output array.
+.It Fl z
+Adapt column widths to fit the largest entries appearing in them.
+.El
+.Pp
+With no arguments,
+.Nm
+transposes its input, and assumes one array entry per input line
+unless the first non-ignored line is longer than the display width.
+Option letters which take numerical arguments interpret a missing
+number as zero unless otherwise indicated.
+.Sh EXAMPLES
+The
+.Nm
+utility can be used as a filter to convert the stream output
+of certain programs (e.g.,
+.Xr spell 1 ,
+.Xr du 1 ,
+.Xr file 1 ,
+.Xr look 1 ,
+.Xr nm 1 ,
+.Xr who 1 ,
+and
+.Xr wc 1 )
+into a convenient ``window'' format, as in
+.Bd -literal -offset indent
+% who | rs
+.Ed
+.Pp
+This function has been incorporated into the
+.Xr ls 1
+program, though for most programs with similar output
+.Nm
+suffices.
+.Pp
+To convert stream input into vector output and back again, use
+.Bd -literal -offset indent
+% rs 1 0 | rs 0 1
+.Ed
+.Pp
+A 10 by 10 array of random numbers from 1 to 100 and
+its transpose can be generated with
+.Bd -literal -offset indent
+% jot \-r 100 | rs 10 10 | tee array | rs \-T > tarray
+.Ed
+.Pp
+In the editor
+.Xr vi 1 ,
+a file consisting of a multi-line vector with 9 elements per line
+can undergo insertions and deletions,
+and then be neatly reshaped into 9 columns with
+.Bd -literal -offset indent
+:1,$!rs 0 9
+.Ed
+.Pp
+Finally, to sort a database by the first line of each 4-line field, try
+.Bd -literal -offset indent
+% rs \-eC 0 4 | sort | rs \-c 0 1
+.Ed
+.Sh SEE ALSO
+.Xr jot 1 ,
+.Xr pr 1 ,
+.Xr sort 1 ,
+.Xr vi 1
+.Sh BUGS
+.Bl -item
+.It
+Handles only two dimensional arrays.
+.It
+The algorithm currently reads the whole file into memory,
+so files that do not fit in memory will not be reshaped.
+.It
+Fields cannot be defined yet on character positions.
+.It
+Re-ordering of columns is not yet possible.
+.It
+There are too many options.
+.It
+Multibyte characters are not recognized.
+.El
diff --git a/text_cmds/rs/rs.c b/text_cmds/rs/rs.c
new file mode 100644
index 0000000..19cb750
--- /dev/null
+++ b/text_cmds/rs/rs.c
@@ -0,0 +1,553 @@
+/*-
+ * Copyright (c) 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)rs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * rs - reshape a data array
+ * Author: John Kunze, Office of Comp. Affairs, UCB
+ * BEWARE: lots of unfinished edges
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/rs/rs.c,v 1.13 2005/04/28 12:37:15 robert Exp $");
+
+#include <err.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+long flags;
+#define TRANSPOSE 000001
+#define MTRANSPOSE 000002
+#define ONEPERLINE 000004
+#define ONEISEPONLY 000010
+#define ONEOSEPONLY 000020
+#define NOTRIMENDCOL 000040
+#define SQUEEZE 000100
+#define SHAPEONLY 000200
+#define DETAILSHAPE 000400
+#define RIGHTADJUST 001000
+#define NULLPAD 002000
+#define RECYCLE 004000
+#define SKIPPRINT 010000
+#define ICOLBOUNDS 020000
+#define OCOLBOUNDS 040000
+#define ONEPERCHAR 0100000
+#define NOARGS 0200000
+
+short *colwidths;
+short *cord;
+short *icbd;
+short *ocbd;
+int nelem;
+char **elem;
+char **endelem;
+char *curline;
+int allocsize = BUFSIZ;
+int curlen;
+int irows, icols;
+int orows = 0, ocols = 0;
+int maxlen;
+int skip;
+int propgutter;
+char isep = ' ', osep = ' ';
+char blank[] = "";
+int owidth = 80, gutter = 2;
+
+void getargs(int, char *[]);
+void getfile(void);
+int rs_getline(void);
+char *getlist(short **, char *);
+char *getnum(int *, char *, int);
+char **getptrs(char **);
+void prepfile(void);
+void prints(char *, int);
+void putfile(void);
+static void usage(void);
+
+#define INCR(ep) do { \
+ if (++ep >= endelem) \
+ ep = getptrs(ep); \
+} while(0)
+
+int
+main(int argc, char *argv[])
+{
+ getargs(argc, argv);
+ getfile();
+ if (flags & SHAPEONLY) {
+ printf("%d %d\n", irows, icols);
+ exit(0);
+ }
+ prepfile();
+ putfile();
+ exit(0);
+}
+
+void
+getfile(void)
+{
+ char *p;
+ char *endp;
+ char **ep;
+ int multisep = (flags & ONEISEPONLY ? 0 : 1);
+ int nullpad = flags & NULLPAD;
+ char **padto;
+
+ while (skip--) {
+ rs_getline();
+ if (flags & SKIPPRINT)
+ puts(curline);
+ }
+ rs_getline();
+ if (flags & NOARGS && curlen < owidth)
+ flags |= ONEPERLINE;
+ if (flags & ONEPERLINE)
+ icols = 1;
+ else /* count cols on first line */
+ for (p = curline, endp = curline + curlen; p < endp; p++) {
+ if (*p == isep && multisep)
+ continue;
+ icols++;
+ while (*p && *p != isep)
+ p++;
+ }
+ ep = getptrs(elem);
+ p = curline;
+ do {
+ if (flags & ONEPERLINE) {
+ *ep = curline;
+ INCR(ep); /* prepare for next entry */
+ if (maxlen < curlen)
+ maxlen = curlen;
+ irows++;
+ continue;
+ }
+ for (p = curline, endp = curline + curlen; p < endp; p++) {
+ if (*p == isep && multisep)
+ continue; /* eat up column separators */
+ if (*p == isep) /* must be an empty column */
+ *ep = blank;
+ else /* store column entry */
+ *ep = p;
+ while (p < endp && *p != isep)
+ p++; /* find end of entry */
+ *p = '\0'; /* mark end of entry */
+ if (maxlen < p - *ep) /* update maxlen */
+ maxlen = p - *ep;
+ INCR(ep); /* prepare for next entry */
+ }
+ irows++; /* update row count */
+ if (nullpad) { /* pad missing entries */
+ padto = elem + irows * icols;
+ while (ep < padto) {
+ *ep = blank;
+ INCR(ep);
+ }
+ }
+ } while (rs_getline() != EOF);
+ *ep = 0; /* mark end of pointers */
+ nelem = ep - elem;
+}
+
+void
+putfile(void)
+{
+ char **ep;
+ int i, j, k;
+
+ ep = elem;
+ if (flags & TRANSPOSE)
+ for (i = 0; i < orows; i++) {
+ for (j = i; j < nelem; j += orows)
+ prints(ep[j], (j - i) / orows);
+ putchar('\n');
+ }
+ else
+ for (i = k = 0; i < orows; i++) {
+ for (j = 0; j < ocols; j++, k++)
+ if (k < nelem)
+ prints(ep[k], j);
+ putchar('\n');
+ }
+}
+
+void
+prints(char *s, int col)
+{
+ int n;
+ char *p = s;
+
+ while (*p)
+ p++;
+ n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
+ if (flags & RIGHTADJUST)
+ while (n-- > 0)
+ putchar(osep);
+ for (p = s; *p; p++)
+ putchar(*p);
+ while (n-- > 0)
+ putchar(osep);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: rs [-[csCS][x][kKgGw][N]tTeEnyjhHmz] [rows [cols]]\n");
+ exit(1);
+}
+
+void
+prepfile(void)
+{
+ char **ep;
+ int i;
+ int j;
+ char **lp;
+ int colw;
+ int max;
+ int n;
+
+ if (!nelem)
+ exit(0);
+ gutter += maxlen * propgutter / 100.0;
+ colw = maxlen + gutter;
+ if (flags & MTRANSPOSE) {
+ orows = icols;
+ ocols = irows;
+ }
+ else if (orows == 0 && ocols == 0) { /* decide rows and cols */
+ ocols = owidth / colw;
+ if (ocols == 0) {
+ warnx("display width %d is less than column width %d",
+ owidth, colw);
+ ocols = 1;
+ }
+ if (ocols > nelem)
+ ocols = nelem;
+ orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+ }
+ else if (orows == 0) /* decide on rows */
+ orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+ else if (ocols == 0) /* decide on cols */
+ ocols = nelem / orows + (nelem % orows ? 1 : 0);
+ lp = elem + orows * ocols;
+ while (lp > endelem) {
+ getptrs(elem + nelem);
+ lp = elem + orows * ocols;
+ }
+ if (flags & RECYCLE) {
+ for (ep = elem + nelem; ep < lp; ep++)
+ *ep = *(ep - nelem);
+ nelem = lp - elem;
+ }
+ if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
+ errx(1, "malloc");
+ if (flags & SQUEEZE) {
+ ep = elem;
+ if (flags & TRANSPOSE)
+ for (i = 0; i < ocols; i++) {
+ max = 0;
+ for (j = 0; *ep != NULL && j < orows; j++)
+ if ((n = strlen(*ep++)) > max)
+ max = n;
+ colwidths[i] = max + gutter;
+ }
+ else
+ for (i = 0; i < ocols; i++) {
+ max = 0;
+ for (j = i; j < nelem; j += ocols)
+ if ((n = strlen(ep[j])) > max)
+ max = n;
+ colwidths[i] = max + gutter;
+ }
+ }
+ /* for (i = 0; i < orows; i++) {
+ for (j = i; j < nelem; j += orows)
+ prints(ep[j], (j - i) / orows);
+ putchar('\n');
+ }
+ else
+ for (i = 0; i < orows; i++) {
+ for (j = 0; j < ocols; j++)
+ prints(*ep++, j);
+ putchar('\n');
+ }*/
+ else
+ for (i = 0; i < ocols; i++)
+ colwidths[i] = colw;
+ if (!(flags & NOTRIMENDCOL)) {
+ if (flags & RIGHTADJUST)
+ colwidths[0] -= gutter;
+ else
+ colwidths[ocols - 1] = 0;
+ }
+ n = orows * ocols;
+ if (n > nelem && (flags & RECYCLE))
+ nelem = n;
+ /*for (i = 0; i < ocols; i++)
+ warnx("%d is colwidths, nelem %d", colwidths[i], nelem);*/
+}
+
+#define BSIZE 2048
+char ibuf[BSIZE]; /* two screenfuls should do */
+
+int
+rs_getline(void) /* get line; maintain curline, curlen; manage storage */
+{
+ static int putlength;
+ static char *endblock = ibuf + BSIZE;
+ char *p;
+ int c, i;
+
+ if (!irows) {
+ curline = ibuf;
+ putlength = flags & DETAILSHAPE;
+ }
+ else if (skip <= 0) { /* don't waste storage */
+ curline += curlen + 1;
+ if (putlength) { /* print length, recycle storage */
+ printf(" %d line %d\n", curlen, irows);
+ curline = ibuf;
+ }
+ }
+ if (!putlength && endblock - curline < BUFSIZ) { /* need storage */
+ /*ww = endblock-curline; tt += ww;*/
+ /*printf("#wasted %d total %d\n",ww,tt);*/
+ if (!(curline = (char *) malloc(BSIZE)))
+ errx(1, "file too large");
+ endblock = curline + BSIZE;
+ /*printf("#endb %d curline %d\n",endblock,curline);*/
+ }
+ for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
+ if ((c = getchar()) == EOF || c == '\n')
+ break;
+ if (ferror(stdin)) {
+ errx(EX_IOERR, "Read error");
+ }
+ *p = '\0';
+ curlen = i - 1;
+ return(c);
+}
+
+char **
+getptrs(char **sp)
+{
+ char **p;
+
+ allocsize += allocsize;
+ p = (char **)realloc(elem, allocsize * sizeof(char *));
+ if (p == NULL)
+ err(1, "no memory");
+
+ sp += (p - elem);
+ endelem = (elem = p) + allocsize;
+ return(sp);
+}
+
+void
+getargs(int ac, char *av[])
+{
+ char *p;
+
+ if (ac == 1) {
+ flags |= NOARGS | TRANSPOSE;
+ }
+ while (--ac>0 && *++av && **av == '-')
+ for (p = *av+1; *p; p++)
+ switch (*p) {
+ case 'T':
+ flags |= MTRANSPOSE;
+ case 't':
+ flags |= TRANSPOSE;
+ break;
+ case 'c': /* input col. separator */
+ flags |= ONEISEPONLY;
+ case 's': /* one or more allowed */
+ if (p[1])
+ isep = *++p;
+ else
+ isep = '\t'; /* default is ^I */
+ break;
+ case 'C':
+ flags |= ONEOSEPONLY;
+ case 'S':
+ if (p[1])
+ osep = *++p;
+ else
+ osep = '\t'; /* default is ^I */
+ break;
+ case 'w': /* window width, default 80 */
+ p = getnum(&owidth, p, 0);
+ if (owidth <= 0)
+ errx(1, "width must be a positive integer");
+ break;
+ case 'K': /* skip N lines */
+ flags |= SKIPPRINT;
+ case 'k': /* skip, do not print */
+ p = getnum(&skip, p, 0);
+ if (!skip)
+ skip = 1;
+ break;
+ case 'm':
+ flags |= NOTRIMENDCOL;
+ break;
+ case 'g': /* gutter space */
+ p = getnum(&gutter, p, 0);
+ break;
+ case 'G':
+ p = getnum(&propgutter, p, 0);
+ break;
+ case 'e': /* each line is an entry */
+ flags |= ONEPERLINE;
+ break;
+ case 'E':
+ flags |= ONEPERCHAR;
+ break;
+ case 'j': /* right adjust */
+ flags |= RIGHTADJUST;
+ break;
+ case 'n': /* null padding for missing values */
+ flags |= NULLPAD;
+ break;
+ case 'y':
+ flags |= RECYCLE;
+ break;
+ case 'H': /* print shape only */
+ flags |= DETAILSHAPE;
+ case 'h':
+ flags |= SHAPEONLY;
+ break;
+ case 'z': /* squeeze col width */
+ flags |= SQUEEZE;
+ break;
+ /*case 'p':
+ ipagespace = atoi(++p); (default is 1)
+ break;*/
+ case 'o': /* col order */
+ p = getlist(&cord, p);
+ break;
+ case 'b':
+ flags |= ICOLBOUNDS;
+ p = getlist(&icbd, p);
+ break;
+ case 'B':
+ flags |= OCOLBOUNDS;
+ p = getlist(&ocbd, p);
+ break;
+ default:
+ usage();
+ }
+ /*if (!osep)
+ osep = isep;*/
+ switch (ac) {
+ /*case 3:
+ opages = atoi(av[2]);*/
+ case 2:
+ if ((ocols = atoi(av[1])) < 0)
+ ocols = 0;
+ case 1:
+ if ((orows = atoi(av[0])) < 0)
+ orows = 0;
+ case 0:
+ break;
+ default:
+ errx(1, "too many arguments");
+ }
+}
+
+char *
+getlist(short **list, char *p)
+{
+ int count = 1;
+ char *t;
+
+ for (t = p + 1; *t; t++) {
+ if (!isdigit((unsigned char)*t))
+ errx(1,
+ "option %.1s requires a list of unsigned numbers separated by commas", t);
+ count++;
+ while (*t && isdigit((unsigned char)*t))
+ t++;
+ if (*t != ',')
+ break;
+ }
+ if (!(*list = (short *) malloc(count * sizeof(short))))
+ errx(1, "no list space");
+ count = 0;
+ for (t = p + 1; *t; t++) {
+ (*list)[count++] = atoi(t);
+ printf("++ %d ", (*list)[count-1]);
+ fflush(stdout);
+ while (*t && isdigit((unsigned char)*t))
+ t++;
+ if (*t != ',')
+ break;
+ }
+ (*list)[count] = 0;
+ return(t - 1);
+}
+
+/*
+ * num = number p points to; if (strict) complain
+ * returns pointer to end of num
+ */
+char *
+getnum(int *num, char *p, int strict)
+{
+ char *t = p;
+
+ if (!isdigit((unsigned char)*++t)) {
+ if (strict || *t == '-' || *t == '+')
+ errx(1, "option %.1s requires an unsigned integer", p);
+ *num = 0;
+ return(p);
+ }
+ *num = atoi(t);
+ while (*++t)
+ if (!isdigit((unsigned char)*t))
+ break;
+ return(--t);
+}
diff --git a/text_cmds/sed/POSIX b/text_cmds/sed/POSIX
new file mode 100644
index 0000000..239acf8
--- /dev/null
+++ b/text_cmds/sed/POSIX
@@ -0,0 +1,204 @@
+# @(#)POSIX 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+Comments on the IEEE P1003.2 Draft 12
+ Part 2: Shell and Utilities
+ Section 4.55: sed - Stream editor
+
+Diomidis Spinellis <dds@doc.ic.ac.uk>
+Keith Bostic <bostic@cs.berkeley.edu>
+
+In the following paragraphs, "wrong" usually means "inconsistent with
+historic practice", as most of the following comments refer to
+undocumented inconsistencies between the historical versions of sed and
+the POSIX 1003.2 standard. All the comments are notes taken while
+implementing a POSIX-compatible version of sed, and should not be
+interpreted as official opinions or criticism towards the POSIX committee.
+All uses of "POSIX" refer to section 4.55, Draft 12 of POSIX 1003.2.
+
+ 1. 32V and BSD derived implementations of sed strip the text
+ arguments of the a, c and i commands of their initial blanks,
+ i.e.
+
+ #!/bin/sed -f
+ a\
+ foo\
+ \ indent\
+ bar
+
+ produces:
+
+ foo
+ indent
+ bar
+
+ POSIX does not specify this behavior as the System V versions of
+ sed do not do this stripping. The argument against stripping is
+ that it is difficult to write sed scripts that have leading blanks
+ if they are stripped. The argument for stripping is that it is
+ difficult to write readable sed scripts unless indentation is allowed
+ and ignored, and leading whitespace is obtainable by entering a
+ backslash in front of it. This implementation follows the BSD
+ historic practice.
+
+ 2. Historical versions of sed required that the w flag be the last
+ flag to an s command as it takes an additional argument. This
+ is obvious, but not specified in POSIX.
+
+ 3. Historical versions of sed required that whitespace follow a w
+ flag to an s command. This is not specified in POSIX. This
+ implementation permits whitespace but does not require it.
+
+ 4. Historical versions of sed permitted any number of whitespace
+ characters to follow the w command. This is not specified in
+ POSIX. This implementation permits whitespace but does not
+ require it.
+
+ 5. The rule for the l command differs from historic practice. Table
+ 2-15 includes the various ANSI C escape sequences, including \\
+ for backslash. Some historical versions of sed displayed two
+ digit octal numbers, too, not three as specified by POSIX. POSIX
+ is a cleanup, and is followed by this implementation.
+
+ 6. The POSIX specification for ! does not specify that for a single
+ command the command must not contain an address specification
+ whereas the command list can contain address specifications. The
+ specification for ! implies that "3!/hello/p" works, and it never
+ has, historically. Note,
+
+ 3!{
+ /hello/p
+ }
+
+ does work.
+
+ 7. POSIX does not specify what happens with consecutive ! commands
+ (e.g. /foo/!!!p). Historic implementations allow any number of
+ !'s without changing the behaviour. (It seems logical that each
+ one might reverse the behaviour.) This implementation follows
+ historic practice.
+
+ 8. Historic versions of sed permitted commands to be separated
+ by semi-colons, e.g. 'sed -ne '1p;2p;3q' printed the first
+ three lines of a file. This is not specified by POSIX.
+ Note, the ; command separator is not allowed for the commands
+ a, c, i, w, r, :, b, t, # and at the end of a w flag in the s
+ command. This implementation follows historic practice and
+ implements the ; separator.
+
+ 9. Historic versions of sed terminated the script if EOF was reached
+ during the execution of the 'n' command, i.e.:
+
+ sed -e '
+ n
+ i\
+ hello
+ ' </dev/null
+
+ did not produce any output. POSIX does not specify this behavior.
+ This implementation follows historic practice.
+
+10. Deleted.
+
+11. Historical implementations do not output the change text of a c
+ command in the case of an address range whose first line number
+ is greater than the second (e.g. 3,1). POSIX requires that the
+ text be output. Since the historic behavior doesn't seem to have
+ any particular purpose, this implementation follows the POSIX
+ behavior.
+
+12. POSIX does not specify whether address ranges are checked and
+ reset if a command is not executed due to a jump. The following
+ program will behave in different ways depending on whether the
+ 'c' command is triggered at the third line, i.e. will the text
+ be output even though line 3 of the input will never logically
+ encounter that command.
+
+ 2,4b
+ 1,3c\
+ text
+
+ Historic implementations did not output the text in the above
+ example. Therefore it was believed that a range whose second
+ address was never matched extended to the end of the input.
+ However, the current practice adopted by this implementation,
+ as well as by those from GNU and SUN, is as follows: The text
+ from the 'c' command still isn't output because the second address
+ isn't actually matched; but the range is reset after all if its
+ second address is a line number. In the above example, only the
+ first line of the input will be deleted.
+
+13. Historical implementations allow an output suppressing #n at the
+ beginning of -e arguments as well as in a script file. POSIX
+ does not specify this. This implementation follows historical
+ practice.
+
+14. POSIX does not explicitly specify how sed behaves if no script is
+ specified. Since the sed Synopsis permits this form of the command,
+ and the language in the Description section states that the input
+ is output, it seems reasonable that it behave like the cat(1)
+ command. Historic sed implementations behave differently for "ls |
+ sed", where they produce no output, and "ls | sed -e#", where they
+ behave like cat. This implementation behaves like cat in both cases.
+
+15. The POSIX requirement to open all w files at the beginning makes
+ sed behave nonintuitively when the w commands are preceded by
+ addresses or are within conditional blocks. This implementation
+ follows historic practice and POSIX, by default, and provides the
+ -a option which opens the files only when they are needed.
+
+16. POSIX does not specify how escape sequences other than \n and \D
+ (where D is the delimiter character) are to be treated. This is
+ reasonable, however, it also doesn't state that the backslash is
+ to be discarded from the output regardless. A strict reading of
+ POSIX would be that "echo xyz | sed s/./\a" would display "\ayz".
+ As historic sed implementations always discarded the backslash,
+ this implementation does as well.
+
+17. POSIX specifies that an address can be "empty". This implies
+ that constructs like ",d" or "1,d" and ",5d" are allowed. This
+ is not true for historic implementations or this implementation
+ of sed.
+
+18. The b t and : commands are documented in POSIX to ignore leading
+ white space, but no mention is made of trailing white space.
+ Historic implementations of sed assigned different locations to
+ the labels "x" and "x ". This is not useful, and leads to subtle
+ programming errors, but it is historic practice and changing it
+ could theoretically break working scripts. This implementation
+ follows historic practice.
+
+19. Although POSIX specifies that reading from files that do not exist
+ from within the script must not terminate the script, it does not
+ specify what happens if a write command fails. Historic practice
+ is to fail immediately if the file cannot be opened or written.
+ This implementation follows historic practice.
+
+20. Historic practice is that the \n construct can be used for either
+ string1 or string2 of the y command. This is not specified by
+ POSIX. This implementation follows historic practice.
+
+21. Deleted.
+
+22. Historic implementations of sed ignore the RE delimiter characters
+ within character classes. This is not specified in POSIX. This
+ implementation follows historic practice.
+
+23. Historic implementations handle empty RE's in a special way: the
+ empty RE is interpreted as if it were the last RE encountered,
+ whether in an address or elsewhere. POSIX does not document this
+ behavior. For example the command:
+
+ sed -e /abc/s//XXX/
+
+ substitutes XXX for the pattern abc. The semantics of "the last
+ RE" can be defined in two different ways:
+
+ 1. The last RE encountered when compiling (lexical/static scope).
+ 2. The last RE encountered while running (dynamic scope).
+
+ While many historical implementations fail on programs depending
+ on scope differences, the SunOS version exhibited dynamic scope
+ behaviour. This implementation does dynamic scoping, as this seems
+ the most useful and in order to remain consistent with historical
+ practice.
diff --git a/text_cmds/sed/TEST/hanoi.sed b/text_cmds/sed/TEST/hanoi.sed
new file mode 100644
index 0000000..0a724b8
--- /dev/null
+++ b/text_cmds/sed/TEST/hanoi.sed
@@ -0,0 +1,103 @@
+# Towers of Hanoi in sed.
+#
+# @(#)hanoi.sed 8.1 (Berkeley) 6/6/93
+# $FreeBSD: src/usr.bin/sed/TEST/hanoi.sed,v 1.3 2002/07/04 05:16:19 tjr Exp $
+#
+#
+# Ex:
+# Run "sed -f hanoi.sed", and enter:
+#
+# :abcd: : :<CR>
+#
+# note -- TWO carriage returns were once required, this will output the
+# sequence of states involved in moving 4 rings, the largest called "a" and
+# the smallest called "d", from the first to the second of three towers, so
+# that the rings on any tower at any time are in descending order of size.
+# You can start with a different arrangement and a different number of rings,
+# say :ce:b:ax: and it will give the shortest procedure for moving them all
+# to the middle tower. The rules are: the names of the rings must all be
+# lower-case letters, they must be input within 3 fields (representing the
+# towers) and delimited by 4 colons, such that the letters within each field
+# are in alphabetical order (i.e. rings are in descending order of size).
+#
+# For the benefit of anyone who wants to figure out the script, an "internal"
+# line of the form
+# b:0abx:1a2b3 :2 :3x2
+# has the following meaning: the material after the three markers :1, :2,
+# and :3 represents the three towers; in this case the current set-up is
+# ":ab : :x :". The numbers after a, b and x in these fields indicate
+# that the next time it gets a chance, it will move a to tower 2, move b
+# to tower 3, and move x to tower 2. The string after :0 just keeps track
+# of the alphabetical order of the names of the rings. The b at the
+# beginning means that it is now dealing with ring b (either about to move
+# it, or re-evaluating where it should next be moved to).
+#
+# Although this version is "limited" to 26 rings because of the size of the
+# alphabet, one could write a script using the same idea in which the rings
+# were represented by arbitrary [strings][within][brackets], and in place of
+# the built-in line of the script giving the order of the letters of the
+# alphabet, it would accept from the user a line giving the ordering to be
+# assumed, e.g. [ucbvax][decvax][hplabs][foo][bar].
+#
+# George Bergman
+# Math, UC Berkeley 94720 USA
+
+# cleaning, diagnostics
+s/ *//g
+/^$/d
+/[^a-z:]/{a\
+Illegal characters: use only a-z and ":". Try again.
+d
+}
+/^:[a-z]*:[a-z]*:[a-z]*:$/!{a\
+Incorrect format: use\
+\ : string1 : string2 : string3 :<CR>\
+Try again.
+d
+}
+/\([a-z]\).*\1/{a\
+Repeated letters not allowed. Try again.
+d
+}
+# initial formatting
+h
+s/[a-z]/ /g
+G
+s/^:\( *\):\( *\):\( *\):\n:\([a-z]*\):\([a-z]*\):\([a-z]*\):$/:1\4\2\3:2\5\1\3:3\6\1\2:0/
+s/[a-z]/&2/g
+s/^/abcdefghijklmnopqrstuvwxyz/
+:a
+s/^\(.\).*\1.*/&\1/
+s/.//
+/^[^:]/ba
+s/\([^0]*\)\(:0.*\)/\2\1:/
+s/^[^0]*0\(.\)/\1&/
+:b
+# outputting current state without markers
+h
+s/.*:1/:/
+s/[123]//gp
+g
+:c
+# establishing destinations
+/^\(.\).*\1:1/td
+/^\(.\).*:1[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:1[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+/^\(.\).*:1[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:2[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+/^\(.\).*:2[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:2[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:3[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:3[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:3[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+bc
+# iterate back to find smallest out-of-place ring
+:d
+s/^\(.\)\(:0[^:]*\([^:]\)\1.*:\([123]\)[^:]*\1\)\4/\3\2\4/
+td
+# move said ring (right, resp. left)
+s/^\(.\)\(.*\)\1\([23]\)\(.*:\3[^ ]*\) /\1\2 \4\1\3/
+s/^\(.\)\(.*:\([12]\)[^ ]*\) \(.*\)\1\3/\1\2\1\3\4 /
+tb
+s/.*/Done! Try another, or end with ^D./p
+d
diff --git a/text_cmds/sed/TEST/math.sed b/text_cmds/sed/TEST/math.sed
new file mode 100644
index 0000000..c3ecc64
--- /dev/null
+++ b/text_cmds/sed/TEST/math.sed
@@ -0,0 +1,439 @@
+# This is ksb's infamous sed calculator. (ksb@sa.fedex.com)
+#
+# $FreeBSD: src/usr.bin/sed/TEST/math.sed,v 1.2 2004/05/01 02:15:58 smkelly Exp $
+#
+# $Id: math.sed,v 2.5 1998/08/02 13:23:34 ksb Exp ksb $
+# expr ::= (expr) | expr! |
+# expr ^ expr |
+# -expr | expr * expr | expr / expr | expr % expr |
+# expr + expr | expr - expr |
+# [0-9][0-9]* ;
+# Bugs: some sign combinations don't work, and I got sick of added cases
+# for unary +. Don't depend on signed math working all the time. -- ksb
+#
+# $Compile: echo "4+7*3+2^7/3" | sed -f %f
+
+# make sure the expression is well formed
+s/[ ]//g
+/[*\/^%+-]$/{
+ a\
+ poorly formed expression, dyadic operator on the end
+ q
+}
+/^[*\/^%]/{
+ a\
+ poorly formed expression, leading dyadic operator
+ q
+}
+
+# fill hold space with done token
+x
+s/^.*/done/
+x
+
+# main loop, process operators ((), !, *, /, %, +, and -)
+: loop
+# uncomment the print below to follow the "logic" -- ksb
+#p
+/^[+]/{
+ s///
+ b loop
+}
+/^--/{
+ s///
+ b loop
+}
+# eval parenthesised sub expressions first
+/^\(.*\)(\([^)]*\))\(.*\)$/{
+ H
+ s//\2/
+ x
+ s/^\(.*\)\n\(.*\)(\([^()]*\))\(.*\)$/()\2@\4@\1/
+ x
+ b loop
+}
+# reduce a^b^c -> a^(b^c)
+/\([0-9][0-9]*^\)\([0-9][0-9]*^[0-9][0-9^]*\)/{
+ s//\1(\2)/
+ b loop
+}
+# pull any burried exponents
+/^\(.*[^0-9]\)\([0-9][0-9]*^[0-9][0-9]*\)$/{
+ s//\1(\2)/
+ b loop
+}
+/^\(.*[^0-9]\)\([0-9][0-9]*^[0-9][0-9]*\)\([^0-9].*\)$/{
+ s//\1(\2)\3/
+ b loop
+}
+/^\([0-9][0-9]*^[0-9][0-9]*\)\([^0-9].*\)$/{
+ s//(\1)\2/
+ b loop
+}
+/^\([-]*[0-9]*\)^0*$/{
+ s//1/
+ b loop
+}
+/^\([-]*[0-9]*\)^0*1$/{
+ s//\1/
+ b loop
+}
+/^\([-]*[0-9]*\)^-[0-9]*$/{
+ s//0/
+ b loop
+}
+/^\([-]*\)\([0-9]*\)^\([0-9][0-9]*[13579]\)$/{
+ s//\1\2*((\2*\2)^(\3\/2))/
+ b loop
+}
+/^[-]*\([0-9]*\)^\([0-9][0-9]*[02468]\)$/{
+ s//(\1*\1)^(\2\/2)/
+ b loop
+}
+# single digit powers (2 3,9 4,6,8 5,7
+/^[-]*\([0-9]*\)^0*2$/{
+ s//(\1*\1)/
+ b loop
+}
+/^\([-]*\)\([0-9]*\)^0*\([39]\)$/{
+ s//\1(\2*(\2*\2))^(\3\/3)/
+ b loop
+}
+/^[-]*\([0-9]*\)^0*\([468]\)$/{
+ s//(\1*\1)^(\2\/2)/
+ b loop
+}
+# 5 7
+/^\([-]*[0-9]*\)^\([0-9]*\)$/{
+ s//\1*(\1^(\2-1))/
+ b loop
+}
+# reduce all number factorials
+/^0*[01]!/{
+ s//1/
+ b loop
+}
+/\([*+-/%^]\)0*[01]!/{
+ s//\11/
+ b loop
+}
+/\([0-9]*\)!/{
+ s//(\1-1)!*\1/
+ b loop
+}
+# sign simplifications
+/^-\([0-9]*\)\([*/%]\)-\([0-9]*\)$/{
+ s//\1\2\3/
+ b loop
+}
+/^\([0-9]*\)\([*/%]\)-\([0-9]*\)$/{
+ s//-\1\2\3/
+ b loop
+}
+/^-\([0-9][0-9]*\)[+]*-\([0-9][0-9]*\)$/{
+ s//\1+\2/
+ x
+ s/\(.*\)/()-@@\1/
+ x
+ b loop
+}
+/^-\([0-9]*\)[+]\([0-9]\)*$/{
+ s//\2-\1/
+ b loop
+}
+/^-.*[-+*/%].*/{
+ H
+ s/^-//
+ x
+ s/^\(.*\)\n-.*$/()-@@\1/
+ x
+ b loop
+}
+# can we simplify multiplications
+/^\([0-9]*\)\([*][0-9]*[1-9]\)00*$/{
+ H
+ s//\1\2/
+ x
+ s/^\(.*\)\n[0-9]*[*][0-9]*[1-9]\(00*\)$/()@\2@\1/
+ x
+ b loop
+}
+/^\([0-9][1-9]*\)00*\([*][0-9]*\)$/{
+ H
+ s//\1\2/
+ x
+ s/^\(.*\)\n[0-9][1-9]*\(00*\)[*][0-9]*$/()@\2@\1/
+ x
+ b loop
+}
+# can we simplify division (20/30 -> 2/3)
+/^\([0-9][0-9]*\)0\([/%]\)\([0-9][0-9]*\)0$/{
+ s//\1\2\3/
+ b loop
+}
+# n/1 -> n
+/^0*\([0-9][0-9]*\)0[/]0*1$/{
+ s//\1/
+ b loop
+}
+# n%2 -> last_digit(n)%2 (same for 1, BTW) N.B. NO LOOP
+/^[0-9]*\([0-9]\)%0*\([12]\)$/{
+ s//\1%\2/
+}
+# move any mul/divs to the front via parans
+/^\([0-9+]*\)\([-+]\)\([0-9]*[*/][0-9*/]*\)/{
+ s//\1\2(\3)/
+ b loop
+}
+# can we div or mul
+/^[0-9]*[*][0-9]*$/{
+ b mul
+}
+/^[0-9]*[/%]0*$/{
+ i\
+divide by zero
+ d
+}
+/^[0-9]*[/%][0-9]*$/{
+ H
+ s/\([0-9]\).*[/%]/\1-/
+ x
+ s/^\(.*\)\n\([0-9]\)\([0-9]*\)\([/%]\)\([0-9]*\).*$/.\4\3q0r\2-\5@\1/
+ x
+ b loop
+}
+/^\([0-9]*[*/%][0-9]*\)\(.*\)/{
+ H
+ s//\1/
+ x
+ s/^\(.*\)\n\([0-9]*[*/][0-9]*\)\(.*\)$/()@\3@\1/
+ x
+ b loop
+}
+# can we add or subtract -- note subtract hold expression for underflow
+/^[0-9]*[+][0-9]*$/{
+ s/$/=/
+ b add
+}
+/^[0-9][0-9]*-[0-9]*$/{
+ H
+ s/$/=/
+ b sub
+}
+/^\([0-9][0-9]*[-+][0-9]*\)\(.*\)/{
+ H
+ s//\1/
+ x
+ s/^\(.*\)\n\([0-9]*[-+][0-9]*\)\(.*\)$/()@\3@\1/
+ x
+ b loop
+}
+# look in hold space for stack to reduce
+x
+/^done$/{
+ x
+ s/^0*\([0-9][0-9]*\)/\1/
+ p
+ d
+}
+# .[/%] numerator q quotient r remainder-divisor @stack
+/^\./{
+ x
+ /^[^-]/{
+ H
+ x
+ s/.\(.\)\([0-9]*\)q\([^r]*\)r\([0-9]*\)-\([0-9]*\)@\(.*\)\n\(.*\)/.\1\2q\3+1r\7-\5@\6/
+ h
+ s/..[0-9]*q[^r]*r\([0-9]*-[0-9]*\)@.*/\1/
+ b loop
+ }
+ /^-/{
+ g
+ /.\(.\)\([0-9]\)\([0-9]*\)q\([^r]*\)r0*\([0-9]*\)-\([^@]*\)@.*/{
+ s//\5\2-\6/
+ x
+ s/.\(.\)\([0-9]\)\([0-9]*\)q\([^r]*\)r0*\([0-9]*\)-\([0-9]*\)@\(.*\)/.\1\3q(\4)*10r\5\2-\6@\7/
+ x
+ b loop
+ }
+# no digits to shift on
+ s/^\.[/]q\([^r]*\)r[^@]*@.*/\1/
+ s/^\.[%]q[^r]*r0*\([0-9][0-9]*\)-[^@]*@.*/\1/
+ /^\./{
+ i\
+divide error
+ q
+ }
+ x
+ s/^\.[/%]q[^r]*r[^@]*@\(.*\)/\1/
+ x
+ b loop
+ }
+}
+/^()/{
+ s///
+ x
+ G
+ s/\(.*\)\n\([^@]*\)@\([^@]*\)@\(.*\)/\2\1\3/
+ x
+ s/[^@]*@[^@]*@\(.*\)/\1/
+ x
+ b loop
+}
+i\
+help, stack problem - the hold space
+p
+x
+i\
+and the pat space
+p
+i\
+quit
+q
+
+# turn mul into add until 1*x -> x, 0*x -> 0
+: mul
+/^00*\*.*/{
+ s//0/
+ b loop
+}
+/^0*1\*/{
+ s///
+: leading
+ s/^0*\([0-9][0-9]*\)/\1/
+ b loop
+}
+s/^\([0-9]*\)0\*\([0-9]*\)/\1*\20/
+s/^\([0-9]*\)1\*\([0-9]*\)/\1*\20+\2/
+s/^\([0-9]*\)2\*\([0-9]*\)/\1*\20+(\2+\2)/
+s/^\([0-9]*\)3\*\([0-9]*\)/\1*\20+(\2+\2+\2)/
+s/^\([0-9]*\)4\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2)/
+s/^\([0-9]*\)5\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2+\2)/
+s/^\([0-9]*\)6\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2+\2+\2)/
+s/^\([0-9]*\)7\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2+\2+\2+\2)/
+s/^\([0-9]*\)8\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2+\2+\2+\2+\2)/
+s/^\([0-9]*\)9\*\([0-9]*\)/\1*\20+(\2+\2+\2+\2+\2+\2+\2+\2+\2)/
+/^0*\*[0-9]*[+]*\(.*\)/{
+ s//\1/
+ b loop
+}
+b mul
+
+# get rid of a plus term until 0+x -> x
+: add
+/^[+]\([0-9+*]*\)=/{
+ s//\1/
+ b leading
+}
+/^\([0-9*]*\)[+]=/{
+ s//\1/
+ b loop
+}
+/^\([0-9]*\)0[+]\([0-9]*\)\([0-9]\)=/{
+ s//\1+\2=\3/
+ b add
+}
+/^\([0-9]*\)\([0-9]\)[+]\([0-9]*\)0=/{
+ s//\1+\3=\2/
+ b add
+}
+s/^\([0-9]*\)1[+]/\10+/
+s/^\([0-9]*\)2[+]/\11+/
+s/^\([0-9]*\)3[+]/\12+/
+s/^\([0-9]*\)4[+]/\13+/
+s/^\([0-9]*\)5[+]/\14+/
+s/^\([0-9]*\)6[+]/\15+/
+s/^\([0-9]*\)7[+]/\16+/
+s/^\([0-9]*\)8[+]/\17+/
+s/^\([0-9]*\)9[+]/\18+/
+
+s/9=\([0-9]*\)$/_=\1/
+s/8=\([0-9]*\)$/9=\1/
+s/7=\([0-9]*\)$/8=\1/
+s/6=\([0-9]*\)$/7=\1/
+s/5=\([0-9]*\)$/6=\1/
+s/4=\([0-9]*\)$/5=\1/
+s/3=\([0-9]*\)$/4=\1/
+s/2=\([0-9]*\)$/3=\1/
+s/1=\([0-9]*\)$/2=\1/
+/_/{
+ s//_0/
+ : inc
+ s/9_/_0/
+ s/8_/9/
+ s/7_/8/
+ s/6_/7/
+ s/5_/6/
+ s/4_/5/
+ s/3_/4/
+ s/2_/3/
+ s/1_/2/
+ s/0_/1/
+ s/[+]_/+1/
+ /_/b inc
+}
+b add
+
+# get rid of a sub term until /-0*=/ or underflow
+: sub
+/^\([0-9]*\)-0*=/{
+ s//\1/
+ x
+ s/\(.*\)\n.*$/\1/
+ x
+ b leading
+}
+/^-\([0-9].*\)=/{
+: under
+ g
+ s/.*\n\([0-9]*\)-\([0-9]*\).*/-(\2-\1)/
+ x
+ s/\(.*\)\n.*/\1/
+ x
+ b loop
+}
+/^\([0-9]*\)\([0-9]\)-\([0-9]*\)0=/{
+ s//\1-\3=\2/
+ b sub
+}
+s/1=/0=/
+s/2=/1=/
+s/3=/2=/
+s/4=/3=/
+s/5=/4=/
+s/6=/5=/
+s/7=/6=/
+s/8=/7=/
+s/9=/8=/
+
+s/^\([0-9]*\)1-/\1_-/
+s/^\([0-9]*\)2-/\11-/
+s/^\([0-9]*\)3-/\12-/
+s/^\([0-9]*\)4-/\13-/
+s/^\([0-9]*\)5-/\14-/
+s/^\([0-9]*\)6-/\15-/
+s/^\([0-9]*\)7-/\16-/
+s/^\([0-9]*\)8-/\17-/
+s/^\([0-9]*\)9-/\18-/
+s/^\([0-9]*\)0-/\1'9-/
+s/_/0/
+
+: scarry
+/0'/{
+ s//'9/
+ b scarry
+}
+/^'/{
+ b under
+}
+s/1'/0/
+s/2'/1/
+s/3'/2/
+s/4'/3/
+s/5'/4/
+s/6'/5/
+s/7'/6/
+s/8'/7/
+s/9'/8/
+
+b sub
diff --git a/text_cmds/sed/TEST/sed.test b/text_cmds/sed/TEST/sed.test
new file mode 100644
index 0000000..8017a61
--- /dev/null
+++ b/text_cmds/sed/TEST/sed.test
@@ -0,0 +1,556 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992 Diomidis Spinellis.
+# Copyright (c) 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.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)sed.test 8.1 (Berkeley) 6/6/93
+#
+# $FreeBSD: src/usr.bin/sed/TEST/sed.test,v 1.4 2004/08/09 15:29:41 dds Exp $
+#
+
+# sed Regression Tests
+#
+# The following files are created:
+# lines[1-4], script1, script2
+# Two directories *.out contain the test results
+
+main()
+{
+ BASE=/usr/bin/sed
+ BASELOG=sed.out
+ TEST=`cd ..; make whereobj`/sed
+ TESTLOG=nsed.out
+ DICT=/usr/share/dict/words
+
+ test_error | more
+
+ awk 'END { for (i = 1; i < 15; i++) print "l1_" i}' </dev/null >lines1
+ awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' </dev/null >lines2
+
+ exec 4>&1 5>&2
+
+ # Set these flags to get messages about known problems
+ BSD=1
+ GNU=0
+ SUN=0
+ tests $BASE $BASELOG
+
+ BSD=0
+ GNU=0
+ SUN=0
+ tests $TEST $TESTLOG
+ exec 1>&4 2>&5
+ diff -c $BASELOG $TESTLOG | more
+}
+
+tests()
+{
+ SED=$1
+ DIR=$2
+ rm -rf $DIR
+ mkdir $DIR
+ MARK=100
+
+ test_args
+ test_addr
+ echo Testing commands
+ test_group
+ test_acid
+ test_branch
+ test_pattern
+ test_print
+ test_subst
+}
+
+mark()
+{
+ MARK=`expr $MARK + 1`
+ exec 1>&4 2>&5
+ exec >"$DIR/${MARK}_$1"
+ echo "Test $1:$MARK"
+ # Uncomment this line to match tests with sed error messages
+ echo "Test $1:$MARK" >&5
+}
+
+test_args()
+{
+ mark '1.1'
+ echo Testing argument parsing
+ echo First type
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED 's/^/e1_/p' lines1
+ fi
+ mark '1.2' ; $SED -n 's/^/e1_/p' lines1
+ mark '1.3'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED 's/^/e1_/p' <lines1
+ fi
+ mark '1.4' ; $SED -n 's/^/e1_/p' <lines1
+ echo Second type
+ mark '1.4.1'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed fails this
+ fi
+ $SED -e '' <lines1
+ echo 's/^/s1_/p' >script1
+ echo 's/^/s2_/p' >script2
+ mark '1.5'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 lines1
+ fi
+ mark '1.6'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 <lines1
+ fi
+ mark '1.7'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' lines1
+ fi
+ mark '1.8'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' <lines1
+ fi
+ mark '1.9' ; $SED -n -f script1 lines1
+ mark '1.10' ; $SED -n -f script1 <lines1
+ mark '1.11' ; $SED -n -e 's/^/e1_/p' lines1
+ mark '1.12'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -n -e 's/^/e1_/p' <lines1
+ fi
+ mark '1.13'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' -e 's/^/e2_/p' lines1
+ fi
+ mark '1.14'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 -f script2 lines1
+ fi
+ mark '1.15'
+ if [ $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo GNU and SunOS sed fail this following older POSIX draft
+ else
+ $SED -e 's/^/e1_/p' -f script1 lines1
+ fi
+ mark '1.16'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' lines1 lines1
+ fi
+ # POSIX D11.2:11251
+ mark '1.17' ; $SED p <lines1 lines1
+cat >script1 <<EOF
+#n
+# A comment
+
+p
+EOF
+ mark '1.18' ; $SED -f script1 <lines1 lines1
+}
+
+test_addr()
+{
+ echo Testing address ranges
+ mark '2.1' ; $SED -n -e '4p' lines1
+ mark '2.2' ; $SED -n -e '20p' lines1 lines2
+ mark '2.3' ; $SED -n -e '$p' lines1
+ mark '2.4' ; $SED -n -e '$p' lines1 lines2
+ mark '2.5' ; $SED -n -e '$a\
+hello' /dev/null
+ mark '2.6' ; $SED -n -e '$p' lines1 /dev/null lines2
+ # Should not print anything
+ mark '2.7' ; $SED -n -e '20p' lines1
+ mark '2.8' ; $SED -n -e '0p' lines1
+ mark '2.9' ; $SED -n '/l1_7/p' lines1
+ mark '2.10' ; $SED -n ' /l1_7/ p' lines1
+ mark '2.11'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '\_l1\_7_p' lines1
+ mark '2.12' ; $SED -n '1,4p' lines1
+ mark '2.13' ; $SED -n '1,$p' lines1 lines2
+ mark '2.14' ; $SED -n '1,/l2_9/p' lines1 lines2
+ mark '2.15' ; $SED -n '/4/,$p' lines1 lines2
+ mark '2.16' ; $SED -n '/4/,20p' lines1 lines2
+ mark '2.17' ; $SED -n '/4/,/10/p' lines1 lines2
+ mark '2.18' ; $SED -n '/l2_3/,/l1_8/p' lines1 lines2
+ mark '2.19'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '12,3p' lines1 lines2
+ mark '2.20'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '/l1_7/,3p' lines1 lines2
+}
+
+test_group()
+{
+ echo Brace and other grouping
+ mark '3.1' ; $SED -e '
+4,12 {
+ s/^/^/
+ s/$/$/
+ s/_/T/
+}' lines1
+ mark '3.2' ; $SED -e '
+4,12 {
+ s/^/^/
+ /6/,/10/ {
+ s/$/$/
+ /8/ s/_/T/
+ }
+}' lines1
+ mark '3.3' ; $SED -e '
+4,12 !{
+ s/^/^/
+ /6/,/10/ !{
+ s/$/$/
+ /8/ !s/_/T/
+ }
+}' lines1
+ mark '3.4' ; $SED -e '4,12!s/^/^/' lines1
+}
+
+test_acid()
+{
+ echo Testing a c d and i commands
+ mark '4.1' ; $SED -n -e '
+s/^/before_i/p
+20i\
+inserted
+s/^/after_i/p
+' lines1 lines2
+ mark '4.2' ; $SED -n -e '
+5,12s/^/5-12/
+s/^/before_a/p
+/5-12/a\
+appended
+s/^/after_a/p
+' lines1 lines2
+ mark '4.3'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n -e '
+s/^/^/p
+/l1_/a\
+appended
+8,10N
+s/$/$/p
+' lines1 lines2
+ mark '4.4' ; $SED -n -e '
+c\
+hello
+' lines1
+ mark '4.5' ; $SED -n -e '
+8c\
+hello
+' lines1
+ mark '4.6' ; $SED -n -e '
+3,14c\
+hello
+' lines1
+# SunOS and GNU sed behave differently. We follow POSIX
+# mark '4.7' ; $SED -n -e '
+#8,3c\
+#hello
+#' lines1
+ mark '4.8' ; $SED d <lines1
+}
+
+test_branch()
+{
+ echo Testing labels and branching
+ mark '5.1' ; $SED -n -e '
+b label4
+:label3
+s/^/label3_/p
+b end
+:label4
+2,12b label1
+b label2
+:label1
+s/^/label1_/p
+b
+:label2
+s/^/label2_/p
+b label3
+:end
+' lines1
+ mark '5.2'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ $SED -n -e '
+s/l1_/l2_/
+t ok
+b
+:ok
+s/^/tested /p
+' lines1 lines2
+# SunOS sed behaves differently here. Clarification needed.
+# mark '5.3' ; $SED -n -e '
+#5,8b inside
+#1,5 {
+# s/^/^/p
+# :inside
+# s/$/$/p
+#}
+#' lines1
+# Check that t clears the substitution done flag
+ mark '5.4' ; $SED -n -e '
+1,8s/^/^/
+t l1
+:l1
+t l2
+s/$/$/p
+b
+:l2
+s/^/ERROR/
+' lines1
+# Check that reading a line clears the substitution done flag
+ mark '5.5'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ $SED -n -e '
+t l2
+1,8s/^/^/p
+2,7N
+b
+:l2
+s/^/ERROR/p
+' lines1
+ mark '5.6' ; $SED 5q lines1
+ mark '5.7' ; $SED -e '
+5i\
+hello
+5q' lines1
+# Branch across block boundary
+ mark '5.8' ; $SED -e '
+{
+:b
+}
+s/l/m/
+tb' lines1
+}
+
+test_pattern()
+{
+echo Pattern space commands
+# Check that the pattern space is deleted
+ mark '6.1' ; $SED -n -e '
+c\
+changed
+p
+' lines1
+ mark '6.2' ; $SED -n -e '
+4d
+p
+' lines1
+# SunOS sed refused to print here
+# mark '6.3' ; $SED -e '
+#N
+#N
+#N
+#D
+#P
+#4p
+#' lines1
+ mark '6.4' ; $SED -e '
+2h
+3H
+4g
+5G
+6x
+6p
+6x
+6p
+' lines1
+ mark '6.5' ; $SED -e '4n' lines1
+ mark '6.6' ; $SED -n -e '4n' lines1
+}
+
+test_print()
+{
+ echo Testing print and file routines
+ awk 'END {for (i = 1; i < 256; i++) printf("%c", i);print "\n"}' \
+ </dev/null >lines3
+ # GNU and SunOS sed behave differently here
+ mark '7.1'
+ $SED -n l lines3
+ mark '7.2' ; $SED -e '/l2_/=' lines1 lines2
+ rm -f lines4
+ mark '7.3' ; $SED -e '3,12w lines4' lines1
+ echo w results
+ cat lines4
+ mark '7.4' ; $SED -e '4r lines2' lines1
+ mark '7.5' ; $SED -e '5r /dev/dds' lines1
+ mark '7.6' ; $SED -e '6r /dev/null' lines1
+ mark '7.7'
+ if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo BSD, GNU and SunOS cannot pass this one
+ else
+ sed '200q' $DICT | sed 's$.*$s/^/&/w tmpdir/&$' >script1
+ rm -rf tmpdir
+ mkdir tmpdir
+ $SED -f script1 lines1
+ cat tmpdir/*
+ rm -rf tmpdir
+ fi
+ mark '7.8'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed cannot pass 7.7
+ else
+ echo line1 > lines3
+ echo "" >> lines3
+ $SED -n -e '$p' lines3 /dev/null
+ fi
+
+}
+
+test_subst()
+{
+ echo Testing substitution commands
+ mark '8.1' ; $SED -e 's/./X/g' lines1
+ mark '8.2' ; $SED -e 's,.,X,g' lines1
+# GNU and SunOS sed thinks we are escaping . as wildcard, not as separator
+# mark '8.3' ; $SED -e 's.\..X.g' lines1
+# POSIX does not say that this should work
+# mark '8.4' ; $SED -e 's/[/]/Q/' lines1
+ mark '8.4' ; $SED -e 's/[\/]/Q/' lines1
+ mark '8.5' ; $SED -e 's_\__X_' lines1
+ mark '8.6' ; $SED -e 's/./(&)/g' lines1
+ mark '8.7' ; $SED -e 's/./(\&)/g' lines1
+ mark '8.8' ; $SED -e 's/\(.\)\(.\)\(.\)/x\3x\2x\1/g' lines1
+ mark '8.9' ; $SED -e 's/_/u0\
+u1\
+u2/g' lines1
+ mark '8.10'
+ if [ $BSD -eq 1 -o $GNU -eq 1 ] ; then
+ echo 'BSD/GNU sed do not understand digit flags on s commands'
+ fi
+ $SED -e 's/./X/4' lines1
+ rm -f lines4
+ mark '8.11' ; $SED -e 's/1/X/w lines4' lines1
+ echo s wfile results
+ cat lines4
+ mark '8.12' ; $SED -e 's/[123]/X/g' lines1
+ mark '8.13' ; $SED -e 'y/0123456789/9876543210/' lines1
+ mark '8.14' ;
+ if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo BSD/GNU/SUN sed fail this test
+ else
+ $SED -e 'y10\123456789198765432\101' lines1
+ fi
+ mark '8.15' ; $SED -e '1N;2y/\n/X/' lines1
+ mark '8.16'
+ echo 'eeefff' | $SED -e '
+ p
+ s/e/X/p
+ :x
+ s//Y/p
+ # Establish limit counter in the hold space
+ # GNU sed version 3.02 enters into an infinite loop here
+ x
+ /.\{10\}/ {
+ s/.*/ERROR/
+ b
+ }
+ s/.*/&./
+ x
+ /f/bx
+ '
+}
+
+test_error()
+{
+ exec 0>&3 4>&1 5>&2
+ exec 0</dev/null
+ exec 2>&1
+ set -x
+ $TEST -x && exit 1
+ $TEST -f && exit 1
+ $TEST -e && exit 1
+ $TEST -f /dev/dds && exit 1
+ $TEST p /dev/dds && exit 1
+ $TEST -f /bin/sh && exit 1
+ $TEST '{' && exit 1
+ $TEST '{' && exit 1
+ $TEST '/hello/' && exit 1
+ $TEST '1,/hello/' && exit 1
+ $TEST -e '-5p' && exit 1
+ $TEST '/jj' && exit 1
+ $TEST 'a hello' && exit 1
+ $TEST 'a \ hello' && exit 1
+ $TEST 'b foo' && exit 1
+ $TEST 'd hello' && exit 1
+ $TEST 's/aa' && exit 1
+ $TEST 's/aa/' && exit 1
+ $TEST 's/a/b' && exit 1
+ $TEST 's/a/b/c/d' && exit 1
+ $TEST 's/a/b/ 1 2' && exit 1
+ $TEST 's/a/b/ 1 g' && exit 1
+ $TEST 's/a/b/w' && exit 1
+ $TEST 'y/aa' && exit 1
+ $TEST 'y/aa/b/' && exit 1
+ $TEST 'y/aa/' && exit 1
+ $TEST 'y/a/b' && exit 1
+ $TEST 'y/a/b/c/d' && exit 1
+ $TEST '!' && exit 1
+ $TEST supercalifrangolisticexprialidociussupercalifrangolisticexcius
+ set +x
+ exec 0>&3 1>&4 2>&5
+}
+
+main
diff --git a/text_cmds/sed/compile.c b/text_cmds/sed/compile.c
new file mode 100644
index 0000000..fbd19fa
--- /dev/null
+++ b/text_cmds/sed/compile.c
@@ -0,0 +1,987 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "defs.h"
+#include "extern.h"
+
+#define LHSZ 128
+#define LHMASK (LHSZ - 1)
+static struct labhash {
+ struct labhash *lh_next;
+ u_int lh_hash;
+ struct s_command *lh_cmd;
+ int lh_ref;
+} *labels[LHSZ];
+
+static char *compile_addr(char *, struct s_addr *);
+static char *compile_ccl(char **, char *);
+static char *compile_delimited(char *, char *, int);
+static char *compile_flags(char *, struct s_subst *);
+static regex_t *compile_re(char *, int);
+static char *compile_subst(char *, struct s_subst *);
+static char *compile_text(void);
+static char *compile_tr(char *, struct s_tr **);
+static struct s_command
+ **compile_stream(struct s_command **);
+static char *duptoeol(char *, const char *);
+static void enterlabel(struct s_command *);
+static struct s_command
+ *findlabel(char *);
+static void fixuplabel(struct s_command *, struct s_command *);
+static void uselabel(void);
+
+/*
+ * Command specification. This is used to drive the command parser.
+ */
+struct s_format {
+ char code; /* Command code */
+ int naddr; /* Number of address args */
+ enum e_args args; /* Argument type */
+};
+
+static struct s_format cmd_fmts[] = {
+ {'{', 2, GROUP},
+ {'}', 0, ENDGROUP},
+ {'a', 1, TEXT},
+ {'b', 2, BRANCH},
+ {'c', 2, TEXT},
+ {'d', 2, EMPTY},
+ {'D', 2, EMPTY},
+ {'g', 2, EMPTY},
+ {'G', 2, EMPTY},
+ {'h', 2, EMPTY},
+ {'H', 2, EMPTY},
+ {'i', 1, TEXT},
+ {'l', 2, EMPTY},
+ {'n', 2, EMPTY},
+ {'N', 2, EMPTY},
+ {'p', 2, EMPTY},
+ {'P', 2, EMPTY},
+ {'q', 1, EMPTY},
+ {'r', 1, RFILE},
+ {'s', 2, SUBST},
+ {'t', 2, BRANCH},
+ {'w', 2, WFILE},
+ {'x', 2, EMPTY},
+ {'y', 2, TR},
+ {'!', 2, NONSEL},
+ {':', 0, LABEL},
+ {'#', 0, COMMENT},
+ {'=', 1, EMPTY},
+ {'\0', 0, COMMENT},
+};
+
+/* The compiled program. */
+struct s_command *prog;
+
+/*
+ * Compile the program into prog.
+ * Initialise appends.
+ */
+void
+compile(void)
+{
+ *compile_stream(&prog) = NULL;
+ fixuplabel(prog, NULL);
+ uselabel();
+ if (appendnum == 0)
+ appends = NULL;
+ else if ((appends = malloc(sizeof(struct s_appends) * appendnum)) ==
+ NULL)
+ err(1, "malloc");
+ if ((match = malloc((maxnsub + 1) * sizeof(regmatch_t))) == NULL)
+ err(1, "malloc");
+}
+
+#define EATSPACE() do { \
+ if (p) \
+ while (*p && isspace((unsigned char)*p)) \
+ p++; \
+ } while (0)
+
+static struct s_command **
+compile_stream(struct s_command **link)
+{
+ char *p;
+ static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */
+ struct s_command *cmd, *cmd2, *stack;
+ struct s_format *fp;
+ char re[_POSIX2_LINE_MAX + 1];
+ int naddr; /* Number of addresses */
+
+ stack = NULL;
+ for (;;) {
+ if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) {
+ if (stack != NULL)
+ errx(1, "%lu: %s: unexpected EOF (pending }'s)",
+ linenum, fname);
+ return (link);
+ }
+
+semicolon: EATSPACE();
+ if (p) {
+ if (*p == '#' || *p == '\0')
+ continue;
+ else if (*p == ';') {
+ p++;
+ goto semicolon;
+ }
+ }
+ if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL)
+ err(1, "malloc");
+ link = &cmd->next;
+ cmd->startline = cmd->nonsel = 0;
+ /* First parse the addresses */
+ naddr = 0;
+
+/* Valid characters to start an address */
+#define addrchar(c) (strchr("0123456789/\\$", (c)))
+ if (addrchar(*p)) {
+ naddr++;
+ if ((cmd->a1 = malloc(sizeof(struct s_addr))) == NULL)
+ err(1, "malloc");
+ p = compile_addr(p, cmd->a1);
+ EATSPACE(); /* EXTENSION */
+ if (*p == ',') {
+ p++;
+ EATSPACE(); /* EXTENSION */
+ naddr++;
+ if ((cmd->a2 = malloc(sizeof(struct s_addr)))
+ == NULL)
+ err(1, "malloc");
+ p = compile_addr(p, cmd->a2);
+ EATSPACE();
+ } else
+ cmd->a2 = NULL;
+ } else
+ cmd->a1 = cmd->a2 = NULL;
+
+nonsel: /* Now parse the command */
+ if (!*p)
+ errx(1, "%lu: %s: command expected", linenum, fname);
+ cmd->code = *p;
+ for (fp = cmd_fmts; fp->code; fp++)
+ if (fp->code == *p)
+ break;
+ if (!fp->code)
+ errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p);
+ if (naddr > fp->naddr)
+ errx(1,
+ "%lu: %s: command %c expects up to %d address(es), found %d",
+ linenum, fname, *p, fp->naddr, naddr);
+ switch (fp->args) {
+ case NONSEL: /* ! */
+ p++;
+ EATSPACE();
+ cmd->nonsel = 1;
+ goto nonsel;
+ case GROUP: /* { */
+ p++;
+ EATSPACE();
+ cmd->next = stack;
+ stack = cmd;
+ link = &cmd->u.c;
+ if (*p)
+ goto semicolon;
+ break;
+ case ENDGROUP:
+ /*
+ * Short-circuit command processing, since end of
+ * group is really just a noop.
+ */
+ cmd->nonsel = 1;
+ if (stack == NULL)
+ errx(1, "%lu: %s: unexpected }", linenum, fname);
+ cmd2 = stack;
+ stack = cmd2->next;
+ cmd2->next = cmd;
+ /*FALLTHROUGH*/
+ case EMPTY: /* d D g G h H l n N p P q x = \0 */
+ p++;
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ if (*p)
+ errx(1, "%lu: %s: extra characters at the end of %c command",
+ linenum, fname, cmd->code);
+ break;
+ case TEXT: /* a c i */
+ p++;
+ EATSPACE();
+ if (*p != '\\')
+ errx(1,
+"%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code);
+ p++;
+ EATSPACE();
+ if (*p)
+ errx(1,
+ "%lu: %s: extra characters after \\ at the end of %c command",
+ linenum, fname, cmd->code);
+ cmd->t = compile_text();
+ break;
+ case COMMENT: /* \0 # */
+ break;
+ case WFILE: /* w */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ errx(1, "%lu: %s: filename expected", linenum, fname);
+ cmd->t = duptoeol(p, "w command");
+ if (aflag)
+ cmd->u.fd = -1;
+ else if ((cmd->u.fd = open(p,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(1, "%s", p);
+ break;
+ case RFILE: /* r */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ errx(1, "%lu: %s: filename expected", linenum, fname);
+ else
+ cmd->t = duptoeol(p, "read command");
+ break;
+ case BRANCH: /* b t */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ cmd->t = NULL;
+ else
+ cmd->t = duptoeol(p, "branch");
+ break;
+ case LABEL: /* : */
+ p++;
+ EATSPACE();
+ cmd->t = duptoeol(p, "label");
+ if (strlen(p) == 0)
+ errx(1, "%lu: %s: empty label", linenum, fname);
+ enterlabel(cmd);
+ break;
+ case SUBST: /* s */
+ p++;
+ if (*p == '\0' || *p == '\\')
+ errx(1,
+"%lu: %s: substitute pattern can not be delimited by newline or backslash",
+ linenum, fname);
+ if ((cmd->u.s = calloc(1, sizeof(struct s_subst))) == NULL)
+ err(1, "malloc");
+ p = compile_delimited(p, re, 0);
+ if (p == NULL)
+ errx(1,
+ "%lu: %s: unterminated substitute pattern", linenum, fname);
+
+ /* Compile RE with no case sensitivity temporarily */
+ if (*re == '\0')
+ cmd->u.s->re = NULL;
+ else
+ cmd->u.s->re = compile_re(re, 0);
+ --p;
+ p = compile_subst(p, cmd->u.s);
+ p = compile_flags(p, cmd->u.s);
+
+ /* Recompile RE with case sensitivity from "I" flag if any */
+ if (*re == '\0')
+ cmd->u.s->re = NULL;
+ else
+ cmd->u.s->re = compile_re(re, cmd->u.s->icase);
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ break;
+ case TR: /* y */
+ p++;
+ p = compile_tr(p, &cmd->u.y);
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ if (*p)
+ errx(1,
+"%lu: %s: extra text at the end of a transform command", linenum, fname);
+ break;
+ }
+ }
+}
+
+/*
+ * Get a delimited string. P points to the delimiter of the string; d points
+ * to a buffer area. Newline and delimiter escapes are processed; other
+ * escapes are ignored.
+ *
+ * Returns a pointer to the first character after the final delimiter or NULL
+ * in the case of a non-terminated string. The character array d is filled
+ * with the processed string.
+ */
+static char *
+compile_delimited(char *p, char *d, int is_tr)
+{
+ char c;
+
+ c = *p++;
+ if (c == '\0')
+ return (NULL);
+ else if (c == '\\')
+ errx(1, "%lu: %s: \\ can not be used as a string delimiter",
+ linenum, fname);
+ else if (c == '\n')
+ errx(1, "%lu: %s: newline can not be used as a string delimiter",
+ linenum, fname);
+ while (*p) {
+ if (*p == '[' && *p != c) {
+ if ((d = compile_ccl(&p, d)) == NULL)
+ errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname);
+ continue;
+ } else if (*p == '\\' && p[1] == '[') {
+ *d++ = *p++;
+ } else if (*p == '\\' && p[1] == c) {
+ p++;
+ } else if (*p == '\\' &&
+ (p[1] == 'n' || p[1] == 'r' || p[1] == 't')) {
+ switch (p[1]) {
+ case 'n':
+ *d++ = '\n';
+ break;
+ case 'r':
+ *d++ = '\r';
+ break;
+ case 't':
+ *d++ = '\t';
+ break;
+ }
+ p += 2;
+ continue;
+ } else if (*p == '\\' && p[1] == '\\') {
+ if (is_tr)
+ p++;
+ else
+ *d++ = *p++;
+ } else if (*p == c) {
+ *d = '\0';
+ return (p + 1);
+ }
+ *d++ = *p++;
+ }
+ return (NULL);
+}
+
+
+/* compile_ccl: expand a POSIX character class */
+static char *
+compile_ccl(char **sp, char *t)
+{
+ int c, d;
+ char *s = *sp;
+
+ *t++ = *s++;
+ if (*s == '^')
+ *t++ = *s++;
+ if (*s == ']')
+ *t++ = *s++;
+ for (; *s && (*t = *s) != ']'; s++, t++) {
+ if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) {
+ *++t = *++s, t++, s++;
+ for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
+ if ((c = *s) == '\0')
+ return NULL;
+ }
+ /*
+ * Apple Note: FreeBSD treats the backslash character as the start of
+ * several possible escape sequences here, but this is not POSIX-
+ * compliant. See XBD 9.3.3: "The period, left-bracket, and backslash
+ * shall be special except when used in a bracket expression".
+ */
+ }
+ return (*s == ']') ? *sp = ++s, ++t : NULL;
+}
+
+/*
+ * Compiles the regular expression in RE and returns a pointer to the compiled
+ * regular expression.
+ * Cflags are passed to regcomp.
+ */
+static regex_t *
+compile_re(char *re, int case_insensitive)
+{
+ regex_t *rep;
+ int eval, flags;
+
+
+ flags = rflags;
+ if (case_insensitive)
+ flags |= REG_ICASE;
+ if ((rep = malloc(sizeof(regex_t))) == NULL)
+ err(1, "malloc");
+ if ((eval = regcomp(rep, re, flags)) != 0)
+ errx(1, "%lu: %s: RE error: %s",
+ linenum, fname, strregerror(eval, rep));
+ if (maxnsub < rep->re_nsub)
+ maxnsub = rep->re_nsub;
+ return (rep);
+}
+
+/*
+ * Compile the substitution string of a regular expression and set res to
+ * point to a saved copy of it. Nsub is the number of parenthesized regular
+ * expressions.
+ */
+static char *
+compile_subst(char *p, struct s_subst *s)
+{
+ static char lbuf[_POSIX2_LINE_MAX + 1];
+ int asize, size;
+ u_char ref;
+ char c, *text, *op, *sp;
+ int more = 1, sawesc = 0;
+
+ c = *p++; /* Terminator character */
+ if (c == '\0')
+ return (NULL);
+
+ s->maxbref = 0;
+ s->linenum = linenum;
+ asize = 2 * _POSIX2_LINE_MAX + 1;
+ if ((text = malloc(asize)) == NULL)
+ err(1, "malloc");
+ size = 0;
+ do {
+ op = sp = text + size;
+ for (; *p; p++) {
+ if (*p == '\\' || sawesc) {
+ /*
+ * If this is a continuation from the last
+ * buffer, we won't have a character to
+ * skip over.
+ */
+ if (sawesc)
+ sawesc = 0;
+ else
+ p++;
+
+ if (*p == '\0') {
+ /*
+ * This escaped character is continued
+ * in the next part of the line. Note
+ * this fact, then cause the loop to
+ * exit w/ normal EOL case and reenter
+ * above with the new buffer.
+ */
+ sawesc = 1;
+ p--;
+ continue;
+ } else if (strchr("123456789", *p) != NULL) {
+ *sp++ = '\\';
+ ref = *p - '0';
+ if (s->re != NULL &&
+ ref > s->re->re_nsub)
+ errx(1, "%lu: %s: \\%c not defined in the RE",
+ linenum, fname, *p);
+ if (s->maxbref < ref)
+ s->maxbref = ref;
+ } else {
+ switch (*p) {
+ case '&':
+ case '\\':
+ *sp++ = '\\';
+ break;
+ case 'n':
+ *p = '\n';
+ break;
+ case 'r':
+ *p = '\r';
+ break;
+ case 't':
+ *p = '\t';
+ break;
+ }
+ }
+ } else if (*p == c) {
+ if (*++p == '\0' && more) {
+ if (cu_fgets(lbuf, sizeof(lbuf), &more))
+ p = lbuf;
+ }
+ *sp++ = '\0';
+ size += sp - op;
+ if ((s->new = realloc(text, size)) == NULL)
+ err(1, "realloc");
+ return (p);
+ } else if (*p == '\n') {
+ errx(1,
+"%lu: %s: unescaped newline inside substitute pattern", linenum, fname);
+ /* NOTREACHED */
+ }
+ *sp++ = *p;
+ }
+ size += sp - op;
+ if (asize - size < _POSIX2_LINE_MAX + 1) {
+ asize *= 2;
+ if ((text = realloc(text, asize)) == NULL)
+ err(1, "realloc");
+ }
+ } while (cu_fgets(p = lbuf, sizeof(lbuf), &more) != NULL);
+ errx(1, "%lu: %s: unterminated substitute in regular expression",
+ linenum, fname);
+ /* NOTREACHED */
+}
+
+/*
+ * Compile the flags of the s command
+ */
+static char *
+compile_flags(char *p, struct s_subst *s)
+{
+ int gn; /* True if we have seen g or n */
+ unsigned long nval;
+ char wfile[_POSIX2_LINE_MAX + 1], *q, *eq;
+
+ s->n = 1; /* Default */
+ s->p = 0;
+ s->wfile = NULL;
+ s->wfd = -1;
+ s->icase = 0;
+ for (gn = 0;;) {
+ EATSPACE(); /* EXTENSION */
+ switch (*p) {
+ case 'g':
+ if (gn)
+ errx(1,
+"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
+ gn = 1;
+ s->n = 0;
+ break;
+ case '\0':
+ case '\n':
+ case ';':
+ return (p);
+ case 'p':
+ s->p = 1;
+ break;
+ case 'i':
+ case 'I':
+ s->icase = 1;
+ break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if (gn)
+ errx(1,
+"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
+ gn = 1;
+ errno = 0;
+ nval = strtol(p, &p, 10);
+ if (errno == ERANGE || nval > INT_MAX)
+ errx(1,
+"%lu: %s: overflow in the 'N' substitute flag", linenum, fname);
+ s->n = nval;
+ p--;
+ break;
+ case 'w':
+ p++;
+#ifdef HISTORIC_PRACTICE
+ if (*p != ' ') {
+ warnx("%lu: %s: space missing before w wfile", linenum, fname);
+ return (p);
+ }
+#endif
+ EATSPACE();
+ q = wfile;
+ eq = wfile + sizeof(wfile) - 1;
+ while (*p) {
+ if (*p == '\n')
+ break;
+ if (q >= eq)
+ err(1, "wfile too long");
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (q == wfile)
+ errx(1, "%lu: %s: no wfile specified", linenum, fname);
+ s->wfile = strdup(wfile);
+ if (!aflag && (s->wfd = open(wfile,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(1, "%s", wfile);
+ return (p);
+ default:
+ errx(1, "%lu: %s: bad flag in substitute command: '%c'",
+ linenum, fname, *p);
+ break;
+ }
+ p++;
+ }
+}
+
+/*
+ * Compile a translation set of strings into a lookup table.
+ */
+static char *
+compile_tr(char *p, struct s_tr **py)
+{
+ struct s_tr *y;
+ int i;
+ const char *op, *np;
+ char old[_POSIX2_LINE_MAX + 1];
+ char new[_POSIX2_LINE_MAX + 1];
+ size_t oclen, oldlen, nclen, newlen;
+ mbstate_t mbs1, mbs2;
+
+ if ((*py = y = malloc(sizeof(*y))) == NULL)
+ err(1, NULL);
+ y->multis = NULL;
+ y->nmultis = 0;
+
+ if (*p == '\0' || *p == '\\')
+ errx(1,
+ "%lu: %s: transform pattern can not be delimited by newline or backslash",
+ linenum, fname);
+ p = compile_delimited(p, old, 1);
+ if (p == NULL)
+ errx(1, "%lu: %s: unterminated transform source string",
+ linenum, fname);
+ p = compile_delimited(p - 1, new, 1);
+ if (p == NULL)
+ errx(1, "%lu: %s: unterminated transform target string",
+ linenum, fname);
+ EATSPACE();
+ op = old;
+ oldlen = mbsrtowcs(NULL, &op, 0, NULL);
+ if (oldlen == (size_t)-1)
+ err(1, NULL);
+ np = new;
+ newlen = mbsrtowcs(NULL, &np, 0, NULL);
+ if (newlen == (size_t)-1)
+ err(1, NULL);
+ if (newlen != oldlen)
+ errx(1, "%lu: %s: transform strings are not the same length",
+ linenum, fname);
+ if (MB_CUR_MAX == 1) {
+ /*
+ * The single-byte encoding case is easy: generate a
+ * lookup table.
+ */
+ for (i = 0; i <= UCHAR_MAX; i++)
+ y->bytetab[i] = (char)i;
+ for (; *op; op++, np++)
+ y->bytetab[(u_char)*op] = *np;
+ } else {
+ /*
+ * Multi-byte encoding case: generate a lookup table as
+ * above, but only for single-byte characters. The first
+ * bytes of multi-byte characters have their lookup table
+ * entries set to 0, which causes do_tr() to search through
+ * an auxiliary vector of multi-byte mappings.
+ */
+ memset(&mbs1, 0, sizeof(mbs1));
+ memset(&mbs2, 0, sizeof(mbs2));
+ for (i = 0; i <= UCHAR_MAX; i++)
+ y->bytetab[i] = (btowc(i) != WEOF) ? i : 0;
+ while (*op != '\0') {
+ oclen = mbrlen(op, MB_LEN_MAX, &mbs1);
+ if (oclen == (size_t)-1 || oclen == (size_t)-2)
+ errc(1, EILSEQ, NULL);
+ nclen = mbrlen(np, MB_LEN_MAX, &mbs2);
+ if (nclen == (size_t)-1 || nclen == (size_t)-2)
+ errc(1, EILSEQ, NULL);
+ if (oclen == 1 && nclen == 1)
+ y->bytetab[(u_char)*op] = *np;
+ else {
+ y->bytetab[(u_char)*op] = 0;
+ y->multis = realloc(y->multis,
+ (y->nmultis + 1) * sizeof(*y->multis));
+ if (y->multis == NULL)
+ err(1, NULL);
+ i = y->nmultis++;
+ y->multis[i].fromlen = oclen;
+ memcpy(y->multis[i].from, op, oclen);
+ y->multis[i].tolen = nclen;
+ memcpy(y->multis[i].to, np, nclen);
+ }
+ op += oclen;
+ np += nclen;
+ }
+ }
+ return (p);
+}
+
+/*
+ * Compile the text following an a, c, or i command.
+ */
+static char *
+compile_text(void)
+{
+ int asize, esc_nl, size;
+ char *text, *p, *op, *s;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ asize = 2 * _POSIX2_LINE_MAX + 1;
+ if ((text = malloc(asize)) == NULL)
+ err(1, "malloc");
+ size = 0;
+ while (cu_fgets(lbuf, sizeof(lbuf), NULL) != NULL) {
+ op = s = text + size;
+ p = lbuf;
+#ifdef LEGACY_BSDSED_COMPAT
+ EATSPACE();
+#endif
+ for (esc_nl = 0; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0' && *++p == '\n')
+ esc_nl = 1;
+ *s++ = *p;
+ }
+ size += s - op;
+ if (!esc_nl) {
+ *s = '\0';
+ break;
+ }
+ if (asize - size < _POSIX2_LINE_MAX + 1) {
+ asize *= 2;
+ if ((text = realloc(text, asize)) == NULL)
+ err(1, "realloc");
+ }
+ }
+ text[size] = '\0';
+ if ((p = realloc(text, size + 1)) == NULL)
+ err(1, "realloc");
+ return (p);
+}
+
+/*
+ * Get an address and return a pointer to the first character after
+ * it. Fill the structure pointed to according to the address.
+ */
+static char *
+compile_addr(char *p, struct s_addr *a)
+{
+ char *end, re[_POSIX2_LINE_MAX + 1];
+ int icase;
+
+ icase = 0;
+
+ a->type = 0;
+ switch (*p) {
+ case '\\': /* Context address */
+ ++p;
+ /* FALLTHROUGH */
+ case '/': /* Context address */
+ p = compile_delimited(p, re, 0);
+ if (p == NULL)
+ errx(1, "%lu: %s: unterminated regular expression", linenum, fname);
+ /* Check for case insensitive regexp flag */
+ if (*p == 'I') {
+ icase = 1;
+ p++;
+ }
+ if (*re == '\0')
+ a->u.r = NULL;
+ else
+ a->u.r = compile_re(re, icase);
+ a->type = AT_RE;
+ return (p);
+
+ case '$': /* Last line */
+ a->type = AT_LAST;
+ return (p + 1);
+
+ case '+': /* Relative line number */
+ a->type = AT_RELLINE;
+ p++;
+ /* FALLTHROUGH */
+ /* Line number */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (a->type == 0)
+ a->type = AT_LINE;
+ a->u.l = strtol(p, &end, 10);
+ return (end);
+ default:
+ errx(1, "%lu: %s: expected context address", linenum, fname);
+ return (NULL);
+ }
+}
+
+/*
+ * duptoeol --
+ * Return a copy of all the characters up to \n or \0.
+ */
+static char *
+duptoeol(char *s, const char *ctype)
+{
+ size_t len;
+ int ws;
+ char *p, *start;
+
+ ws = 0;
+ for (start = s; *s != '\0' && *s != '\n'; ++s)
+ ws = isspace((unsigned char)*s);
+ *s = '\0';
+ if (ws)
+ warnx("%lu: %s: whitespace after %s", linenum, fname, ctype);
+ len = s - start + 1;
+ if ((p = malloc(len)) == NULL)
+ err(1, "malloc");
+ return (memmove(p, start, len));
+}
+
+/*
+ * Convert goto label names to addresses, and count a and r commands, in
+ * the given subset of the script. Free the memory used by labels in b
+ * and t commands (but not by :).
+ *
+ * TODO: Remove } nodes
+ */
+static void
+fixuplabel(struct s_command *cp, struct s_command *end)
+{
+
+ for (; cp != end; cp = cp->next)
+ switch (cp->code) {
+ case 'a':
+ case 'r':
+ appendnum++;
+ break;
+ case 'b':
+ case 't':
+ /* Resolve branch target. */
+ if (cp->t == NULL) {
+ cp->u.c = NULL;
+ break;
+ }
+ if ((cp->u.c = findlabel(cp->t)) == NULL)
+ errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t);
+ free(cp->t);
+ break;
+ case '{':
+ /* Do interior commands. */
+ fixuplabel(cp->u.c, cp->next);
+ break;
+ }
+}
+
+/*
+ * Associate the given command label for later lookup.
+ */
+static void
+enterlabel(struct s_command *cp)
+{
+ struct labhash **lhp, *lh;
+ u_char *p;
+ u_int h, c;
+
+ for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)
+ h = (h << 5) + h + c;
+ lhp = &labels[h & LHMASK];
+ for (lh = *lhp; lh != NULL; lh = lh->lh_next)
+ if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)
+ errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t);
+ if ((lh = malloc(sizeof *lh)) == NULL)
+ err(1, "malloc");
+ lh->lh_next = *lhp;
+ lh->lh_hash = h;
+ lh->lh_cmd = cp;
+ lh->lh_ref = 0;
+ *lhp = lh;
+}
+
+/*
+ * Find the label contained in the command l in the command linked
+ * list cp. L is excluded from the search. Return NULL if not found.
+ */
+static struct s_command *
+findlabel(char *name)
+{
+ struct labhash *lh;
+ u_char *p;
+ u_int h, c;
+
+ for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)
+ h = (h << 5) + h + c;
+ for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) {
+ if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) {
+ lh->lh_ref = 1;
+ return (lh->lh_cmd);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Warn about any unused labels. As a side effect, release the label hash
+ * table space.
+ */
+static void
+uselabel(void)
+{
+ struct labhash *lh, *next;
+ int i;
+
+ for (i = 0; i < LHSZ; i++) {
+ for (lh = labels[i]; lh != NULL; lh = next) {
+ next = lh->lh_next;
+ if (!lh->lh_ref)
+ warnx("%lu: %s: unused label '%s'",
+ linenum, fname, lh->lh_cmd->t);
+ free(lh);
+ }
+ }
+}
diff --git a/text_cmds/sed/defs.h b/text_cmds/sed/defs.h
new file mode 100644
index 0000000..894b5f3
--- /dev/null
+++ b/text_cmds/sed/defs.h
@@ -0,0 +1,151 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * 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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+/*
+ * Types of address specifications
+ */
+enum e_atype {
+ AT_RE = 1, /* Line that match RE */
+ AT_LINE, /* Specific line */
+ AT_RELLINE, /* Relative line */
+ AT_LAST, /* Last line */
+};
+
+/*
+ * Format of an address
+ */
+struct s_addr {
+ enum e_atype type; /* Address type */
+ union {
+ u_long l; /* Line number */
+ regex_t *r; /* Regular expression */
+ } u;
+};
+
+/*
+ * Substitution command
+ */
+struct s_subst {
+ int n; /* Occurrence to subst. */
+ int p; /* True if p flag */
+ int icase; /* True if I flag */
+ char *wfile; /* NULL if no wfile */
+ int wfd; /* Cached file descriptor */
+ regex_t *re; /* Regular expression */
+ unsigned int maxbref; /* Largest backreference. */
+ u_long linenum; /* Line number. */
+ char *new; /* Replacement text */
+};
+
+/*
+ * Translate command.
+ */
+struct s_tr {
+ unsigned char bytetab[256];
+ struct trmulti {
+ size_t fromlen;
+ char from[MB_LEN_MAX];
+ size_t tolen;
+ char to[MB_LEN_MAX];
+ } *multis;
+ int nmultis;
+};
+
+/*
+ * An internally compiled command.
+ * Initialy, label references are stored in t, on a second pass they
+ * are updated to pointers.
+ */
+struct s_command {
+ struct s_command *next; /* Pointer to next command */
+ struct s_addr *a1, *a2; /* Start and end address */
+ u_long startline; /* Start line number or zero */
+ char *t; /* Text for : a c i r w */
+ union {
+ struct s_command *c; /* Command(s) for b t { */
+ struct s_subst *s; /* Substitute command */
+ struct s_tr *y; /* Replace command array */
+ int fd; /* File descriptor for w */
+ } u;
+ char code; /* Command code */
+ u_int nonsel:1; /* True if ! */
+};
+
+/*
+ * Types of command arguments recognised by the parser
+ */
+enum e_args {
+ EMPTY, /* d D g G h H l n N p P q x = \0 */
+ TEXT, /* a c i */
+ NONSEL, /* ! */
+ GROUP, /* { */
+ ENDGROUP, /* } */
+ COMMENT, /* # */
+ BRANCH, /* b t */
+ LABEL, /* : */
+ RFILE, /* r */
+ WFILE, /* w */
+ SUBST, /* s */
+ TR /* y */
+};
+
+/*
+ * Structure containing things to append before a line is read
+ */
+struct s_appends {
+ enum {AP_STRING, AP_FILE} type;
+ char *s;
+ size_t len;
+};
+
+enum e_spflag {
+ APPEND, /* Append to the contents. */
+ REPLACE, /* Replace the contents. */
+};
+
+/*
+ * Structure for a space (process, hold, otherwise).
+ */
+typedef struct {
+ char *space; /* Current space pointer. */
+ size_t len; /* Current length. */
+ int deleted; /* If deleted. */
+ int append_newline; /* If originally terminated by \n. */
+ char *back; /* Backing memory. */
+ size_t blen; /* Backing memory length. */
+} SPACE;
diff --git a/text_cmds/sed/extern.h b/text_cmds/sed/extern.h
new file mode 100644
index 0000000..4d88650
--- /dev/null
+++ b/text_cmds/sed/extern.h
@@ -0,0 +1,60 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+extern struct s_command *prog;
+extern struct s_appends *appends;
+extern regmatch_t *match;
+extern size_t maxnsub;
+extern u_long linenum;
+extern unsigned int appendnum;
+extern int aflag, eflag, nflag;
+extern const char *fname, *outfname;
+extern FILE *infile, *outfile;
+extern int rflags; /* regex flags to use */
+extern const char *inplace;
+extern int quit;
+
+void cfclose(struct s_command *, struct s_command *);
+void compile(void);
+void cspace(SPACE *, const char *, size_t, enum e_spflag);
+char *cu_fgets(char *, int, int *);
+int mf_fgets(SPACE *, enum e_spflag);
+int lastline(void);
+void process(void);
+void resetstate(void);
+char *strregerror(int, regex_t *);
diff --git a/text_cmds/sed/main.c b/text_cmds/sed/main.c
new file mode 100644
index 0000000..96723cc
--- /dev/null
+++ b/text_cmds/sed/main.c
@@ -0,0 +1,547 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson.
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94";
+#endif
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <locale.h>
+#include <regex.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "defs.h"
+#include "extern.h"
+
+/*
+ * Linked list of units (strings and files) to be compiled
+ */
+struct s_compunit {
+ struct s_compunit *next;
+ enum e_cut {CU_FILE, CU_STRING} type;
+ char *s; /* Pointer to string or fname */
+};
+
+/*
+ * Linked list pointer to compilation units and pointer to current
+ * next pointer.
+ */
+static struct s_compunit *script, **cu_nextp = &script;
+
+/*
+ * Linked list of files to be processed
+ */
+struct s_flist {
+ char *fname;
+ struct s_flist *next;
+};
+
+/*
+ * Linked list pointer to files and pointer to current
+ * next pointer.
+ */
+static struct s_flist *files, **fl_nextp = &files;
+
+FILE *infile; /* Current input file */
+FILE *outfile; /* Current output file */
+
+int aflag, eflag, nflag;
+int rflags = 0;
+int quit = 0;
+static int rval; /* Exit status */
+
+static int ispan; /* Whether inplace editing spans across files */
+
+/*
+ * Current file and line number; line numbers restart across compilation
+ * units, but span across input files. The latter is optional if editing
+ * in place.
+ */
+const char *fname; /* File name. */
+const char *outfname; /* Output file name */
+static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */
+static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */
+const char *inplace; /* Inplace edit file extension. */
+u_long linenum;
+
+static void add_compunit(enum e_cut, char *);
+static void add_file(char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c, fflag;
+ char *temp_arg;
+
+ (void) setlocale(LC_ALL, "");
+
+ fflag = 0;
+ inplace = NULL;
+
+ while ((c = getopt(argc, argv, "EI:ae:f:i:lnru")) != -1)
+ switch (c) {
+ case 'r': /* Gnu sed compat */
+ case 'E':
+ rflags = REG_EXTENDED;
+ break;
+ case 'I':
+ inplace = optarg;
+ ispan = 1; /* span across input files */
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL)
+ err(1, "malloc");
+ strcpy(temp_arg, optarg);
+ strcat(temp_arg, "\n");
+ add_compunit(CU_STRING, temp_arg);
+ break;
+ case 'f':
+ fflag = 1;
+ add_compunit(CU_FILE, optarg);
+ break;
+ case 'i':
+ inplace = optarg;
+ ispan = 0; /* don't span across input files */
+ break;
+ case 'l':
+ if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
+ warnx("setting line buffered output failed");
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'u':
+ if(setvbuf(stdout, NULL, _IONBF, 0) != 0)
+ warnx("setting unbuffered output failed");
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* First usage case; script is the first arg */
+ if (!eflag && !fflag && *argv) {
+ add_compunit(CU_STRING, *argv);
+ argv++;
+ }
+
+ compile();
+
+ /* Continue with first and start second usage */
+ if (*argv)
+ for (; *argv; argv++)
+ add_file(*argv);
+ else
+ add_file(NULL);
+ process();
+ cfclose(prog, NULL);
+ if (fclose(stdout))
+ err(1, "stdout");
+ exit(rval);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: %s script [-Ealnru] [-i extension] [file ...]\n"
+ "\t%s [-Ealnu] [-i extension] [-e script] ... [-f script_file]"
+ " ... [file ...]\n", getprogname(), getprogname());
+ exit(1);
+}
+
+/*
+ * Like fgets, but go through the chain of compilation units chaining them
+ * together. Empty strings and files are ignored.
+ */
+char *
+cu_fgets(char *buf, int n, int *more)
+{
+ static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
+ static FILE *f; /* Current open file */
+ static char *s; /* Current pointer inside string */
+ static char string_ident[30];
+ char *p;
+
+again:
+ switch (state) {
+ case ST_EOF:
+ if (script == NULL) {
+ if (more != NULL)
+ *more = 0;
+ return (NULL);
+ }
+ linenum = 0;
+ switch (script->type) {
+ case CU_FILE:
+ if ((f = fopen(script->s, "r")) == NULL)
+ err(1, "%s", script->s);
+ fname = script->s;
+ state = ST_FILE;
+ goto again;
+ case CU_STRING:
+ if (((size_t)snprintf(string_ident,
+ sizeof(string_ident), "\"%s\"", script->s)) >=
+ sizeof(string_ident) - 1)
+ (void)strcpy(string_ident +
+ sizeof(string_ident) - 6, " ...\"");
+ fname = string_ident;
+ s = script->s;
+ state = ST_STRING;
+ goto again;
+ default:
+ __builtin_unreachable();
+ }
+ case ST_FILE:
+ if ((p = fgets(buf, n, f)) != NULL) {
+ linenum++;
+ if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
+ nflag = 1;
+ if (more != NULL)
+ *more = !feof(f);
+ return (p);
+ }
+ script = script->next;
+ (void)fclose(f);
+ state = ST_EOF;
+ goto again;
+ case ST_STRING:
+ if (linenum == 0 && s[0] == '#' && s[1] == 'n')
+ nflag = 1;
+ p = buf;
+ for (;;) {
+ if (n-- <= 1) {
+ *p = '\0';
+ linenum++;
+ if (more != NULL)
+ *more = 1;
+ return (buf);
+ }
+ switch (*s) {
+ case '\0':
+ state = ST_EOF;
+ if (s == script->s) {
+ script = script->next;
+ goto again;
+ } else {
+ script = script->next;
+ *p = '\0';
+ linenum++;
+ if (more != NULL)
+ *more = 0;
+ return (buf);
+ }
+ case '\n':
+ *p++ = '\n';
+ *p = '\0';
+ s++;
+ linenum++;
+ if (more != NULL)
+ *more = 0;
+ return (buf);
+ default:
+ *p++ = *s++;
+ }
+ }
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
+
+/*
+ * Like fgets, but go through the list of files chaining them together.
+ * Set len to the length of the line.
+ */
+int
+mf_fgets(SPACE *sp, enum e_spflag spflag)
+{
+ struct stat sb;
+ ssize_t len;
+ char *dirbuf, *basebuf;
+ static char *p = NULL;
+ static size_t plen = 0;
+ int c;
+ static int firstfile;
+
+ if (infile == NULL) {
+ /* stdin? */
+ if (files->fname == NULL) {
+ if (inplace != NULL)
+ errx(1, "-I or -i may not be used with stdin");
+ infile = stdin;
+ fname = "stdin";
+ outfile = stdout;
+ outfname = "stdout";
+ }
+ firstfile = 1;
+ }
+
+ for (;;) {
+ if (infile != NULL && (c = getc(infile)) != EOF && !quit) {
+ (void)ungetc(c, infile);
+ break;
+ }
+ /* If we are here then either eof or no files are open yet */
+ if (infile == stdin) {
+ sp->len = 0;
+ return (0);
+ }
+ if (infile != NULL) {
+ fclose(infile);
+ if (*oldfname != '\0') {
+ /* if there was a backup file, remove it */
+ unlink(oldfname);
+ /*
+ * Backup the original. Note that hard links
+ * are not supported on all filesystems.
+ */
+ if ((link(fname, oldfname) != 0) &&
+ (rename(fname, oldfname) != 0)) {
+ warn("rename()");
+ if (*tmpfname)
+ unlink(tmpfname);
+ exit(1);
+ }
+ *oldfname = '\0';
+ }
+ if (*tmpfname != '\0') {
+ if (outfile != NULL && outfile != stdout)
+ if (fclose(outfile) != 0) {
+ warn("fclose()");
+ unlink(tmpfname);
+ exit(1);
+ }
+ outfile = NULL;
+ if (rename(tmpfname, fname) != 0) {
+ /* this should not happen really! */
+ warn("rename()");
+ unlink(tmpfname);
+ exit(1);
+ }
+ *tmpfname = '\0';
+ }
+ outfname = NULL;
+ }
+ if (firstfile == 0)
+ files = files->next;
+ else
+ firstfile = 0;
+ if (files == NULL) {
+ sp->len = 0;
+ return (0);
+ }
+ fname = files->fname;
+ if (inplace != NULL) {
+ if (lstat(fname, &sb) != 0)
+ err(1, "%s", fname);
+ if (!S_ISREG(sb.st_mode))
+ errx(1, "%s: %s %s", fname,
+ "in-place editing only",
+ "works for regular files");
+ if (*inplace != '\0') {
+ strlcpy(oldfname, fname,
+ sizeof(oldfname));
+ len = strlcat(oldfname, inplace,
+ sizeof(oldfname));
+ if (len > (ssize_t)sizeof(oldfname))
+ errx(1, "%s: name too long", fname);
+ }
+ if ((dirbuf = strdup(fname)) == NULL ||
+ (basebuf = strdup(fname)) == NULL)
+ err(1, "strdup");
+ len = snprintf(tmpfname, sizeof(tmpfname),
+ "%s/.!%ld!%s", dirname(dirbuf), (long)getpid(),
+ basename(basebuf));
+ free(dirbuf);
+ free(basebuf);
+ if (len >= (ssize_t)sizeof(tmpfname))
+ errx(1, "%s: name too long", fname);
+ unlink(tmpfname);
+ if (outfile != NULL && outfile != stdout)
+ fclose(outfile);
+ if ((outfile = fopen(tmpfname, "w")) == NULL)
+ err(1, "%s", fname);
+ fchown(fileno(outfile), sb.st_uid, sb.st_gid);
+ fchmod(fileno(outfile), sb.st_mode & ALLPERMS);
+ outfname = tmpfname;
+ if (!ispan) {
+ linenum = 0;
+ resetstate();
+ }
+ } else {
+ outfile = stdout;
+ outfname = "stdout";
+ }
+ if ((infile = fopen(fname, "r")) == NULL) {
+ warn("%s", fname);
+ rval = 1;
+ continue;
+ }
+ }
+ /*
+ * We are here only when infile is open and we still have something
+ * to read from it.
+ *
+ * Use getline() so that we can handle essentially infinite input
+ * data. The p and plen are static so each invocation gives
+ * getline() the same buffer which is expanded as needed.
+ */
+ len = getline(&p, &plen, infile);
+ if (len == -1)
+ err(1, "%s", fname);
+ if (len != 0 && p[len - 1] == '\n') {
+ sp->append_newline = 1;
+ len--;
+ } else if (!lastline()) {
+ sp->append_newline = 1;
+ } else {
+ sp->append_newline = 0;
+ }
+ cspace(sp, p, len, spflag);
+
+ linenum++;
+
+ return (1);
+}
+
+/*
+ * Add a compilation unit to the linked list
+ */
+static void
+add_compunit(enum e_cut type, char *s)
+{
+ struct s_compunit *cu;
+
+ if ((cu = malloc(sizeof(struct s_compunit))) == NULL)
+ err(1, "malloc");
+ cu->type = type;
+ cu->s = s;
+ cu->next = NULL;
+ *cu_nextp = cu;
+ cu_nextp = &cu->next;
+}
+
+/*
+ * Add a file to the linked list
+ */
+static void
+add_file(char *s)
+{
+ struct s_flist *fp;
+
+ if ((fp = malloc(sizeof(struct s_flist))) == NULL)
+ err(1, "malloc");
+ fp->next = NULL;
+ *fl_nextp = fp;
+ fp->fname = s;
+ fl_nextp = &fp->next;
+}
+
+static int
+next_files_have_lines(void)
+{
+ struct s_flist *file;
+ FILE *file_fd;
+ int ch;
+
+ file = files;
+ while ((file = file->next) != NULL) {
+ if ((file_fd = fopen(file->fname, "r")) == NULL)
+ continue;
+
+ if ((ch = getc(file_fd)) != EOF) {
+ /*
+ * This next file has content, therefore current
+ * file doesn't contains the last line.
+ */
+ ungetc(ch, file_fd);
+ fclose(file_fd);
+ return (1);
+ }
+
+ fclose(file_fd);
+ }
+
+ return (0);
+}
+
+int
+lastline(void)
+{
+ int ch;
+
+ if (feof(infile)) {
+ return !(
+ (inplace == NULL || ispan) &&
+ next_files_have_lines());
+ }
+ if ((ch = getc(infile)) == EOF) {
+ return !(
+ (inplace == NULL || ispan) &&
+ next_files_have_lines());
+ }
+ ungetc(ch, infile);
+ return (0);
+}
diff --git a/text_cmds/sed/misc.c b/text_cmds/sed/misc.c
new file mode 100644
index 0000000..7068f70
--- /dev/null
+++ b/text_cmds/sed/misc.c
@@ -0,0 +1,73 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "defs.h"
+#include "extern.h"
+
+/*
+ * Return a string for a regular expression error passed. This is overkill,
+ * because of the silly semantics of regerror (we can never know the size of
+ * the buffer).
+ */
+char *
+strregerror(int errcode, regex_t *preg)
+{
+ static char *oe;
+ size_t s;
+
+ if (oe != NULL)
+ free(oe);
+ s = regerror(errcode, preg, NULL, 0);
+ if ((oe = malloc(s)) == NULL)
+ err(1, "malloc");
+ (void)regerror(errcode, preg, oe, s);
+ return (oe);
+}
diff --git a/text_cmds/sed/process.c b/text_cmds/sed/process.c
new file mode 100644
index 0000000..261c623
--- /dev/null
+++ b/text_cmds/sed/process.c
@@ -0,0 +1,791 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "defs.h"
+#include "extern.h"
+
+static SPACE HS, PS, SS, YS;
+#define pd PS.deleted
+#define ps PS.space
+#define psl PS.len
+#define psanl PS.append_newline
+#define hs HS.space
+#define hsl HS.len
+
+static inline int applies(struct s_command *);
+static void do_tr(struct s_tr *);
+static void flush_appends(void);
+static void lputs(char *, size_t);
+static int regexec_e(regex_t *, const char *, int, int, size_t,
+ size_t);
+static void regsub(SPACE *, char *, char *);
+static int substitute(struct s_command *);
+
+struct s_appends *appends; /* Array of pointers to strings to append. */
+static unsigned int appendx; /* Index into appends array. */
+unsigned int appendnum; /* Size of appends array. */
+
+static int lastaddr; /* Set by applies if last address of a range. */
+static int sdone; /* If any substitutes since last line input. */
+ /* Iov structure for 'w' commands. */
+static regex_t *defpreg;
+size_t maxnsub;
+regmatch_t *match;
+
+#define OUT() do { \
+ fwrite(ps, 1, psl, outfile); \
+ if (psanl) fputc('\n', outfile); \
+} while (0)
+
+void
+process(void)
+{
+ struct s_command *cp;
+ SPACE tspace;
+ size_t oldpsl;
+ char *p;
+ int oldpsanl;
+
+ p = NULL;
+ oldpsanl = oldpsl = 0;
+
+ for (linenum = 0; mf_fgets(&PS, REPLACE);) {
+ pd = 0;
+top:
+ cp = prog;
+redirect:
+ while (cp != NULL) {
+ if (!applies(cp)) {
+ cp = cp->next;
+ continue;
+ }
+ switch (cp->code) {
+ case '{':
+ cp = cp->u.c;
+ goto redirect;
+ case 'a':
+ if (appendx >= appendnum)
+ if ((appends = realloc(appends,
+ sizeof(struct s_appends) *
+ (appendnum *= 2))) == NULL)
+ err(1, "realloc");
+ appends[appendx].type = AP_STRING;
+ appends[appendx].s = cp->t;
+ appends[appendx].len = strlen(cp->t);
+ appendx++;
+ break;
+ case 'b':
+ cp = cp->u.c;
+ goto redirect;
+ case 'c':
+ pd = 1;
+ psl = 0;
+ if (cp->a2 == NULL || lastaddr || lastline())
+ (void)fprintf(outfile, "%s", cp->t);
+ break;
+ case 'd':
+ pd = 1;
+ goto new;
+ case 'D':
+ if (pd)
+ goto new;
+ if (psl == 0 ||
+ (p = memchr(ps, '\n', psl)) == NULL) {
+ pd = 1;
+ goto new;
+ } else {
+ psl -= (p + 1) - ps;
+ memmove(ps, p + 1, psl);
+ goto top;
+ }
+ case 'g':
+ cspace(&PS, hs, hsl, REPLACE);
+ break;
+ case 'G':
+ cspace(&PS, "\n", 1, APPEND);
+ cspace(&PS, hs, hsl, APPEND);
+ break;
+ case 'h':
+ cspace(&HS, ps, psl, REPLACE);
+ break;
+ case 'H':
+ cspace(&HS, "\n", 1, APPEND);
+ cspace(&HS, ps, psl, APPEND);
+ break;
+ case 'i':
+ (void)fprintf(outfile, "%s", cp->t);
+ break;
+ case 'l':
+ lputs(ps, psl);
+ break;
+ case 'n':
+ if (!nflag && !pd)
+ OUT();
+ flush_appends();
+ if (!mf_fgets(&PS, REPLACE))
+ exit(0);
+ pd = 0;
+ break;
+ case 'N':
+ flush_appends();
+ cspace(&PS, "\n", 1, APPEND);
+ if (!mf_fgets(&PS, APPEND))
+ exit(0);
+ break;
+ case 'p':
+ if (pd)
+ break;
+ OUT();
+ break;
+ case 'P':
+ if (pd)
+ break;
+ if ((p = memchr(ps, '\n', psl)) != NULL) {
+ oldpsl = psl;
+ oldpsanl = psanl;
+ psl = p - ps;
+ psanl = 1;
+ }
+ OUT();
+ if (p != NULL) {
+ psl = oldpsl;
+ psanl = oldpsanl;
+ }
+ break;
+ case 'q':
+ if (inplace == NULL) {
+ if (!nflag && !pd)
+ OUT();
+ flush_appends();
+ exit(0);
+ }
+ quit = 1;
+ break;
+ case 'r':
+ if (appendx >= appendnum)
+ if ((appends = realloc(appends,
+ sizeof(struct s_appends) *
+ (appendnum *= 2))) == NULL)
+ err(1, "realloc");
+ appends[appendx].type = AP_FILE;
+ appends[appendx].s = cp->t;
+ appends[appendx].len = strlen(cp->t);
+ appendx++;
+ break;
+ case 's':
+ sdone |= substitute(cp);
+ break;
+ case 't':
+ if (sdone) {
+ sdone = 0;
+ cp = cp->u.c;
+ goto redirect;
+ }
+ break;
+ case 'w':
+ if (pd)
+ break;
+ if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(1, "%s", cp->t);
+ if (write(cp->u.fd, ps, psl) != (ssize_t)psl ||
+ write(cp->u.fd, "\n", 1) != 1)
+ err(1, "%s", cp->t);
+ break;
+ case 'x':
+ /*
+ * If the hold space is null, make it empty
+ * but not null. Otherwise the pattern space
+ * will become null after the swap, which is
+ * an abnormal condition.
+ */
+ if (hs == NULL)
+ cspace(&HS, "", 0, REPLACE);
+ tspace = PS;
+ PS = HS;
+ psanl = tspace.append_newline;
+ HS = tspace;
+ break;
+ case 'y':
+ if (pd || psl == 0)
+ break;
+ do_tr(cp->u.y);
+ break;
+ case ':':
+ case '}':
+ break;
+ case '=':
+ (void)fprintf(outfile, "%lu\n", linenum);
+ }
+ cp = cp->next;
+ } /* for all cp */
+
+new: if (!nflag && !pd)
+ OUT();
+ flush_appends();
+ } /* for all lines */
+}
+
+/*
+ * TRUE if the address passed matches the current program state
+ * (lastline, linenumber, ps).
+ */
+#define MATCH(a) \
+ ((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, 0, psl) : \
+ (a)->type == AT_LINE ? linenum == (a)->u.l : lastline())
+
+/*
+ * Return TRUE if the command applies to the current line. Sets the start
+ * line for process ranges. Interprets the non-select (``!'') flag.
+ */
+static inline int
+applies(struct s_command *cp)
+{
+ int r;
+
+ lastaddr = 0;
+ if (cp->a1 == NULL && cp->a2 == NULL)
+ r = 1;
+ else if (cp->a2)
+ if (cp->startline > 0) {
+ switch (cp->a2->type) {
+ case AT_RELLINE:
+ if (linenum - cp->startline <= cp->a2->u.l)
+ r = 1;
+ else {
+ cp->startline = 0;
+ r = 0;
+ }
+ break;
+ default:
+ if (MATCH(cp->a2)) {
+ cp->startline = 0;
+ lastaddr = 1;
+ r = 1;
+ } else if (cp->a2->type == AT_LINE &&
+ linenum > cp->a2->u.l) {
+ /*
+ * We missed the 2nd address due to a
+ * branch, so just close the range and
+ * return false.
+ */
+ cp->startline = 0;
+ r = 0;
+ } else
+ r = 1;
+ }
+ } else if (cp->a1 && MATCH(cp->a1)) {
+ /*
+ * If the second address is a number less than or
+ * equal to the line number first selected, only
+ * one line shall be selected.
+ * -- POSIX 1003.2
+ * Likewise if the relative second line address is zero.
+ */
+ if ((cp->a2->type == AT_LINE &&
+ linenum >= cp->a2->u.l) ||
+ (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0))
+ lastaddr = 1;
+ else {
+ cp->startline = linenum;
+ }
+ r = 1;
+ } else
+ r = 0;
+ else
+ r = MATCH(cp->a1);
+ return (cp->nonsel ? ! r : r);
+}
+
+/*
+ * Reset the sed processor to its initial state.
+ */
+void
+resetstate(void)
+{
+ struct s_command *cp;
+
+ /*
+ * Reset all in-range markers.
+ */
+ for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
+ if (cp->a2)
+ cp->startline = 0;
+
+ /*
+ * Clear out the hold space.
+ */
+ cspace(&HS, "", 0, REPLACE);
+}
+
+/*
+ * substitute --
+ * Do substitutions in the pattern space. Currently, we build a
+ * copy of the new pattern space in the substitute space structure
+ * and then swap them.
+ */
+static int
+substitute(struct s_command *cp)
+{
+ SPACE tspace;
+ regex_t *re;
+ regoff_t slen;
+ int lastempty, n;
+ regoff_t le = 0;
+ char *s;
+
+ s = ps;
+ re = cp->u.s->re;
+ if (re == NULL) {
+ if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
+ linenum = cp->u.s->linenum;
+ errx(1, "%lu: %s: \\%u not defined in the RE",
+ linenum, fname, cp->u.s->maxbref);
+ }
+ }
+ if (!regexec_e(re, ps, 0, 0, 0, psl))
+ return (0);
+
+ SS.len = 0; /* Clean substitute space. */
+ slen = psl;
+ n = cp->u.s->n;
+ lastempty = 1;
+
+ do {
+ /* Copy the leading retained string. */
+ if (n <= 1 && (match[0].rm_so > le))
+ cspace(&SS, s, match[0].rm_so - le, APPEND);
+
+ /* Skip zero-length matches right after other matches. */
+ if (lastempty || (match[0].rm_so - le) ||
+ match[0].rm_so != match[0].rm_eo) {
+ if (n <= 1) {
+ /* Want this match: append replacement. */
+ regsub(&SS, ps, cp->u.s->new);
+ if (n == 1)
+ n = -1;
+ } else {
+ /* Want a later match: append original. */
+ if (match[0].rm_eo - le)
+ cspace(&SS, s, match[0].rm_eo - le,
+ APPEND);
+ n--;
+ }
+ }
+
+ /* Move past this match. */
+ s = ps + match[0].rm_eo;
+ slen = psl - match[0].rm_eo;
+ le = match[0].rm_eo;
+
+ /*
+ * After a zero-length match, advance one byte,
+ * and at the end of the line, terminate.
+ */
+ if (match[0].rm_so == match[0].rm_eo) {
+ if (*s == '\0' || *s == '\n')
+ slen = -1;
+ else
+ slen--;
+ if (*s != '\0') {
+ cspace(&SS, s++, 1, APPEND);
+ le++;
+ }
+ lastempty = 1;
+ } else
+ lastempty = 0;
+
+ } while (n >= 0 && slen >= 0 &&
+ regexec_e(re, ps, REG_NOTBOL, 0, le, psl));
+
+ /* Did not find the requested number of matches. */
+ if (n > 0)
+ return (0);
+
+ /* Copy the trailing retained string. */
+ if (slen > 0)
+ cspace(&SS, s, slen, APPEND);
+
+ /*
+ * Swap the substitute space and the pattern space, and make sure
+ * that any leftover pointers into stdio memory get lost.
+ */
+ tspace = PS;
+ PS = SS;
+ psanl = tspace.append_newline;
+ SS = tspace;
+ SS.space = SS.back;
+
+ /* Handle the 'p' flag. */
+ if (cp->u.s->p)
+ OUT();
+
+ /* Handle the 'w' flag. */
+ if (cp->u.s->wfile && !pd) {
+ if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
+ err(1, "%s", cp->u.s->wfile);
+ if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl ||
+ write(cp->u.s->wfd, "\n", 1) != 1)
+ err(1, "%s", cp->u.s->wfile);
+ }
+ return (1);
+}
+
+/*
+ * do_tr --
+ * Perform translation ('y' command) in the pattern space.
+ */
+static void
+do_tr(struct s_tr *y)
+{
+ SPACE tmp;
+ char c, *p;
+ size_t clen, left;
+ int i;
+
+ if (MB_CUR_MAX == 1) {
+ /*
+ * Single-byte encoding: perform in-place translation
+ * of the pattern space.
+ */
+ for (p = ps; p < &ps[psl]; p++)
+ *p = y->bytetab[(u_char)*p];
+ } else {
+ /*
+ * Multi-byte encoding: perform translation into the
+ * translation space, then swap the translation and
+ * pattern spaces.
+ */
+ /* Clean translation space. */
+ YS.len = 0;
+ for (p = ps, left = psl; left > 0; p += clen, left -= clen) {
+ if ((c = y->bytetab[(u_char)*p]) != '\0') {
+ cspace(&YS, &c, 1, APPEND);
+ clen = 1;
+ continue;
+ }
+ for (i = 0; i < y->nmultis; i++)
+ if (left >= y->multis[i].fromlen &&
+ memcmp(p, y->multis[i].from,
+ y->multis[i].fromlen) == 0)
+ break;
+ if (i < y->nmultis) {
+ cspace(&YS, y->multis[i].to,
+ y->multis[i].tolen, APPEND);
+ clen = y->multis[i].fromlen;
+ } else {
+ cspace(&YS, p, 1, APPEND);
+ clen = 1;
+ }
+ }
+ /* Swap the translation space and the pattern space. */
+ tmp = PS;
+ PS = YS;
+ psanl = tmp.append_newline;
+ YS = tmp;
+ YS.space = YS.back;
+ }
+}
+
+/*
+ * Flush append requests. Always called before reading a line,
+ * therefore it also resets the substitution done (sdone) flag.
+ */
+static void
+flush_appends(void)
+{
+ FILE *f;
+ unsigned int count, idx;
+ char buf[8 * 1024];
+
+ for (idx = 0; idx < appendx; idx++)
+ switch (appends[idx].type) {
+ case AP_STRING:
+ fwrite(appends[idx].s, sizeof(char), appends[idx].len,
+ outfile);
+ break;
+ case AP_FILE:
+ /*
+ * Read files probably shouldn't be cached. Since
+ * it's not an error to read a non-existent file,
+ * it's possible that another program is interacting
+ * with the sed script through the filesystem. It
+ * would be truly bizarre, but possible. It's probably
+ * not that big a performance win, anyhow.
+ */
+ if ((f = fopen(appends[idx].s, "r")) == NULL)
+ break;
+ while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
+ (void)fwrite(buf, sizeof(char), count, outfile);
+ (void)fclose(f);
+ break;
+ }
+ if (ferror(outfile))
+ errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
+ appendx = sdone = 0;
+}
+
+static void
+lputs(char *s, size_t len)
+{
+ static const char escapes[] = "\\\a\b\f\r\t\v";
+ int c, col, width;
+ const char *p;
+ struct winsize win;
+ static int termwidth = -1;
+ size_t clen, i;
+ wchar_t wc;
+ mbstate_t mbs;
+
+ if (outfile != stdout)
+ termwidth = 60;
+ if (termwidth == -1) {
+ if ((p = getenv("COLUMNS")) && *p != '\0')
+ termwidth = atoi(p);
+ else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
+ win.ws_col > 0)
+ termwidth = win.ws_col;
+ else
+ termwidth = 60;
+ }
+ if (termwidth <= 0)
+ termwidth = 1;
+
+ memset(&mbs, 0, sizeof(mbs));
+ col = 0;
+ while (len != 0) {
+ clen = mbrtowc(&wc, s, len, &mbs);
+ if (clen == 0)
+ clen = 1;
+ if (clen == (size_t)-1 || clen == (size_t)-2) {
+ wc = (unsigned char)*s;
+ clen = 1;
+ memset(&mbs, 0, sizeof(mbs));
+ }
+ if (wc == '\n') {
+ if (col + 1 >= termwidth)
+ fprintf(outfile, "\\\n");
+ fputc('$', outfile);
+ fputc('\n', outfile);
+ col = 0;
+ } else if (iswprint(wc)) {
+ width = wcwidth(wc);
+ if (col + width >= termwidth) {
+ fprintf(outfile, "\\\n");
+ col = 0;
+ }
+ fwrite(s, 1, clen, outfile);
+ col += width;
+ } else if (wc != L'\0' && (c = wctob(wc)) != EOF &&
+ (p = strchr(escapes, c)) != NULL) {
+ if (col + 2 >= termwidth) {
+ fprintf(outfile, "\\\n");
+ col = 0;
+ }
+ fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]);
+ col += 2;
+ } else {
+ if (col + 4 * clen >= (unsigned)termwidth) {
+ fprintf(outfile, "\\\n");
+ col = 0;
+ }
+ for (i = 0; i < clen; i++)
+ fprintf(outfile, "\\%03o",
+ (int)(unsigned char)s[i]);
+ col += 4 * clen;
+ }
+ s += clen;
+ len -= clen;
+ }
+ if (col + 1 >= termwidth)
+ fprintf(outfile, "\\\n");
+ (void)fputc('$', outfile);
+ (void)fputc('\n', outfile);
+ if (ferror(outfile))
+ errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
+}
+
+static int
+regexec_e(regex_t *preg, const char *string, int eflags, int nomatch,
+ size_t start, size_t stop)
+{
+ int eval;
+
+ if (preg == NULL) {
+ if (defpreg == NULL)
+ errx(1, "first RE may not be empty");
+ } else
+ defpreg = preg;
+
+ /* Set anchors */
+ match[0].rm_so = start;
+ match[0].rm_eo = stop;
+
+ eval = regexec(defpreg, string,
+ nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
+ switch(eval) {
+ case 0:
+ return (1);
+ case REG_NOMATCH:
+ return (0);
+ }
+ errx(1, "RE error: %s", strregerror(eval, defpreg));
+ /* NOTREACHED */
+}
+
+/*
+ * regsub - perform substitutions after a regexp match
+ * Based on a routine by Henry Spencer
+ */
+static void
+regsub(SPACE *sp, char *string, char *src)
+{
+ int len, no;
+ char c, *dst;
+
+#define NEEDSP(reqlen) \
+ /* XXX What is the +1 for? */ \
+ if (sp->len + (reqlen) + 1 >= sp->blen) { \
+ sp->blen += (reqlen) + 1024; \
+ if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \
+ == NULL) \
+ err(1, "realloc"); \
+ dst = sp->space + sp->len; \
+ }
+
+ dst = sp->space + sp->len;
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '\\' && isdigit((unsigned char)*src))
+ no = *src++ - '0';
+ else
+ no = -1;
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '\\' || *src == '&'))
+ c = *src++;
+ NEEDSP(1);
+ *dst++ = c;
+ ++sp->len;
+ } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
+ len = match[no].rm_eo - match[no].rm_so;
+ NEEDSP(len);
+ memmove(dst, string + match[no].rm_so, len);
+ dst += len;
+ sp->len += len;
+ }
+ }
+ NEEDSP(1);
+ *dst = '\0';
+}
+
+/*
+ * cspace --
+ * Concatenate space: append the source space to the destination space,
+ * allocating new space as necessary.
+ */
+void
+cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
+{
+ size_t tlen;
+
+ /* Make sure SPACE has enough memory and ramp up quickly. */
+ tlen = sp->len + len + 1;
+ if (tlen > sp->blen) {
+ sp->blen = tlen + 1024;
+ if ((sp->space = sp->back = realloc(sp->back, sp->blen)) ==
+ NULL)
+ err(1, "realloc");
+ }
+
+ if (spflag == REPLACE)
+ sp->len = 0;
+
+ memmove(sp->space + sp->len, p, len);
+
+ sp->space[sp->len += len] = '\0';
+}
+
+/*
+ * Close all cached opened files and report any errors
+ */
+void
+cfclose(struct s_command *cp, struct s_command *end)
+{
+
+ for (; cp != end; cp = cp->next)
+ switch(cp->code) {
+ case 's':
+ if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
+ err(1, "%s", cp->u.s->wfile);
+ cp->u.s->wfd = -1;
+ break;
+ case 'w':
+ if (cp->u.fd != -1 && close(cp->u.fd))
+ err(1, "%s", cp->t);
+ cp->u.fd = -1;
+ break;
+ case '{':
+ cfclose(cp->u.c, cp->next);
+ break;
+ }
+}
diff --git a/text_cmds/sed/sed.1 b/text_cmds/sed/sed.1
new file mode 100644
index 0000000..3f36bd0
--- /dev/null
+++ b/text_cmds/sed/sed.1
@@ -0,0 +1,668 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sed.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2017
+.Dt SED 1
+.Os
+.Sh NAME
+.Nm sed
+.Nd stream editor
+.Sh SYNOPSIS
+.Nm
+.Op Fl Ealnru
+.Ar command
+.Op Ar
+.Nm
+.Op Fl Ealnr
+.Op Fl e Ar command
+.Op Fl f Ar command_file
+.Op Fl I Ar extension
+.Op Fl i Ar extension
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the specified files, or the standard input if no files
+are specified, modifying the input as specified by a list of commands.
+The input is then written to the standard output.
+.Pp
+A single command may be specified as the first argument to
+.Nm .
+Multiple commands may be specified by using the
+.Fl e
+or
+.Fl f
+options.
+All commands are applied to the input in the order they are specified
+regardless of their origin.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl E
+Interpret regular expressions as extended (modern) regular expressions
+rather than basic regular expressions (BRE's).
+The
+.Xr re_format 7
+manual page fully describes both formats.
+.It Fl a
+The files listed as parameters for the
+.Dq w
+functions are created (or truncated) before any processing begins,
+by default.
+The
+.Fl a
+option causes
+.Nm
+to delay opening each file until a command containing the related
+.Dq w
+function is applied to a line of input.
+.It Fl e Ar command
+Append the editing commands specified by the
+.Ar command
+argument
+to the list of commands.
+.It Fl f Ar command_file
+Append the editing commands found in the file
+.Ar command_file
+to the list of commands.
+The editing commands should each be listed on a separate line.
+.It Fl I Ar extension
+Edit files in-place, saving backups with the specified
+.Ar extension .
+If a zero-length
+.Ar extension
+is given, no backup will be saved.
+It is not recommended to give a zero-length
+.Ar extension
+when in-place editing files, as you risk corruption or partial content
+in situations where disk space is exhausted, etc.
+.Pp
+Note that in-place editing with
+.Fl I
+still takes place in a single continuous line address space covering
+all files, although each file preserves its individuality instead of
+forming one output stream.
+The line counter is never reset between files, address ranges can span
+file boundaries, and the
+.Dq $
+address matches only the last line of the last file.
+(See
+.Sx "Sed Addresses" . )
+That can lead to unexpected results in many cases of in-place editing,
+where using
+.Fl i
+is desired.
+.It Fl i Ar extension
+Edit files in-place similarly to
+.Fl I ,
+but treat each file independently from other files.
+In particular, line numbers in each file start at 1,
+the
+.Dq $
+address matches the last line of the current file,
+and address ranges are limited to the current file.
+(See
+.Sx "Sed Addresses" . )
+The net result is as though each file were edited by a separate
+.Nm
+instance.
+.It Fl l
+Make output line buffered.
+.It Fl n
+By default, each line of input is echoed to the standard output after
+all of the commands have been applied to it.
+The
+.Fl n
+option suppresses this behavior.
+.It Fl r
+Same as
+.Fl E
+for compatibility with GNU sed.
+.It Fl u
+Make output unbuffered.
+.El
+.Pp
+The form of a
+.Nm
+command is as follows:
+.Pp
+.Dl [address[,address]]function[arguments]
+.Pp
+Whitespace may be inserted before the first address and the function
+portions of the command.
+.Pp
+Normally,
+.Nm
+cyclically copies a line of input, not including its terminating newline
+character, into a
+.Em "pattern space" ,
+(unless there is something left after a
+.Dq D
+function),
+applies all of the commands with addresses that select that pattern space,
+copies the pattern space to the standard output, appending a newline, and
+deletes the pattern space.
+.Pp
+Some of the functions use a
+.Em "hold space"
+to save all or part of the pattern space for subsequent retrieval.
+.Sh "Sed Addresses"
+An address is not required, but if specified must have one of the
+following formats:
+.Bl -bullet -offset indent
+.It
+a number that counts
+input lines
+cumulatively across input files (or in each file independently
+if a
+.Fl i
+option is in effect);
+.It
+a dollar
+.Pq Dq $
+character that addresses the last line of input (or the last line
+of the current file if a
+.Fl i
+option was specified);
+.It
+a context address
+that consists of a regular expression preceded and followed by a
+delimiter. The closing delimiter can also optionally be followed by the
+.Dq I
+character, to indicate that the regular expression is to be matched
+in a case-insensitive way.
+.El
+.Pp
+A command line with no addresses selects every pattern space.
+.Pp
+A command line with one address selects all of the pattern spaces
+that match the address.
+.Pp
+A command line with two addresses selects an inclusive range.
+This
+range starts with the first pattern space that matches the first
+address.
+The end of the range is the next following pattern space
+that matches the second address.
+If the second address is a number
+less than or equal to the line number first selected, only that
+line is selected.
+The number in the second address may be prefixed with a
+.Pq Dq \&+
+to specify the number of lines to match after the first pattern.
+In the case when the second address is a context
+address,
+.Nm
+does not re-match the second address against the
+pattern space that matched the first address.
+Starting at the
+first line following the selected range,
+.Nm
+starts looking again for the first address.
+.Pp
+Editing commands can be applied to non-selected pattern spaces by use
+of the exclamation character
+.Pq Dq \&!
+function.
+.Sh "Sed Regular Expressions"
+The regular expressions used in
+.Nm ,
+by default, are basic regular expressions (BREs, see
+.Xr re_format 7
+for more information), but extended (modern) regular expressions can be used
+instead if the
+.Fl E
+flag is given.
+In addition,
+.Nm
+has the following two additions to regular expressions:
+.Pp
+.Bl -enum -compact
+.It
+In a context address, any character other than a backslash
+.Pq Dq \e
+or newline character may be used to delimit the regular expression.
+The opening delimiter needs to be preceded by a backslash
+unless it is a slash.
+For example, the context address
+.Li \exabcx
+is equivalent to
+.Li /abc/ .
+Also, putting a backslash character before the delimiting character
+within the regular expression causes the character to be treated literally.
+For example, in the context address
+.Li \exabc\exdefx ,
+the RE delimiter is an
+.Dq x
+and the second
+.Dq x
+stands for itself, so that the regular expression is
+.Dq abcxdef .
+.Pp
+.It
+The escape sequence \en matches a newline character embedded in the
+pattern space.
+You cannot, however, use a literal newline character in an address or
+in the substitute command.
+.El
+.Pp
+One special feature of
+.Nm
+regular expressions is that they can default to the last regular
+expression used.
+If a regular expression is empty, i.e., just the delimiter characters
+are specified, the last regular expression encountered is used instead.
+The last regular expression is defined as the last regular expression
+used as part of an address or substitute command, and at run-time, not
+compile-time.
+For example, the command
+.Dq /abc/s//XXX/
+will substitute
+.Dq XXX
+for the pattern
+.Dq abc .
+.Sh "Sed Functions"
+In the following list of commands, the maximum number of permissible
+addresses for each command is indicated by [0addr], [1addr], or [2addr],
+representing zero, one, or two addresses.
+.Pp
+The argument
+.Em text
+consists of one or more lines.
+To embed a newline in the text, precede it with a backslash.
+Other backslashes in text are deleted and the following character
+taken literally.
+.Pp
+The
+.Dq r
+and
+.Dq w
+functions take an optional file parameter, which should be separated
+from the function letter by white space.
+Each file given as an argument to
+.Nm
+is created (or its contents truncated) before any input processing begins.
+.Pp
+The
+.Dq b ,
+.Dq r ,
+.Dq s ,
+.Dq t ,
+.Dq w ,
+.Dq y ,
+.Dq \&! ,
+and
+.Dq \&:
+functions all accept additional arguments.
+The following synopses indicate which arguments have to be separated from
+the function letters by white space characters.
+.Pp
+Two of the functions take a function-list.
+This is a list of
+.Nm
+functions separated by newlines, as follows:
+.Bd -literal -offset indent
+{ function
+ function
+ ...
+ function
+}
+.Ed
+.Pp
+The
+.Dq {
+can be preceded by white space and can be followed by white space.
+The function can be preceded by white space.
+The terminating
+.Dq }
+must be preceded by a newline, and may also be preceded by white space.
+.Pp
+.Bl -tag -width "XXXXXX" -compact
+.It [2addr] function-list
+Execute function-list only when the pattern space is selected.
+.Pp
+.It [1addr]a\e
+.It text
+Write
+.Em text
+to standard output immediately before each attempt to read a line of input,
+whether by executing the
+.Dq N
+function or by beginning a new cycle.
+.Pp
+.It [2addr]b[label]
+Branch to the
+.Dq \&:
+function with the specified label.
+If the label is not specified, branch to the end of the script.
+.Pp
+.It [2addr]c\e
+.It text
+Delete the pattern space.
+With 0 or 1 address or at the end of a 2-address range,
+.Em text
+is written to the standard output.
+.Pp
+.It [2addr]d
+Delete the pattern space and start the next cycle.
+.Pp
+.It [2addr]D
+Delete the initial segment of the pattern space through the first
+newline character and start the next cycle.
+.Pp
+.It [2addr]g
+Replace the contents of the pattern space with the contents of the
+hold space.
+.Pp
+.It [2addr]G
+Append a newline character followed by the contents of the hold space
+to the pattern space.
+.Pp
+.It [2addr]h
+Replace the contents of the hold space with the contents of the
+pattern space.
+.Pp
+.It [2addr]H
+Append a newline character followed by the contents of the pattern space
+to the hold space.
+.Pp
+.It [1addr]i\e
+.It text
+Write
+.Em text
+to the standard output.
+.Pp
+.It [2addr]l
+(The letter ell.)
+Write the pattern space to the standard output in a visually unambiguous
+form.
+This form is as follows:
+.Pp
+.Bl -tag -width "carriage-returnXX" -offset indent -compact
+.It backslash
+\e\e
+.It alert
+\ea
+.It form-feed
+\ef
+.It carriage-return
+\er
+.It tab
+\et
+.It vertical tab
+\ev
+.El
+.Pp
+Nonprintable characters are written as three-digit octal numbers (with a
+preceding backslash) for each byte in the character (most significant byte
+first).
+Long lines are folded, with the point of folding indicated by displaying
+a backslash followed by a newline.
+The end of each line is marked with a
+.Dq $ .
+.Pp
+.It [2addr]n
+Write the pattern space to the standard output if the default output has
+not been suppressed, and replace the pattern space with the next line of
+input.
+.Pp
+.It [2addr]N
+Append the next line of input to the pattern space, using an embedded
+newline character to separate the appended material from the original
+contents.
+Note that the current line number changes.
+.Pp
+.It [2addr]p
+Write the pattern space to standard output.
+.Pp
+.It [2addr]P
+Write the pattern space, up to the first newline character to the
+standard output.
+.Pp
+.It [1addr]q
+Branch to the end of the script and quit without starting a new cycle.
+.Pp
+.It [1addr]r file
+Copy the contents of
+.Em file
+to the standard output immediately before the next attempt to read a
+line of input.
+If
+.Em file
+cannot be read for any reason, it is silently ignored and no error
+condition is set.
+.Pp
+.It [2addr]s/regular expression/replacement/flags
+Substitute the replacement string for the first instance of the regular
+expression in the pattern space.
+Any character other than backslash or newline can be used instead of
+a slash to delimit the RE and the replacement.
+Within the RE and the replacement, the RE delimiter itself can be used as
+a literal character if it is preceded by a backslash.
+.Pp
+An ampersand
+.Pq Dq &
+appearing in the replacement is replaced by the string matching the RE.
+The special meaning of
+.Dq &
+in this context can be suppressed by preceding it by a backslash.
+The string
+.Dq \e# ,
+where
+.Dq #
+is a digit, is replaced by the text matched
+by the corresponding backreference expression (see
+.Xr re_format 7 ) .
+.Pp
+A line can be split by substituting a newline character into it.
+To specify a newline character in the replacement string, precede it with
+a backslash.
+.Pp
+The value of
+.Em flags
+in the substitute function is zero or more of the following:
+.Bl -tag -width "XXXXXX" -offset indent
+.It Ar N
+Make the substitution only for the
+.Ar N Ns 'th
+occurrence of the regular expression in the pattern space.
+.It g
+Make the substitution for all non-overlapping matches of the
+regular expression, not just the first one.
+.It p
+Write the pattern space to standard output if a replacement was made.
+If the replacement string is identical to that which it replaces, it
+is still considered to have been a replacement.
+.It w Em file
+Append the pattern space to
+.Em file
+if a replacement was made.
+If the replacement string is identical to that which it replaces, it
+is still considered to have been a replacement.
+.It i or I
+Match the regular expression in a case-insensitive way.
+.El
+.Pp
+.It [2addr]t [label]
+Branch to the
+.Dq \&:
+function bearing the label if any substitutions have been made since the
+most recent reading of an input line or execution of a
+.Dq t
+function.
+If no label is specified, branch to the end of the script.
+.Pp
+.It [2addr]w Em file
+Append the pattern space to the
+.Em file .
+.Pp
+.It [2addr]x
+Swap the contents of the pattern and hold spaces.
+.Pp
+.It [2addr]y/string1/string2/
+Replace all occurrences of characters in
+.Em string1
+in the pattern space with the corresponding characters from
+.Em string2 .
+Any character other than a backslash or newline can be used instead of
+a slash to delimit the strings.
+Within
+.Em string1
+and
+.Em string2 ,
+a backslash followed by any character other than a newline is that literal
+character, and a backslash followed by an ``n'' is replaced by a newline
+character.
+.Pp
+.It [2addr]!function
+.It [2addr]!function-list
+Apply the function or function-list only to the lines that are
+.Em not
+selected by the address(es).
+.Pp
+.It [0addr]:label
+This function does nothing; it bears a label to which the
+.Dq b
+and
+.Dq t
+commands may branch.
+.Pp
+.It [1addr]=
+Write the line number to the standard output followed by a newline
+character.
+.Pp
+.It [0addr]
+Empty lines are ignored.
+.Pp
+.It [0addr]#
+The
+.Dq #
+and the remainder of the line are ignored (treated as a comment), with
+the single exception that if the first two characters in the file are
+.Dq #n ,
+the default output is suppressed.
+This is the same as specifying the
+.Fl n
+option on the command line.
+.El
+.Sh ENVIRONMENT
+The
+.Ev COLUMNS , LANG , LC_ALL , LC_CTYPE
+and
+.Ev LC_COLLATE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Replace
+.Ql bar
+with
+.Ql baz
+when piped from another command:
+.Bd -literal -offset indent
+echo "An alternate word, like bar, is sometimes used in examples." | sed 's/bar/baz/'
+.Ed
+.Pp
+Using backlashes can sometimes be hard to read and follow:
+.Bd -literal -offset indent
+echo "/home/example" | sed 's/\\/home\\/example/\\/usr\\/local\\/example/'
+.Ed
+.Pp
+Using a different separator can be handy when working with paths:
+.Bd -literal -offset indent
+echo "/home/example" | sed 's#/home/example#/usr/local/example#'
+.Ed
+.Pp
+Replace all occurances of
+.Ql foo
+with
+.Ql bar
+in the file
+.Pa test.txt ,
+without creating a backup of the file:
+.Bd -literal -offset indent
+sed -i '' -e 's/foo/bar/g' test.txt
+.Ed
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr ed 1 ,
+.Xr grep 1 ,
+.Xr regex 3 ,
+.Xr re_format 7
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2
+specification.
+.Pp
+The
+.Fl E , I , a
+and
+.Fl i
+options, the prefixing
+.Dq \&+
+in the second member of an address range,
+as well as the
+.Dq I
+flag to the address regular expression and substitution command are
+non-standard
+.Fx
+extensions and may not be available on other operating systems.
+.Sh HISTORY
+A
+.Nm
+command, written by
+.An L. E. McMahon ,
+appeared in
+.At v7 .
+.Sh AUTHORS
+.An Diomidis D. Spinellis Aq Mt dds@FreeBSD.org
+.Sh BUGS
+Multibyte characters containing a byte with value 0x5C
+.Tn ( ASCII
+.Ql \e )
+may be incorrectly treated as line continuation characters in arguments to the
+.Dq a ,
+.Dq c
+and
+.Dq i
+commands.
+Multibyte characters cannot be used as delimiters with the
+.Dq s
+and
+.Dq y
+commands.
diff --git a/text_cmds/sort/bwstring.c b/text_cmds/sort/bwstring.c
new file mode 100644
index 0000000..3b7b233
--- /dev/null
+++ b/text_cmds/sort/bwstring.c
@@ -0,0 +1,1142 @@
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+#include <langinfo.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "bwstring.h"
+#include "sort.h"
+
+bool byte_sort;
+
+static wchar_t **wmonths;
+static unsigned char **cmonths;
+
+/* initialise months */
+
+void
+initialise_months(void)
+{
+ const nl_item item[12] = { ABMON_1, ABMON_2, ABMON_3, ABMON_4,
+ ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10,
+ ABMON_11, ABMON_12 };
+ unsigned char *tmp;
+ size_t len;
+
+ if (MB_CUR_MAX == 1) {
+ if (cmonths == NULL) {
+ unsigned char *m;
+
+ cmonths = sort_malloc(sizeof(unsigned char*) * 12);
+ for (int i = 0; i < 12; i++) {
+ cmonths[i] = NULL;
+ tmp = (unsigned char *) nl_langinfo(item[i]);
+ if (debug_sort)
+ printf("month[%d]=%s\n", i, tmp);
+ if (*tmp == '\0')
+ continue;
+ m = sort_strdup(tmp);
+ len = strlen(tmp);
+ for (unsigned int j = 0; j < len; j++)
+ m[j] = toupper(m[j]);
+ cmonths[i] = m;
+ }
+ }
+
+ } else {
+ if (wmonths == NULL) {
+ wchar_t *m;
+
+ wmonths = sort_malloc(sizeof(wchar_t *) * 12);
+ for (int i = 0; i < 12; i++) {
+ wmonths[i] = NULL;
+ tmp = (unsigned char *) nl_langinfo(item[i]);
+ if (debug_sort)
+ printf("month[%d]=%s\n", i, tmp);
+ if (*tmp == '\0')
+ continue;
+ len = strlen(tmp);
+ m = sort_malloc(SIZEOF_WCHAR_STRING(len + 1));
+ if (mbstowcs(m, (char*)tmp, len) ==
+ ((size_t) - 1)) {
+ sort_free(m);
+ continue;
+ }
+ m[len] = L'\0';
+ for (unsigned int j = 0; j < len; j++)
+ m[j] = towupper(m[j]);
+ wmonths[i] = m;
+ }
+ }
+ }
+}
+
+/*
+ * Compare two wide-character strings
+ */
+static int
+wide_str_coll(const wchar_t *s1, const wchar_t *s2)
+{
+ int ret = 0;
+
+ errno = 0;
+ ret = wcscoll(s1, s2);
+ if (errno == EILSEQ) {
+ errno = 0;
+ ret = wcscmp(s1, s2);
+ if (errno != 0) {
+ for (size_t i = 0; ; ++i) {
+ wchar_t c1 = s1[i];
+ wchar_t c2 = s2[i];
+ if (c1 == L'\0')
+ return ((c2 == L'\0') ? 0 : -1);
+ if (c2 == L'\0')
+ return (+1);
+ if (c1 == c2)
+ continue;
+ return ((int)(c1 - c2));
+ }
+ }
+ }
+ return (ret);
+}
+
+/* counterparts of wcs functions */
+
+void
+bwsprintf(FILE *f, struct bwstring *bws, const char *prefix, const char *suffix)
+{
+
+ if (MB_CUR_MAX == 1)
+ fprintf(f, "%s%s%s", prefix, bws->data.cstr, suffix);
+ else
+ fprintf(f, "%s%S%s", prefix, bws->data.wstr, suffix);
+}
+
+const void* bwsrawdata(const struct bwstring *bws)
+{
+
+ return (&(bws->data));
+}
+
+size_t bwsrawlen(const struct bwstring *bws)
+{
+
+ return ((MB_CUR_MAX == 1) ? bws->len : SIZEOF_WCHAR_STRING(bws->len));
+}
+
+size_t
+bws_memsize(const struct bwstring *bws)
+{
+
+ return ((MB_CUR_MAX == 1) ? (bws->len + 2 + sizeof(struct bwstring)) :
+ (SIZEOF_WCHAR_STRING(bws->len + 1) + sizeof(struct bwstring)));
+}
+
+void
+bws_setlen(struct bwstring *bws, size_t newlen)
+{
+
+ if (bws && newlen != bws->len && newlen <= bws->len) {
+ bws->len = newlen;
+ if (MB_CUR_MAX == 1)
+ bws->data.cstr[newlen] = '\0';
+ else
+ bws->data.wstr[newlen] = L'\0';
+ }
+}
+
+/*
+ * Allocate a new binary string of specified size
+ */
+struct bwstring *
+bwsalloc(size_t sz)
+{
+ struct bwstring *ret;
+
+ if (MB_CUR_MAX == 1)
+ ret = sort_malloc(sizeof(struct bwstring) + 1 + sz);
+ else
+ ret = sort_malloc(sizeof(struct bwstring) +
+ SIZEOF_WCHAR_STRING(sz + 1));
+ ret->len = sz;
+
+ if (MB_CUR_MAX == 1)
+ ret->data.cstr[ret->len] = '\0';
+ else
+ ret->data.wstr[ret->len] = L'\0';
+
+ return (ret);
+}
+
+/*
+ * Create a copy of binary string.
+ * New string size equals the length of the old string.
+ */
+struct bwstring *
+bwsdup(const struct bwstring *s)
+{
+
+ if (s == NULL)
+ return (NULL);
+ else {
+ struct bwstring *ret = bwsalloc(s->len);
+
+ if (MB_CUR_MAX == 1)
+ memcpy(ret->data.cstr, s->data.cstr, (s->len));
+ else
+ memcpy(ret->data.wstr, s->data.wstr,
+ SIZEOF_WCHAR_STRING(s->len));
+
+ return (ret);
+ }
+}
+
+/*
+ * Create a new binary string from a wide character buffer.
+ */
+struct bwstring *
+bwssbdup(const wchar_t *str, size_t len)
+{
+
+ if (str == NULL)
+ return ((len == 0) ? bwsalloc(0) : NULL);
+ else {
+ struct bwstring *ret;
+
+ ret = bwsalloc(len);
+
+ if (MB_CUR_MAX == 1)
+ for (size_t i = 0; i < len; ++i)
+ ret->data.cstr[i] = (unsigned char) str[i];
+ else
+ memcpy(ret->data.wstr, str, SIZEOF_WCHAR_STRING(len));
+
+ return (ret);
+ }
+}
+
+/*
+ * Create a new binary string from a raw binary buffer.
+ */
+struct bwstring *
+bwscsbdup(const unsigned char *str, size_t len)
+{
+ struct bwstring *ret;
+
+ ret = bwsalloc(len);
+
+ if (str) {
+ if (MB_CUR_MAX == 1)
+ memcpy(ret->data.cstr, str, len);
+ else {
+ mbstate_t mbs;
+ const char *s;
+ size_t charlen, chars, cptr;
+
+ chars = 0;
+ cptr = 0;
+ s = (const char *) str;
+
+ memset(&mbs, 0, sizeof(mbs));
+
+ while (cptr < len) {
+ size_t n = MB_CUR_MAX;
+
+ if (n > len - cptr)
+ n = len - cptr;
+ charlen = mbrlen(s + cptr, n, &mbs);
+ switch (charlen) {
+ case 0:
+ /* FALLTHROUGH */
+ case (size_t) -1:
+ /* FALLTHROUGH */
+ case (size_t) -2:
+ ret->data.wstr[chars++] =
+ (unsigned char) s[cptr];
+ ++cptr;
+ break;
+ default:
+ n = mbrtowc(ret->data.wstr + (chars++),
+ s + cptr, charlen, &mbs);
+ if ((n == (size_t)-1) || (n == (size_t)-2))
+ /* NOTREACHED */
+ err(2, "mbrtowc error");
+ cptr += charlen;
+ }
+ }
+
+ ret->len = chars;
+ ret->data.wstr[ret->len] = L'\0';
+ }
+ }
+ return (ret);
+}
+
+/*
+ * De-allocate object memory
+ */
+void
+bwsfree(const struct bwstring *s)
+{
+
+ if (s)
+ sort_free(s);
+}
+
+/*
+ * Copy content of src binary string to dst.
+ * If the capacity of the dst string is not sufficient,
+ * then the data is truncated.
+ */
+size_t
+bwscpy(struct bwstring *dst, const struct bwstring *src)
+{
+ size_t nums = src->len;
+
+ if (nums > dst->len)
+ nums = dst->len;
+ dst->len = nums;
+
+ if (MB_CUR_MAX == 1) {
+ memcpy(dst->data.cstr, src->data.cstr, nums);
+ dst->data.cstr[dst->len] = '\0';
+ } else {
+ memcpy(dst->data.wstr, src->data.wstr,
+ SIZEOF_WCHAR_STRING(nums + 1));
+ dst->data.wstr[dst->len] = L'\0';
+ }
+
+ return (nums);
+}
+
+/*
+ * Copy content of src binary string to dst,
+ * with specified number of symbols to be copied.
+ * If the capacity of the dst string is not sufficient,
+ * then the data is truncated.
+ */
+struct bwstring *
+bwsncpy(struct bwstring *dst, const struct bwstring *src, size_t size)
+{
+ size_t nums = src->len;
+
+ if (nums > dst->len)
+ nums = dst->len;
+ if (nums > size)
+ nums = size;
+ dst->len = nums;
+
+ if (MB_CUR_MAX == 1) {
+ memcpy(dst->data.cstr, src->data.cstr, nums);
+ dst->data.cstr[dst->len] = '\0';
+ } else {
+ memcpy(dst->data.wstr, src->data.wstr,
+ SIZEOF_WCHAR_STRING(nums + 1));
+ dst->data.wstr[dst->len] = L'\0';
+ }
+
+ return (dst);
+}
+
+/*
+ * Copy content of src binary string to dst,
+ * with specified number of symbols to be copied.
+ * An offset value can be specified, from the start of src string.
+ * If the capacity of the dst string is not sufficient,
+ * then the data is truncated.
+ */
+struct bwstring *
+bwsnocpy(struct bwstring *dst, const struct bwstring *src, size_t offset,
+ size_t size)
+{
+
+ if (offset >= src->len) {
+ dst->data.wstr[0] = 0;
+ dst->len = 0;
+ } else {
+ size_t nums = src->len - offset;
+
+ if (nums > dst->len)
+ nums = dst->len;
+ if (nums > size)
+ nums = size;
+ dst->len = nums;
+ if (MB_CUR_MAX == 1) {
+ memcpy(dst->data.cstr, src->data.cstr + offset,
+ (nums));
+ dst->data.cstr[dst->len] = '\0';
+ } else {
+ memcpy(dst->data.wstr, src->data.wstr + offset,
+ SIZEOF_WCHAR_STRING(nums));
+ dst->data.wstr[dst->len] = L'\0';
+ }
+ }
+ return (dst);
+}
+
+/*
+ * Write binary string to the file.
+ * The output is ended either with '\n' (nl == true)
+ * or '\0' (nl == false).
+ */
+size_t
+bwsfwrite(struct bwstring *bws, FILE *f, bool zero_ended)
+{
+
+ if (MB_CUR_MAX == 1) {
+ size_t len = bws->len;
+
+ if (!zero_ended) {
+ bws->data.cstr[len] = '\n';
+
+ if (fwrite(bws->data.cstr, len + 1, 1, f) < 1)
+ err(2, NULL);
+
+ bws->data.cstr[len] = '\0';
+ } else if (fwrite(bws->data.cstr, len + 1, 1, f) < 1)
+ err(2, NULL);
+
+ return (len + 1);
+
+ } else {
+ wchar_t eols;
+ size_t printed = 0;
+
+ eols = zero_ended ? btowc('\0') : btowc('\n');
+
+ while (printed < BWSLEN(bws)) {
+ const wchar_t *s = bws->data.wstr + printed;
+
+ if (*s == L'\0') {
+ int nums;
+
+ nums = fwprintf(f, L"%lc", *s);
+
+ if (nums != 1)
+ err(2, NULL);
+ ++printed;
+ } else {
+ int nums;
+
+ nums = fwprintf(f, L"%ls", s);
+
+ if (nums < 1)
+ err(2, NULL);
+ printed += nums;
+ }
+ }
+ fwprintf(f, L"%lc", eols);
+ return (printed + 1);
+ }
+}
+
+/*
+ * Allocate and read a binary string from file.
+ * The strings are nl-ended or zero-ended, depending on the sort setting.
+ */
+struct bwstring *
+bwsfgetln(FILE *f, size_t *len, bool zero_ended, struct reader_buffer *rb)
+{
+ wint_t eols;
+
+ eols = zero_ended ? btowc('\0') : btowc('\n');
+
+ if (!zero_ended && (MB_CUR_MAX > 1)) {
+ wchar_t *ret;
+
+ ret = fgetwln(f, len);
+
+ if (ret == NULL) {
+ if (!feof(f))
+ err(2, NULL);
+ return (NULL);
+ }
+ if (*len > 0) {
+ if (ret[*len - 1] == (wchar_t)eols)
+ --(*len);
+ }
+ return (bwssbdup(ret, *len));
+
+ } else if (!zero_ended && (MB_CUR_MAX == 1)) {
+ char *ret;
+
+ ret = fgetln(f, len);
+
+ if (ret == NULL) {
+ if (!feof(f))
+ err(2, NULL);
+ return (NULL);
+ }
+ if (*len > 0) {
+ if (ret[*len - 1] == '\n')
+ --(*len);
+ }
+ return (bwscsbdup((unsigned char*)ret, *len));
+
+ } else {
+ *len = 0;
+
+ if (feof(f))
+ return (NULL);
+
+ if (2 >= rb->fgetwln_z_buffer_size) {
+ rb->fgetwln_z_buffer_size += 256;
+ rb->fgetwln_z_buffer = sort_realloc(rb->fgetwln_z_buffer,
+ sizeof(wchar_t) * rb->fgetwln_z_buffer_size);
+ }
+ rb->fgetwln_z_buffer[*len] = 0;
+
+ if (MB_CUR_MAX == 1)
+ while (!feof(f)) {
+ int c;
+
+ c = fgetc(f);
+
+ if (c == EOF) {
+ if (*len == 0)
+ return (NULL);
+ goto line_read_done;
+ }
+ if (c == eols)
+ goto line_read_done;
+
+ if (*len + 1 >= rb->fgetwln_z_buffer_size) {
+ rb->fgetwln_z_buffer_size += 256;
+ rb->fgetwln_z_buffer = sort_realloc(rb->fgetwln_z_buffer,
+ SIZEOF_WCHAR_STRING(rb->fgetwln_z_buffer_size));
+ }
+
+ rb->fgetwln_z_buffer[*len] = c;
+ rb->fgetwln_z_buffer[++(*len)] = 0;
+ }
+ else
+ while (!feof(f)) {
+ wint_t c = 0;
+
+ c = fgetwc(f);
+
+ if (c == WEOF) {
+ if (*len == 0)
+ return (NULL);
+ goto line_read_done;
+ }
+ if (c == eols)
+ goto line_read_done;
+
+ if (*len + 1 >= rb->fgetwln_z_buffer_size) {
+ rb->fgetwln_z_buffer_size += 256;
+ rb->fgetwln_z_buffer = sort_realloc(rb->fgetwln_z_buffer,
+ SIZEOF_WCHAR_STRING(rb->fgetwln_z_buffer_size));
+ }
+
+ rb->fgetwln_z_buffer[*len] = c;
+ rb->fgetwln_z_buffer[++(*len)] = 0;
+ }
+
+line_read_done:
+ /* we do not count the last 0 */
+ return (bwssbdup(rb->fgetwln_z_buffer, *len));
+ }
+}
+
+int
+bwsncmp(const struct bwstring *bws1, const struct bwstring *bws2,
+ size_t offset, size_t len)
+{
+ size_t cmp_len, len1, len2;
+ int res = 0;
+
+ len1 = bws1->len;
+ len2 = bws2->len;
+
+ if (len1 <= offset) {
+ return ((len2 <= offset) ? 0 : -1);
+ } else {
+ if (len2 <= offset)
+ return (+1);
+ else {
+ len1 -= offset;
+ len2 -= offset;
+
+ cmp_len = len1;
+
+ if (len2 < cmp_len)
+ cmp_len = len2;
+
+ if (len < cmp_len)
+ cmp_len = len;
+
+ if (MB_CUR_MAX == 1) {
+ const unsigned char *s1, *s2;
+
+ s1 = bws1->data.cstr + offset;
+ s2 = bws2->data.cstr + offset;
+
+ res = memcmp(s1, s2, cmp_len);
+
+ } else {
+ const wchar_t *s1, *s2;
+
+ s1 = bws1->data.wstr + offset;
+ s2 = bws2->data.wstr + offset;
+
+ res = memcmp(s1, s2, SIZEOF_WCHAR_STRING(cmp_len));
+ }
+ }
+ }
+
+ if (res == 0) {
+ if (len1 < cmp_len && len1 < len2)
+ res = -1;
+ else if (len2 < cmp_len && len2 < len1)
+ res = +1;
+ }
+
+ return (res);
+}
+
+int
+bwscmp(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset)
+{
+ size_t len1, len2, cmp_len;
+ int res;
+
+ len1 = bws1->len;
+ len2 = bws2->len;
+
+ len1 -= offset;
+ len2 -= offset;
+
+ cmp_len = len1;
+
+ if (len2 < cmp_len)
+ cmp_len = len2;
+
+ res = bwsncmp(bws1, bws2, offset, cmp_len);
+
+ if (res == 0) {
+ if( len1 < len2)
+ res = -1;
+ else if (len2 < len1)
+ res = +1;
+ }
+
+ return (res);
+}
+
+int
+bws_iterator_cmp(bwstring_iterator iter1, bwstring_iterator iter2, size_t len)
+{
+ wchar_t c1, c2;
+ size_t i = 0;
+
+ for (i = 0; i < len; ++i) {
+ c1 = bws_get_iter_value(iter1);
+ c2 = bws_get_iter_value(iter2);
+ if (c1 != c2)
+ return (c1 - c2);
+ iter1 = bws_iterator_inc(iter1, 1);
+ iter2 = bws_iterator_inc(iter2, 1);
+ }
+
+ return (0);
+}
+
+int
+bwscoll(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset)
+{
+ size_t len1, len2;
+
+ len1 = bws1->len;
+ len2 = bws2->len;
+
+ if (len1 <= offset)
+ return ((len2 <= offset) ? 0 : -1);
+ else {
+ if (len2 <= offset)
+ return (+1);
+ else {
+ len1 -= offset;
+ len2 -= offset;
+
+ if (MB_CUR_MAX == 1) {
+ const unsigned char *s1, *s2;
+
+ s1 = bws1->data.cstr + offset;
+ s2 = bws2->data.cstr + offset;
+
+ if (byte_sort) {
+ int res = 0;
+
+ if (len1 > len2) {
+ res = memcmp(s1, s2, len2);
+ if (!res)
+ res = +1;
+ } else if (len1 < len2) {
+ res = memcmp(s1, s2, len1);
+ if (!res)
+ res = -1;
+ } else
+ res = memcmp(s1, s2, len1);
+
+ return (res);
+
+ } else {
+ int res = 0;
+ size_t i, maxlen;
+
+ i = 0;
+ maxlen = len1;
+
+ if (maxlen > len2)
+ maxlen = len2;
+
+ while (i < maxlen) {
+ /* goto next non-zero part: */
+ while ((i < maxlen) &&
+ !s1[i] && !s2[i])
+ ++i;
+
+ if (i >= maxlen)
+ break;
+
+ if (s1[i] == 0) {
+ if (s2[i] == 0)
+ /* NOTREACHED */
+ err(2, "bwscoll error 01");
+ else
+ return (-1);
+ } else if (s2[i] == 0)
+ return (+1);
+
+ res = strcoll((const char*)(s1 + i), (const char*)(s2 + i));
+ if (res)
+ return (res);
+
+ while ((i < maxlen) &&
+ s1[i] && s2[i])
+ ++i;
+
+ if (i >= maxlen)
+ break;
+
+ if (s1[i] == 0) {
+ if (s2[i] == 0) {
+ ++i;
+ continue;
+ } else
+ return (-1);
+ } else if (s2[i] == 0)
+ return (+1);
+ else
+ /* NOTREACHED */
+ err(2, "bwscoll error 02");
+ }
+
+ if (len1 < len2)
+ return (-1);
+ else if (len1 > len2)
+ return (+1);
+
+ return (0);
+ }
+ } else {
+ const wchar_t *s1, *s2;
+ size_t i, maxlen;
+ int res = 0;
+
+ s1 = bws1->data.wstr + offset;
+ s2 = bws2->data.wstr + offset;
+
+ i = 0;
+ maxlen = len1;
+
+ if (maxlen > len2)
+ maxlen = len2;
+
+ while (i < maxlen) {
+
+ /* goto next non-zero part: */
+ while ((i < maxlen) &&
+ !s1[i] && !s2[i])
+ ++i;
+
+ if (i >= maxlen)
+ break;
+
+ if (s1[i] == 0) {
+ if (s2[i] == 0)
+ /* NOTREACHED */
+ err(2, "bwscoll error 1");
+ else
+ return (-1);
+ } else if (s2[i] == 0)
+ return (+1);
+
+ res = wide_str_coll(s1 + i, s2 + i);
+ if (res)
+ return (res);
+
+ while ((i < maxlen) && s1[i] && s2[i])
+ ++i;
+
+ if (i >= maxlen)
+ break;
+
+ if (s1[i] == 0) {
+ if (s2[i] == 0) {
+ ++i;
+ continue;
+ } else
+ return (-1);
+ } else if (s2[i] == 0)
+ return (+1);
+ else
+ /* NOTREACHED */
+ err(2, "bwscoll error 2");
+ }
+
+ if (len1 < len2)
+ return (-1);
+ else if (len1 > len2)
+ return (+1);
+
+ return (0);
+ }
+ }
+ }
+}
+
+/*
+ * Correction of the system API
+ */
+double
+bwstod(struct bwstring *s0, bool *empty)
+{
+ double ret = 0;
+
+ if (MB_CUR_MAX == 1) {
+ unsigned char *end, *s;
+ char *ep;
+
+ s = s0->data.cstr;
+ end = s + s0->len;
+ ep = NULL;
+
+ while (isblank_f(*s) && s < end)
+ ++s;
+
+ if (!isprint(*s)) {
+ *empty = true;
+ return (0);
+ }
+
+ ret = strtod((char*)s, &ep);
+ if ((unsigned char*) ep == s) {
+ *empty = true;
+ return (0);
+ }
+ } else {
+ wchar_t *end, *ep, *s;
+
+ s = s0->data.wstr;
+ end = s + s0->len;
+ ep = NULL;
+
+ while (iswblank_f(*s) && s < end)
+ ++s;
+
+ if (!iswprint(*s)) {
+ *empty = true;
+ return (0);
+ }
+
+ ret = wcstod(s, &ep);
+ if (ep == s) {
+ *empty = true;
+ return (0);
+ }
+ }
+
+ *empty = false;
+ return (ret);
+}
+
+/*
+ * A helper function for monthcoll. If a line matches
+ * a month name, it returns (number of the month - 1),
+ * while if there is no match, it just return -1.
+ */
+
+int
+bws_month_score(const struct bwstring *s0)
+{
+
+ if (MB_CUR_MAX == 1) {
+ const unsigned char *end, *s;
+
+ s = s0->data.cstr;
+ end = s + s0->len;
+
+ while (isblank_f(*s) && s < end)
+ ++s;
+
+ for (int i = 11; i >= 0; --i) {
+ if (cmonths[i] &&
+ (s == (unsigned char*)strstr((const char*)s, (char*)(cmonths[i]))))
+ return (i);
+ }
+
+ } else {
+ const wchar_t *end, *s;
+
+ s = s0->data.wstr;
+ end = s + s0->len;
+
+ while (iswblank_f(*s) && s < end)
+ ++s;
+
+ for (int i = 11; i >= 0; --i) {
+ if (wmonths[i] && (s == wcsstr(s, wmonths[i])))
+ return (i);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * Rips out leading blanks (-b).
+ */
+struct bwstring *
+ignore_leading_blanks(struct bwstring *str)
+{
+
+ if (MB_CUR_MAX == 1) {
+ unsigned char *dst, *end, *src;
+
+ src = str->data.cstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end && isblank_f(*src))
+ ++src;
+
+ if (src != dst) {
+ size_t newlen;
+
+ newlen = BWSLEN(str) - (src - dst);
+
+ while (src < end) {
+ *dst = *src;
+ ++dst;
+ ++src;
+ }
+ bws_setlen(str, newlen);
+ }
+ } else {
+ wchar_t *dst, *end, *src;
+
+ src = str->data.wstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end && iswblank_f(*src))
+ ++src;
+
+ if (src != dst) {
+
+ size_t newlen = BWSLEN(str) - (src - dst);
+
+ while (src < end) {
+ *dst = *src;
+ ++dst;
+ ++src;
+ }
+ bws_setlen(str, newlen);
+
+ }
+ }
+ return (str);
+}
+
+/*
+ * Rips out nonprinting characters (-i).
+ */
+struct bwstring *
+ignore_nonprinting(struct bwstring *str)
+{
+ size_t newlen = str->len;
+
+ if (MB_CUR_MAX == 1) {
+ unsigned char *dst, *end, *src;
+ unsigned char c;
+
+ src = str->data.cstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end) {
+ c = *src;
+ if (isprint(c)) {
+ *dst = c;
+ ++dst;
+ ++src;
+ } else {
+ ++src;
+ --newlen;
+ }
+ }
+ } else {
+ wchar_t *dst, *end, *src;
+ wchar_t c;
+
+ src = str->data.wstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end) {
+ c = *src;
+ if (iswprint(c)) {
+ *dst = c;
+ ++dst;
+ ++src;
+ } else {
+ ++src;
+ --newlen;
+ }
+ }
+ }
+ bws_setlen(str, newlen);
+
+ return (str);
+}
+
+/*
+ * Rips out any characters that are not alphanumeric characters
+ * nor blanks (-d).
+ */
+struct bwstring *
+dictionary_order(struct bwstring *str)
+{
+ size_t newlen = str->len;
+
+ if (MB_CUR_MAX == 1) {
+ unsigned char *dst, *end, *src;
+ unsigned char c;
+
+ src = str->data.cstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end) {
+ c = *src;
+ if (isalnum(c) || isblank_f(c)) {
+ *dst = c;
+ ++dst;
+ ++src;
+ } else {
+ ++src;
+ --newlen;
+ }
+ }
+ } else {
+ wchar_t *dst, *end, *src;
+ wchar_t c;
+
+ src = str->data.wstr;
+ dst = src;
+ end = src + str->len;
+
+ while (src < end) {
+ c = *src;
+ if (iswalnum(c) || iswblank_f(c)) {
+ *dst = c;
+ ++dst;
+ ++src;
+ } else {
+ ++src;
+ --newlen;
+ }
+ }
+ }
+ bws_setlen(str, newlen);
+
+ return (str);
+}
+
+/*
+ * Converts string to lower case(-f).
+ */
+struct bwstring *
+ignore_case(struct bwstring *str)
+{
+
+ if (MB_CUR_MAX == 1) {
+ unsigned char *end, *s;
+
+ s = str->data.cstr;
+ end = s + str->len;
+
+ while (s < end) {
+ *s = toupper(*s);
+ ++s;
+ }
+ } else {
+ wchar_t *end, *s;
+
+ s = str->data.wstr;
+ end = s + str->len;
+
+ while (s < end) {
+ *s = towupper(*s);
+ ++s;
+ }
+ }
+ return (str);
+}
+
+void
+bws_disorder_warnx(struct bwstring *s, const char *fn, size_t pos)
+{
+
+ if (MB_CUR_MAX == 1)
+ warnx("%s:%zu: disorder: %s", fn, pos + 1, s->data.cstr);
+ else
+ warnx("%s:%zu: disorder: %ls", fn, pos + 1, s->data.wstr);
+}
diff --git a/text_cmds/sort/bwstring.h b/text_cmds/sort/bwstring.h
new file mode 100644
index 0000000..857de13
--- /dev/null
+++ b/text_cmds/sort/bwstring.h
@@ -0,0 +1,142 @@
+/* $FreeBSD: head/usr.bin/sort/bwstring.h 264744 2014-04-21 22:52:18Z pfg $ */
+
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__BWSTRING_H__)
+#define __BWSTRING_H__
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sysexits.h>
+#include <wchar.h>
+
+#include "mem.h"
+
+extern bool byte_sort;
+
+/* wchar_t is of 4 bytes: */
+#define SIZEOF_WCHAR_STRING(LEN) ((LEN)*sizeof(wchar_t))
+
+/*
+ * Binary "wide" string
+ */
+struct bwstring
+{
+ size_t len;
+ union
+ {
+ wchar_t wstr[0];
+ unsigned char cstr[0];
+ } data;
+};
+
+struct reader_buffer
+{
+ wchar_t *fgetwln_z_buffer;
+ size_t fgetwln_z_buffer_size;
+};
+
+typedef void *bwstring_iterator;
+
+#define BWSLEN(s) ((s)->len)
+
+struct bwstring *bwsalloc(size_t sz);
+
+size_t bwsrawlen(const struct bwstring *bws);
+const void* bwsrawdata(const struct bwstring *bws);
+void bws_setlen(struct bwstring *bws, size_t newlen);
+size_t bws_memsize(const struct bwstring *bws);
+double bwstod(struct bwstring *s0, bool *empty);
+int bws_month_score(const struct bwstring *s0);
+
+struct bwstring *ignore_leading_blanks(struct bwstring *str);
+struct bwstring *ignore_nonprinting(struct bwstring *str);
+struct bwstring *dictionary_order(struct bwstring *str);
+struct bwstring *ignore_case(struct bwstring *str);
+
+void bwsprintf(FILE*, struct bwstring*, const char *prefix, const char *suffix);
+void bws_disorder_warnx(struct bwstring *s, const char *fn, size_t pos);
+
+struct bwstring *bwsdup(const struct bwstring *s);
+struct bwstring *bwssbdup(const wchar_t *str, size_t size);
+struct bwstring *bwscsbdup(const unsigned char *str, size_t size);
+void bwsfree(const struct bwstring *s);
+size_t bwscpy(struct bwstring *dst, const struct bwstring *src);
+struct bwstring *bwsncpy(struct bwstring *dst, const struct bwstring *src, size_t size);
+struct bwstring *bwsnocpy(struct bwstring *dst, const struct bwstring *src, size_t offset, size_t size);
+int bwscmp(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset);
+int bwsncmp(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset, size_t len);
+int bwscoll(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset);
+size_t bwsfwrite(struct bwstring *bws, FILE *f, bool zero_ended);
+struct bwstring *bwsfgetln(FILE *file, size_t *len, bool zero_ended, struct reader_buffer *rb);
+
+static inline bwstring_iterator
+bws_begin(struct bwstring *bws)
+{
+
+ return (bwstring_iterator) (&(bws->data));
+}
+
+static inline bwstring_iterator
+bws_end(struct bwstring *bws)
+{
+
+ return ((MB_CUR_MAX == 1) ?
+ (bwstring_iterator) (bws->data.cstr + bws->len) :
+ (bwstring_iterator) (bws->data.wstr + bws->len));
+}
+
+static inline bwstring_iterator
+bws_iterator_inc(bwstring_iterator iter, size_t pos)
+{
+
+ if (MB_CUR_MAX == 1)
+ return ((unsigned char *) iter) + pos;
+ else
+ return ((wchar_t*) iter) + pos;
+}
+
+static inline wchar_t
+bws_get_iter_value(bwstring_iterator iter)
+{
+
+ if (MB_CUR_MAX == 1)
+ return *((unsigned char *) iter);
+ else
+ return *((wchar_t*) iter);
+}
+
+int
+bws_iterator_cmp(bwstring_iterator iter1, bwstring_iterator iter2, size_t len);
+
+#define BWS_GET(bws, pos) ((MB_CUR_MAX == 1) ? ((bws)->data.cstr[(pos)]) : (bws)->data.wstr[(pos)])
+
+void initialise_months(void);
+
+#endif /* __BWSTRING_H__ */
diff --git a/text_cmds/sort/coll.c b/text_cmds/sort/coll.c
new file mode 100644
index 0000000..f87ae0c
--- /dev/null
+++ b/text_cmds/sort/coll.c
@@ -0,0 +1,1324 @@
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <err.h>
+#include <langinfo.h>
+#include <limits.h>
+#include <math.h>
+#ifndef __APPLE__
+#include <md5.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "coll.h"
+#include "vsort.h"
+
+struct key_specs *keys;
+size_t keys_num = 0;
+
+wint_t symbol_decimal_point = L'.';
+/* there is no default thousands separator in collate rules: */
+wint_t symbol_thousands_sep = 0;
+wint_t symbol_negative_sign = L'-';
+wint_t symbol_positive_sign = L'+';
+
+static int wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset);
+static int gnumcoll(struct key_value*, struct key_value *, size_t offset);
+static int monthcoll(struct key_value*, struct key_value *, size_t offset);
+static int numcoll(struct key_value*, struct key_value *, size_t offset);
+static int hnumcoll(struct key_value*, struct key_value *, size_t offset);
+static int randomcoll(struct key_value*, struct key_value *, size_t offset);
+static int versioncoll(struct key_value*, struct key_value *, size_t offset);
+
+/*
+ * Allocate keys array
+ */
+struct keys_array *
+keys_array_alloc(void)
+{
+ struct keys_array *ka;
+ size_t sz;
+
+ sz = keys_array_size();
+ ka = sort_malloc(sz);
+ memset(ka, 0, sz);
+
+ return (ka);
+}
+
+/*
+ * Calculate whether we need key hint space
+ */
+static size_t
+key_hint_size(void)
+{
+
+ return (need_hint ? sizeof(struct key_hint) : 0);
+}
+
+/*
+ * Calculate keys array size
+ */
+size_t
+keys_array_size(void)
+{
+
+ return (keys_num * (sizeof(struct key_value) + key_hint_size()));
+}
+
+/*
+ * Clean data of keys array
+ */
+void
+clean_keys_array(const struct bwstring *s, struct keys_array *ka)
+{
+
+ if (ka) {
+ for (size_t i = 0; i < keys_num; ++i) {
+ const struct key_value *kv;
+
+ kv = get_key_from_keys_array(ka, i);
+ if (kv->k && kv->k != s)
+ bwsfree(kv->k);
+ }
+ memset(ka, 0, keys_array_size());
+ }
+}
+
+/*
+ * Get pointer to a key value in the keys set
+ */
+struct key_value *
+get_key_from_keys_array(struct keys_array *ka, size_t ind)
+{
+
+ return ((struct key_value *)((caddr_t)ka->key +
+ ind * (sizeof(struct key_value) + key_hint_size())));
+}
+
+/*
+ * Set value of a key in the keys set
+ */
+void
+set_key_on_keys_array(struct keys_array *ka, struct bwstring *s, size_t ind)
+{
+
+ if (ka && keys_num > ind) {
+ struct key_value *kv;
+
+ kv = get_key_from_keys_array(ka, ind);
+
+ if (kv->k && kv->k != s)
+ bwsfree(kv->k);
+ kv->k = s;
+ }
+}
+
+/*
+ * Initialize a sort list item
+ */
+struct sort_list_item *
+sort_list_item_alloc(void)
+{
+ struct sort_list_item *si;
+ size_t sz;
+
+ sz = sizeof(struct sort_list_item) + keys_array_size();
+ si = sort_malloc(sz);
+ memset(si, 0, sz);
+
+ return (si);
+}
+
+size_t
+sort_list_item_size(struct sort_list_item *si)
+{
+ size_t ret = 0;
+
+ if (si) {
+ ret = sizeof(struct sort_list_item) + keys_array_size();
+ if (si->str)
+ ret += bws_memsize(si->str);
+ for (size_t i = 0; i < keys_num; ++i) {
+ const struct key_value *kv;
+
+ kv = get_key_from_keys_array(&si->ka, i);
+
+ if (kv->k != si->str)
+ ret += bws_memsize(kv->k);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Calculate key for a sort list item
+ */
+static void
+sort_list_item_make_key(struct sort_list_item *si)
+{
+
+ preproc(si->str, &(si->ka));
+}
+
+/*
+ * Set value of a sort list item.
+ * Return combined string and keys memory size.
+ */
+void
+sort_list_item_set(struct sort_list_item *si, struct bwstring *str)
+{
+
+ if (si) {
+ clean_keys_array(si->str, &(si->ka));
+ if (si->str) {
+ if (si->str == str) {
+ /* we are trying to reset the same string */
+ return;
+ } else {
+ bwsfree(si->str);
+ si->str = NULL;
+ }
+ }
+ si->str = str;
+ sort_list_item_make_key(si);
+ }
+}
+
+/*
+ * De-allocate a sort list item object memory
+ */
+void
+sort_list_item_clean(struct sort_list_item *si)
+{
+
+ if (si) {
+ clean_keys_array(si->str, &(si->ka));
+ if (si->str) {
+ bwsfree(si->str);
+ si->str = NULL;
+ }
+ }
+}
+
+/*
+ * Skip columns according to specs
+ */
+static size_t
+skip_cols_to_start(const struct bwstring *s, size_t cols, size_t start,
+ bool skip_blanks, bool *empty_key)
+{
+ if (cols < 1)
+ return (BWSLEN(s) + 1);
+
+ if (skip_blanks)
+ while (start < BWSLEN(s) && iswblank_f(BWS_GET(s,start)))
+ ++start;
+
+ while (start < BWSLEN(s) && cols > 1) {
+ --cols;
+ ++start;
+ }
+
+ if (start >= BWSLEN(s))
+ *empty_key = true;
+
+ return (start);
+}
+
+/*
+ * Skip fields according to specs
+ */
+static size_t
+skip_fields_to_start(const struct bwstring *s, size_t fields, bool *empty_field)
+{
+
+ if (fields < 2) {
+ if (BWSLEN(s) == 0)
+ *empty_field = true;
+ return (0);
+ } else if (!(sort_opts_vals.tflag)) {
+ size_t cpos = 0;
+ bool pb = true;
+
+ while (cpos < BWSLEN(s)) {
+ bool isblank;
+
+ isblank = iswblank_f(BWS_GET(s, cpos));
+
+ if (isblank && !pb) {
+ --fields;
+ if (fields <= 1)
+ return (cpos);
+ }
+ pb = isblank;
+ ++cpos;
+ }
+ if (fields > 1)
+ *empty_field = true;
+ return (cpos);
+ } else {
+ size_t cpos = 0;
+
+ while (cpos < BWSLEN(s)) {
+ if (BWS_GET(s,cpos) == (wchar_t)sort_opts_vals.field_sep) {
+ --fields;
+ if (fields <= 1)
+ return (cpos + 1);
+ }
+ ++cpos;
+ }
+ if (fields > 1)
+ *empty_field = true;
+ return (cpos);
+ }
+}
+
+/*
+ * Find fields start
+ */
+static void
+find_field_start(const struct bwstring *s, struct key_specs *ks,
+ size_t *field_start, size_t *key_start, bool *empty_field, bool *empty_key)
+{
+
+ *field_start = skip_fields_to_start(s, ks->f1, empty_field);
+ if (!*empty_field)
+ *key_start = skip_cols_to_start(s, ks->c1, *field_start,
+ ks->pos1b, empty_key);
+ else
+ *empty_key = true;
+}
+
+/*
+ * Find end key position
+ */
+static size_t
+find_field_end(const struct bwstring *s, struct key_specs *ks)
+{
+ size_t f2, next_field_start, pos_end;
+ bool empty_field, empty_key;
+
+ empty_field = false;
+ empty_key = false;
+ f2 = ks->f2;
+
+ if (f2 == 0)
+ return (BWSLEN(s) + 1);
+ else {
+ if (ks->c2 == 0) {
+ next_field_start = skip_fields_to_start(s, f2 + 1,
+ &empty_field);
+ if ((next_field_start > 0) && sort_opts_vals.tflag &&
+ ((wchar_t)sort_opts_vals.field_sep == BWS_GET(s,
+ next_field_start - 1)))
+ --next_field_start;
+ } else
+ next_field_start = skip_fields_to_start(s, f2,
+ &empty_field);
+ }
+
+ if (empty_field || (next_field_start >= BWSLEN(s)))
+ return (BWSLEN(s) + 1);
+
+ if (ks->c2) {
+ pos_end = skip_cols_to_start(s, ks->c2, next_field_start,
+ ks->pos2b, &empty_key);
+ if (pos_end < BWSLEN(s))
+ ++pos_end;
+ } else
+ pos_end = next_field_start;
+
+ return (pos_end);
+}
+
+/*
+ * Cut a field according to the key specs
+ */
+static struct bwstring *
+cut_field(const struct bwstring *s, struct key_specs *ks)
+{
+ struct bwstring *ret = NULL;
+
+ if (s && ks) {
+ size_t field_start, key_end, key_start, sz;
+ bool empty_field, empty_key;
+
+ field_start = 0;
+ key_start = 0;
+ empty_field = false;
+ empty_key = false;
+
+ find_field_start(s, ks, &field_start, &key_start,
+ &empty_field, &empty_key);
+
+ if (empty_key)
+ sz = 0;
+ else {
+ key_end = find_field_end(s, ks);
+ sz = (key_end < key_start) ? 0 : (key_end - key_start);
+ }
+
+ ret = bwsalloc(sz);
+ if (sz)
+ bwsnocpy(ret, s, key_start, sz);
+ } else
+ ret = bwsalloc(0);
+
+ return (ret);
+}
+
+/*
+ * Preprocesses a line applying the necessary transformations
+ * specified by command line options and returns the preprocessed
+ * string, which can be used to compare.
+ */
+int
+preproc(struct bwstring *s, struct keys_array *ka)
+{
+
+ if (sort_opts_vals.kflag)
+ for (size_t i = 0; i < keys_num; i++) {
+ struct bwstring *key;
+ struct key_specs *kspecs;
+ struct sort_mods *sm;
+
+ kspecs = &(keys[i]);
+ key = cut_field(s, kspecs);
+
+ sm = &(kspecs->sm);
+ if (sm->dflag)
+ key = dictionary_order(key);
+ else if (sm->iflag)
+ key = ignore_nonprinting(key);
+ if (sm->fflag || sm->Mflag)
+ key = ignore_case(key);
+
+ set_key_on_keys_array(ka, key, i);
+ }
+ else {
+ struct bwstring *ret = NULL;
+ struct sort_mods *sm = default_sort_mods;
+
+ if (sm->bflag) {
+ if (ret == NULL)
+ ret = bwsdup(s);
+ ret = ignore_leading_blanks(ret);
+ }
+ if (sm->dflag) {
+ if (ret == NULL)
+ ret = bwsdup(s);
+ ret = dictionary_order(ret);
+ } else if (sm->iflag) {
+ if (ret == NULL)
+ ret = bwsdup(s);
+ ret = ignore_nonprinting(ret);
+ }
+ if (sm->fflag || sm->Mflag) {
+ if (ret == NULL)
+ ret = bwsdup(s);
+ ret = ignore_case(ret);
+ }
+ if (ret == NULL)
+ set_key_on_keys_array(ka, s, 0);
+ else
+ set_key_on_keys_array(ka, ret, 0);
+ }
+
+ return 0;
+}
+
+cmpcoll_t
+get_sort_func(struct sort_mods *sm)
+{
+
+ if (sm->nflag)
+ return (numcoll);
+ else if (sm->hflag)
+ return (hnumcoll);
+ else if (sm->gflag)
+ return (gnumcoll);
+ else if (sm->Mflag)
+ return (monthcoll);
+ else if (sm->Rflag)
+ return (randomcoll);
+ else if (sm->Vflag)
+ return (versioncoll);
+ else
+ return (wstrcoll);
+}
+
+/*
+ * Compares the given strings. Returns a positive number if
+ * the first precedes the second, a negative number if the second is
+ * the preceding one, and zero if they are equal. This function calls
+ * the underlying collate functions, which done the actual comparison.
+ */
+int
+key_coll(struct keys_array *ps1, struct keys_array *ps2, size_t offset)
+{
+ struct key_value *kv1, *kv2;
+ struct sort_mods *sm;
+ int res = 0;
+
+ for (size_t i = 0; i < keys_num; ++i) {
+ kv1 = get_key_from_keys_array(ps1, i);
+ kv2 = get_key_from_keys_array(ps2, i);
+ sm = &(keys[i].sm);
+
+ if (sm->rflag)
+ res = sm->func(kv2, kv1, offset);
+ else
+ res = sm->func(kv1, kv2, offset);
+
+ if (res)
+ break;
+
+ /* offset applies to only the first key */
+ offset = 0;
+ }
+ return (res);
+}
+
+/*
+ * Compare two strings.
+ * Plain symbol-by-symbol comparison.
+ */
+int
+top_level_str_coll(const struct bwstring *s1, const struct bwstring *s2)
+{
+
+ if (default_sort_mods->rflag) {
+ const struct bwstring *tmp;
+
+ tmp = s1;
+ s1 = s2;
+ s2 = tmp;
+ }
+
+ return (bwscoll(s1, s2, 0));
+}
+
+/*
+ * Compare a string and a sort list item, according to the sort specs.
+ */
+int
+str_list_coll(struct bwstring *str1, struct sort_list_item **ss2)
+{
+ struct keys_array *ka1;
+ int ret = 0;
+
+ ka1 = keys_array_alloc();
+
+ preproc(str1, ka1);
+
+ sort_list_item_make_key(*ss2);
+
+ if (debug_sort) {
+ bwsprintf(stdout, str1, "; s1=<", ">");
+ bwsprintf(stdout, (*ss2)->str, ", s2=<", ">");
+ }
+
+ ret = key_coll(ka1, &((*ss2)->ka), 0);
+
+ if (debug_sort)
+ printf("; cmp1=%d", ret);
+
+ clean_keys_array(str1, ka1);
+ sort_free(ka1);
+
+ if ((ret == 0) && !(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
+ ret = top_level_str_coll(str1, ((*ss2)->str));
+ if (debug_sort)
+ printf("; cmp2=%d", ret);
+ }
+
+ if (debug_sort)
+ printf("\n");
+
+ return (ret);
+}
+
+/*
+ * Compare two sort list items, according to the sort specs.
+ */
+int
+list_coll_offset(struct sort_list_item **ss1, struct sort_list_item **ss2,
+ size_t offset)
+{
+ int ret;
+
+ ret = key_coll(&((*ss1)->ka), &((*ss2)->ka), offset);
+
+ if (debug_sort) {
+ if (offset)
+ printf("; offset=%d", (int) offset);
+ bwsprintf(stdout, ((*ss1)->str), "; s1=<", ">");
+ bwsprintf(stdout, ((*ss2)->str), ", s2=<", ">");
+ printf("; cmp1=%d\n", ret);
+ }
+
+ if (ret)
+ return (ret);
+
+ if (!(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
+ ret = top_level_str_coll(((*ss1)->str), ((*ss2)->str));
+ if (debug_sort)
+ printf("; cmp2=%d\n", ret);
+ }
+
+ return (ret);
+}
+
+/*
+ * Compare two sort list items, according to the sort specs.
+ */
+int
+list_coll(struct sort_list_item **ss1, struct sort_list_item **ss2)
+{
+
+ return (list_coll_offset(ss1, ss2, 0));
+}
+
+#define LSCDEF(N) \
+static int \
+list_coll_##N(struct sort_list_item **ss1, struct sort_list_item **ss2) \
+{ \
+ \
+ return (list_coll_offset(ss1, ss2, N)); \
+}
+
+LSCDEF(1)
+LSCDEF(2)
+LSCDEF(3)
+LSCDEF(4)
+LSCDEF(5)
+LSCDEF(6)
+LSCDEF(7)
+LSCDEF(8)
+LSCDEF(9)
+LSCDEF(10)
+LSCDEF(11)
+LSCDEF(12)
+LSCDEF(13)
+LSCDEF(14)
+LSCDEF(15)
+LSCDEF(16)
+LSCDEF(17)
+LSCDEF(18)
+LSCDEF(19)
+LSCDEF(20)
+
+listcoll_t
+get_list_call_func(size_t offset)
+{
+ static const listcoll_t lsarray[] = { list_coll, list_coll_1,
+ list_coll_2, list_coll_3, list_coll_4, list_coll_5,
+ list_coll_6, list_coll_7, list_coll_8, list_coll_9,
+ list_coll_10, list_coll_11, list_coll_12, list_coll_13,
+ list_coll_14, list_coll_15, list_coll_16, list_coll_17,
+ list_coll_18, list_coll_19, list_coll_20 };
+
+ if (offset <= 20)
+ return (lsarray[offset]);
+
+ return (list_coll);
+}
+
+/*
+ * Compare two sort list items, only by their original string.
+ */
+int
+list_coll_by_str_only(struct sort_list_item **ss1, struct sort_list_item **ss2)
+{
+
+ return (top_level_str_coll(((*ss1)->str), ((*ss2)->str)));
+}
+
+/*
+ * Maximum size of a number in the string (before or after decimal point)
+ */
+#define MAX_NUM_SIZE (128)
+
+/*
+ * Set suffix value
+ */
+static void setsuffix(wchar_t c, unsigned char *si)
+{
+ switch (c){
+ case L'k':
+ case L'K':
+ *si = 1;
+ break;
+ case L'M':
+ *si = 2;
+ break;
+ case L'G':
+ *si = 3;
+ break;
+ case L'T':
+ *si = 4;
+ break;
+ case L'P':
+ *si = 5;
+ break;
+ case L'E':
+ *si = 6;
+ break;
+ case L'Z':
+ *si = 7;
+ break;
+ case L'Y':
+ *si = 8;
+ break;
+ default:
+ *si = 0;
+ }
+}
+
+/*
+ * Read string s and parse the string into a fixed-decimal-point number.
+ * sign equals -1 if the number is negative (explicit plus is not allowed,
+ * according to GNU sort's "info sort".
+ * The number part before decimal point is in the smain, after the decimal
+ * point is in sfrac, tail is the pointer to the remainder of the string.
+ */
+static int
+read_number(struct bwstring *s0, int *sign, wchar_t *smain, size_t *main_len, wchar_t *sfrac, size_t *frac_len, unsigned char *si)
+{
+ bwstring_iterator s;
+ bool prev_thousand_sep = false;
+
+ s = bws_begin(s0);
+
+ /* always end the fraction with zero, even if we have no fraction */
+ sfrac[0] = 0;
+
+ while (iswblank_f(bws_get_iter_value(s)))
+ s = bws_iterator_inc(s, 1);
+
+ if (bws_get_iter_value(s) == (wchar_t)symbol_negative_sign) {
+ *sign = -1;
+ s = bws_iterator_inc(s, 1);
+ }
+
+ // This is '0', not '\0', do not change this
+ while (iswdigit(bws_get_iter_value(s)) &&
+ (bws_get_iter_value(s) == L'0'))
+ s = bws_iterator_inc(s, 1);
+
+ while (bws_get_iter_value(s) && *main_len < MAX_NUM_SIZE) {
+ if (iswdigit(bws_get_iter_value(s))) {
+ smain[*main_len] = bws_get_iter_value(s);
+ s = bws_iterator_inc(s, 1);
+ *main_len += 1;
+ prev_thousand_sep = false;
+ } else if (symbol_thousands_sep
+ && (bws_get_iter_value(s) == (wchar_t)symbol_thousands_sep)
+ && (!prev_thousand_sep)
+ ) {
+ s = bws_iterator_inc(s, 1);
+ prev_thousand_sep = true;
+ } else
+ break;
+ }
+
+ smain[*main_len] = 0;
+
+ if (bws_get_iter_value(s) == (wchar_t)symbol_decimal_point) {
+ s = bws_iterator_inc(s, 1);
+ while (iswdigit(bws_get_iter_value(s)) &&
+ *frac_len < MAX_NUM_SIZE) {
+ sfrac[*frac_len] = bws_get_iter_value(s);
+ s = bws_iterator_inc(s, 1);
+ *frac_len += 1;
+ }
+ sfrac[*frac_len] = 0;
+
+ while (*frac_len > 0 && sfrac[*frac_len - 1] == L'0') {
+ --(*frac_len);
+ sfrac[*frac_len] = L'\0';
+ }
+ }
+
+ setsuffix(bws_get_iter_value(s),si);
+
+ if ((*main_len + *frac_len) == 0)
+ *sign = 0;
+
+ return (0);
+}
+
+/*
+ * Implements string sort.
+ */
+static int
+wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
+{
+
+ if (debug_sort) {
+ if (offset)
+ printf("; offset=%d\n", (int) offset);
+ bwsprintf(stdout, kv1->k, "; k1=<", ">");
+ printf("(%zu)", BWSLEN(kv1->k));
+ bwsprintf(stdout, kv2->k, ", k2=<", ">");
+ printf("(%zu)", BWSLEN(kv2->k));
+ }
+
+ return (bwscoll(kv1->k, kv2->k, offset));
+}
+
+/*
+ * Compare two suffixes
+ */
+static inline int
+cmpsuffix(unsigned char si1, unsigned char si2)
+{
+
+ return ((char)si1 - (char)si2);
+}
+
+/*
+ * Implements numeric sort for -n and -h.
+ */
+static int
+numcoll_impl(struct key_value *kv1, struct key_value *kv2,
+ size_t offset __unused, bool use_suffix)
+{
+ struct bwstring *s1, *s2;
+ wchar_t sfrac1[MAX_NUM_SIZE + 1], sfrac2[MAX_NUM_SIZE + 1];
+ wchar_t smain1[MAX_NUM_SIZE + 1], smain2[MAX_NUM_SIZE + 1];
+ int cmp_res, sign1, sign2;
+ size_t frac1, frac2, main1, main2;
+ unsigned char SI1, SI2;
+ bool e1, e2, key1_read, key2_read;
+
+ s1 = kv1->k;
+ s2 = kv2->k;
+ sign1 = sign2 = 0;
+ main1 = main2 = 0;
+ frac1 = frac2 = 0;
+
+ key1_read = key2_read = false;
+
+ if (debug_sort) {
+ bwsprintf(stdout, s1, "; k1=<", ">");
+ bwsprintf(stdout, s2, ", k2=<", ">");
+ }
+
+ if (s1 == s2)
+ return (0);
+
+ if (kv1->hint->status == HS_UNINITIALIZED) {
+ /* read the number from the string */
+ read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
+ key1_read = true;
+ kv1->hint->v.nh.n1 = wcstoull(smain1, NULL, 10);
+ if(main1 < 1 && frac1 < 1)
+ kv1->hint->v.nh.empty=true;
+ kv1->hint->v.nh.si = SI1;
+ kv1->hint->status = (kv1->hint->v.nh.n1 != ULLONG_MAX) ?
+ HS_INITIALIZED : HS_ERROR;
+ kv1->hint->v.nh.neg = (sign1 < 0) ? true : false;
+ }
+
+ if (kv2->hint->status == HS_UNINITIALIZED) {
+ /* read the number from the string */
+ read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2,&SI2);
+ key2_read = true;
+ kv2->hint->v.nh.n1 = wcstoull(smain2, NULL, 10);
+ if(main2 < 1 && frac2 < 1)
+ kv2->hint->v.nh.empty=true;
+ kv2->hint->v.nh.si = SI2;
+ kv2->hint->status = (kv2->hint->v.nh.n1 != ULLONG_MAX) ?
+ HS_INITIALIZED : HS_ERROR;
+ kv2->hint->v.nh.neg = (sign2 < 0) ? true : false;
+ }
+
+ if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status ==
+ HS_INITIALIZED) {
+ unsigned long long n1, n2;
+ bool neg1, neg2;
+
+ e1 = kv1->hint->v.nh.empty;
+ e2 = kv2->hint->v.nh.empty;
+
+ if (e1 && e2)
+ return (0);
+
+ neg1 = kv1->hint->v.nh.neg;
+ neg2 = kv2->hint->v.nh.neg;
+
+ if (neg1 && !neg2)
+ return (-1);
+ if (neg2 && !neg1)
+ return (+1);
+
+ if (e1)
+ return (neg2 ? +1 : -1);
+ else if (e2)
+ return (neg1 ? -1 : +1);
+
+
+ if (use_suffix) {
+ cmp_res = cmpsuffix(kv1->hint->v.nh.si, kv2->hint->v.nh.si);
+ if (cmp_res)
+ return (neg1 ? -cmp_res : cmp_res);
+ }
+
+ n1 = kv1->hint->v.nh.n1;
+ n2 = kv2->hint->v.nh.n1;
+ if (n1 < n2)
+ return (neg1 ? +1 : -1);
+ else if (n1 > n2)
+ return (neg1 ? -1 : +1);
+ }
+
+ /* read the numbers from the strings */
+ if (!key1_read)
+ read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
+ if (!key2_read)
+ read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2, &SI2);
+
+ e1 = ((main1 + frac1) == 0);
+ e2 = ((main2 + frac2) == 0);
+
+ if (e1 && e2)
+ return (0);
+
+ /* we know the result if the signs are different */
+ if (sign1 < 0 && sign2 >= 0)
+ return (-1);
+ if (sign1 >= 0 && sign2 < 0)
+ return (+1);
+
+ if (e1)
+ return ((sign2 < 0) ? +1 : -1);
+ else if (e2)
+ return ((sign1 < 0) ? -1 : +1);
+
+ if (use_suffix) {
+ cmp_res = cmpsuffix(SI1, SI2);
+ if (cmp_res)
+ return ((sign1 < 0) ? -cmp_res : cmp_res);
+ }
+
+ /* if both numbers are empty assume that the strings are equal */
+ if (main1 < 1 && main2 < 1 && frac1 < 1 && frac2 < 1)
+ return (0);
+
+ /*
+ * if the main part is of different size, we know the result
+ * (because the leading zeros are removed)
+ */
+ if (main1 < main2)
+ cmp_res = -1;
+ else if (main1 > main2)
+ cmp_res = +1;
+ /* if the sizes are equal then simple non-collate string compare gives the correct result */
+ else
+ cmp_res = wcscmp(smain1, smain2);
+
+ /* check fraction */
+ if (!cmp_res)
+ cmp_res = wcscmp(sfrac1, sfrac2);
+
+ if (!cmp_res)
+ return (0);
+
+ /* reverse result if the signs are negative */
+ if (sign1 < 0 && sign2 < 0)
+ cmp_res = -cmp_res;
+
+ return (cmp_res);
+}
+
+/*
+ * Implements numeric sort (-n).
+ */
+static int
+numcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
+{
+
+ return (numcoll_impl(kv1, kv2, offset, false));
+}
+
+/*
+ * Implements 'human' numeric sort (-h).
+ */
+static int
+hnumcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
+{
+
+ return (numcoll_impl(kv1, kv2, offset, true));
+}
+
+/*
+ * Implements random sort (-R).
+ */
+static int
+randomcoll(struct key_value *kv1, struct key_value *kv2,
+ size_t offset __unused)
+{
+ struct bwstring *s1, *s2;
+ MD5_CTX ctx1, ctx2;
+ char *b1, *b2;
+
+ s1 = kv1->k;
+ s2 = kv2->k;
+
+ if (debug_sort) {
+ bwsprintf(stdout, s1, "; k1=<", ">");
+ bwsprintf(stdout, s2, ", k2=<", ">");
+ }
+
+ if (s1 == s2)
+ return (0);
+
+ memcpy(&ctx1,&md5_ctx,sizeof(MD5_CTX));
+ memcpy(&ctx2,&md5_ctx,sizeof(MD5_CTX));
+
+ MD5Update(&ctx1, bwsrawdata(s1), bwsrawlen(s1));
+ MD5Update(&ctx2, bwsrawdata(s2), bwsrawlen(s2));
+ b1 = MD5End(&ctx1, NULL);
+ b2 = MD5End(&ctx2, NULL);
+ if (b1 == NULL) {
+ if (b2 == NULL)
+ return (0);
+ else {
+ sort_free(b2);
+ return (-1);
+ }
+ } else if (b2 == NULL) {
+ sort_free(b1);
+ return (+1);
+ } else {
+ int cmp_res;
+
+ cmp_res = strcmp(b1,b2);
+ sort_free(b1);
+ sort_free(b2);
+
+ if (!cmp_res)
+ cmp_res = bwscoll(s1, s2, 0);
+
+ return (cmp_res);
+ }
+}
+
+/*
+ * Implements version sort (-V).
+ */
+static int
+versioncoll(struct key_value *kv1, struct key_value *kv2,
+ size_t offset __unused)
+{
+ struct bwstring *s1, *s2;
+
+ s1 = kv1->k;
+ s2 = kv2->k;
+
+ if (debug_sort) {
+ bwsprintf(stdout, s1, "; k1=<", ">");
+ bwsprintf(stdout, s2, ", k2=<", ">");
+ }
+
+ if (s1 == s2)
+ return (0);
+
+ return (vcmp(s1, s2));
+}
+
+/*
+ * Check for minus infinity
+ */
+static inline bool
+huge_minus(double d, int err1)
+{
+
+ if (err1 == ERANGE)
+ if (d == -HUGE_VAL || d == -HUGE_VALF || d == -HUGE_VALL)
+ return (+1);
+
+ return (0);
+}
+
+/*
+ * Check for plus infinity
+ */
+static inline bool
+huge_plus(double d, int err1)
+{
+
+ if (err1 == ERANGE)
+ if (d == HUGE_VAL || d == HUGE_VALF || d == HUGE_VALL)
+ return (+1);
+
+ return (0);
+}
+
+/*
+ * Check whether a function is a NAN
+ */
+static bool
+is_nan(double d)
+{
+
+ return ((d == NAN) || (isnan(d)));
+}
+
+/*
+ * Compare two NANs
+ */
+static int
+cmp_nans(double d1, double d2)
+{
+
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+ return (0);
+}
+
+/*
+ * Implements general numeric sort (-g).
+ */
+static int
+gnumcoll(struct key_value *kv1, struct key_value *kv2,
+ size_t offset __unused)
+{
+ double d1, d2;
+ int err1, err2;
+ bool empty1, empty2, key1_read, key2_read;
+
+ d1 = d2 = 0;
+ err1 = err2 = 0;
+ key1_read = key2_read = false;
+
+ if (debug_sort) {
+ bwsprintf(stdout, kv1->k, "; k1=<", ">");
+ bwsprintf(stdout, kv2->k, "; k2=<", ">");
+ }
+
+ if (kv1->hint->status == HS_UNINITIALIZED) {
+ errno = 0;
+ d1 = bwstod(kv1->k, &empty1);
+ err1 = errno;
+
+ if (empty1)
+ kv1->hint->v.gh.notnum = true;
+ else if (err1 == 0) {
+ kv1->hint->v.gh.d = d1;
+ kv1->hint->v.gh.nan = is_nan(d1);
+ kv1->hint->status = HS_INITIALIZED;
+ } else
+ kv1->hint->status = HS_ERROR;
+
+ key1_read = true;
+ }
+
+ if (kv2->hint->status == HS_UNINITIALIZED) {
+ errno = 0;
+ d2 = bwstod(kv2->k, &empty2);
+ err2 = errno;
+
+ if (empty2)
+ kv2->hint->v.gh.notnum = true;
+ else if (err2 == 0) {
+ kv2->hint->v.gh.d = d2;
+ kv2->hint->v.gh.nan = is_nan(d2);
+ kv2->hint->status = HS_INITIALIZED;
+ } else
+ kv2->hint->status = HS_ERROR;
+
+ key2_read = true;
+ }
+
+ if (kv1->hint->status == HS_INITIALIZED &&
+ kv2->hint->status == HS_INITIALIZED) {
+ if (kv1->hint->v.gh.notnum)
+ return ((kv2->hint->v.gh.notnum) ? 0 : -1);
+ else if (kv2->hint->v.gh.notnum)
+ return (+1);
+
+ if (kv1->hint->v.gh.nan)
+ return ((kv2->hint->v.gh.nan) ?
+ cmp_nans(kv1->hint->v.gh.d, kv2->hint->v.gh.d) :
+ -1);
+ else if (kv2->hint->v.gh.nan)
+ return (+1);
+
+ d1 = kv1->hint->v.gh.d;
+ d2 = kv2->hint->v.gh.d;
+
+ if (d1 < d2)
+ return (-1);
+ else if (d1 > d2)
+ return (+1);
+ else
+ return (0);
+ }
+
+ if (!key1_read) {
+ errno = 0;
+ d1 = bwstod(kv1->k, &empty1);
+ err1 = errno;
+ }
+
+ if (!key2_read) {
+ errno = 0;
+ d2 = bwstod(kv2->k, &empty2);
+ err2 = errno;
+ }
+
+ /* Non-value case: */
+ if (empty1)
+ return (empty2 ? 0 : -1);
+ else if (empty2)
+ return (+1);
+
+ /* NAN case */
+ if (is_nan(d1))
+ return (is_nan(d2) ? cmp_nans(d1, d2) : -1);
+ else if (is_nan(d2))
+ return (+1);
+
+ /* Infinities */
+ if (err1 == ERANGE || err2 == ERANGE) {
+ /* Minus infinity case */
+ if (huge_minus(d1, err1)) {
+ if (huge_minus(d2, err2)) {
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+ return (0);
+ } else
+ return (-1);
+
+ } else if (huge_minus(d2, err2)) {
+ if (huge_minus(d1, err1)) {
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+ return (0);
+ } else
+ return (+1);
+ }
+
+ /* Plus infinity case */
+ if (huge_plus(d1, err1)) {
+ if (huge_plus(d2, err2)) {
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+ return (0);
+ } else
+ return (+1);
+ } else if (huge_plus(d2, err2)) {
+ if (huge_plus(d1, err1)) {
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+ return (0);
+ } else
+ return (-1);
+ }
+ }
+
+ if (d1 < d2)
+ return (-1);
+ if (d1 > d2)
+ return (+1);
+
+ return (0);
+}
+
+/*
+ * Implements month sort (-M).
+ */
+static int
+monthcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused)
+{
+ int val1, val2;
+ bool key1_read, key2_read;
+
+ val1 = val2 = 0;
+ key1_read = key2_read = false;
+
+ if (debug_sort) {
+ bwsprintf(stdout, kv1->k, "; k1=<", ">");
+ bwsprintf(stdout, kv2->k, "; k2=<", ">");
+ }
+
+ if (kv1->hint->status == HS_UNINITIALIZED) {
+ kv1->hint->v.Mh.m = bws_month_score(kv1->k);
+ key1_read = true;
+ kv1->hint->status = HS_INITIALIZED;
+ }
+
+ if (kv2->hint->status == HS_UNINITIALIZED) {
+ kv2->hint->v.Mh.m = bws_month_score(kv2->k);
+ key2_read = true;
+ kv2->hint->status = HS_INITIALIZED;
+ }
+
+ if (kv1->hint->status == HS_INITIALIZED) {
+ val1 = kv1->hint->v.Mh.m;
+ key1_read = true;
+ }
+
+ if (kv2->hint->status == HS_INITIALIZED) {
+ val2 = kv2->hint->v.Mh.m;
+ key2_read = true;
+ }
+
+ if (!key1_read)
+ val1 = bws_month_score(kv1->k);
+ if (!key2_read)
+ val2 = bws_month_score(kv2->k);
+
+ if (val1 == val2) {
+ return (0);
+ }
+ if (val1 < val2)
+ return (-1);
+ return (+1);
+}
diff --git a/text_cmds/sort/coll.h b/text_cmds/sort/coll.h
new file mode 100644
index 0000000..6e3f9b4
--- /dev/null
+++ b/text_cmds/sort/coll.h
@@ -0,0 +1,168 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__COLL_H__)
+#define __COLL_H__
+
+#include "bwstring.h"
+#include "sort.h"
+
+/*
+ * Sort hint data for -n
+ */
+struct n_hint
+{
+ unsigned long long n1;
+ unsigned char si;
+ bool empty;
+ bool neg;
+};
+
+/*
+ * Sort hint data for -g
+ */
+struct g_hint
+{
+ double d;
+ bool nan;
+ bool notnum;
+};
+
+/*
+ * Sort hint data for -M
+ */
+struct M_hint
+{
+ int m;
+};
+
+/*
+ * Status of a sort hint object
+ */
+typedef enum
+{
+ HS_ERROR = -1, HS_UNINITIALIZED = 0, HS_INITIALIZED = 1
+} hint_status;
+
+/*
+ * Sort hint object
+ */
+struct key_hint
+{
+ hint_status status;
+ union
+ {
+ struct n_hint nh;
+ struct g_hint gh;
+ struct M_hint Mh;
+ } v;
+};
+
+/*
+ * Key value
+ */
+struct key_value
+{
+ struct bwstring *k; /* key string */
+ struct key_hint hint[0]; /* key sort hint */
+} __packed;
+
+/*
+ * Set of keys container object.
+ */
+struct keys_array
+{
+ struct key_value key[0];
+};
+
+/*
+ * Parsed -k option data
+ */
+struct key_specs
+{
+ struct sort_mods sm;
+ size_t c1;
+ size_t c2;
+ size_t f1;
+ size_t f2;
+ bool pos1b;
+ bool pos2b;
+};
+
+/*
+ * Single entry in sort list.
+ */
+struct sort_list_item
+{
+ struct bwstring *str;
+ struct keys_array ka;
+};
+
+/*
+ * Function type, used to compare two list objects
+ */
+typedef int (*listcoll_t)(struct sort_list_item **ss1, struct sort_list_item **ss2);
+
+extern struct key_specs *keys;
+extern size_t keys_num;
+
+/*
+ * Main localised symbols. These must be wint_t as they may hold WEOF.
+ */
+extern wint_t symbol_decimal_point;
+extern wint_t symbol_thousands_sep;
+extern wint_t symbol_negative_sign;
+extern wint_t symbol_positive_sign;
+
+/* funcs */
+
+cmpcoll_t get_sort_func(struct sort_mods *sm);
+
+struct keys_array *keys_array_alloc(void);
+size_t keys_array_size(void);
+struct key_value *get_key_from_keys_array(struct keys_array *ka, size_t ind);
+void set_key_on_keys_array(struct keys_array *ka, struct bwstring *s, size_t ind);
+void clean_keys_array(const struct bwstring *s, struct keys_array *ka);
+
+struct sort_list_item *sort_list_item_alloc(void);
+void sort_list_item_set(struct sort_list_item *si, struct bwstring *str);
+void sort_list_item_clean(struct sort_list_item *si);
+size_t sort_list_item_size(struct sort_list_item *si);
+
+int preproc(struct bwstring *s, struct keys_array *ka);
+int top_level_str_coll(const struct bwstring *, const struct bwstring *);
+int key_coll(struct keys_array *ks1, struct keys_array *ks2, size_t offset);
+int str_list_coll(struct bwstring *str1, struct sort_list_item **ss2);
+int list_coll_by_str_only(struct sort_list_item **ss1, struct sort_list_item **ss2);
+int list_coll(struct sort_list_item **ss1, struct sort_list_item **ss2);
+int list_coll_offset(struct sort_list_item **ss1, struct sort_list_item **ss2, size_t offset);
+
+listcoll_t get_list_call_func(size_t offset);
+
+#endif /* __COLL_H__ */
diff --git a/text_cmds/sort/commoncrypto.c b/text_cmds/sort/commoncrypto.c
new file mode 100644
index 0000000..c7b3b8c
--- /dev/null
+++ b/text_cmds/sort/commoncrypto.c
@@ -0,0 +1,98 @@
+/* mdXhl.c
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/lib/libmd/mdXhl.c 294037 2016-01-14 21:08:23Z jtl $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "commoncrypto.h"
+
+#define LENGTH CC_MD5_DIGEST_LENGTH
+
+char *MD5FileChunk(const char *, char *, off_t, off_t);
+
+char *
+MD5End(CC_MD5_CTX *ctx, char *buf)
+{
+ int i;
+ unsigned char digest[LENGTH];
+ static const char hex[]="0123456789abcdef";
+
+ if (!buf)
+ buf = malloc(2*LENGTH + 1);
+ if (!buf)
+ return 0;
+ CC_MD5_Final(digest, ctx);
+ for (i = 0; i < LENGTH; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+
+char *
+MD5File(const char *filename, char *buf)
+{
+ return (MD5FileChunk(filename, buf, 0, 0));
+}
+
+char *
+MD5FileChunk(const char *filename, char *buf, off_t ofs, off_t len)
+{
+ unsigned char buffer[16*1024];
+ CC_MD5_CTX ctx;
+ int fd, readrv, e;
+ off_t remain;
+
+ if (len < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ CC_MD5_Init(&ctx);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ if (ofs != 0) {
+ errno = 0;
+ if (lseek(fd, ofs, SEEK_SET) != ofs ||
+ (ofs == -1 && errno != 0)) {
+ readrv = -1;
+ goto error;
+ }
+ }
+ remain = len;
+ readrv = 0;
+ while (len == 0 || remain > 0) {
+ if (len == 0 || remain > (off_t)sizeof(buffer))
+ readrv = read(fd, buffer, sizeof(buffer));
+ else
+ readrv = read(fd, buffer, remain);
+ if (readrv <= 0)
+ break;
+ CC_MD5_Update(&ctx, buffer, readrv);
+ remain -= readrv;
+ }
+error:
+ e = errno;
+ close(fd);
+ errno = e;
+ if (readrv < 0)
+ return NULL;
+ return (MD5End(&ctx, buf));
+}
diff --git a/text_cmds/sort/commoncrypto.h b/text_cmds/sort/commoncrypto.h
new file mode 100644
index 0000000..5170b51
--- /dev/null
+++ b/text_cmds/sort/commoncrypto.h
@@ -0,0 +1,8 @@
+#include <CommonCrypto/CommonDigest.h>
+
+#define MD5_CTX CC_MD5_CTX
+#define MD5Init CC_MD5_Init
+#define MD5Update CC_MD5_Update
+
+char *MD5End(CC_MD5_CTX *, char *);
+char *MD5File(const char *, char *);
diff --git a/text_cmds/sort/file.c b/text_cmds/sort/file.c
new file mode 100644
index 0000000..346819b
--- /dev/null
+++ b/text_cmds/sort/file.c
@@ -0,0 +1,1684 @@
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <err.h>
+#include <fcntl.h>
+#if defined(SORT_THREADS)
+#include <pthread.h>
+#endif
+#ifndef __APPLE__
+#include <semaphore.h>
+#else
+#include <mach/mach_init.h>
+#include <mach/mach_error.h>
+#include <mach/semaphore.h>
+#include <mach/task.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "coll.h"
+#include "file.h"
+#include "radixsort.h"
+
+unsigned long long free_memory = 1000000;
+unsigned long long available_free_memory = 1000000;
+
+bool use_mmap;
+
+const char *tmpdir = "/var/tmp";
+const char *compress_program;
+
+size_t max_open_files = 16;
+
+/*
+ * How much space we read from file at once
+ */
+#define READ_CHUNK (4096)
+
+/*
+ * File reader structure
+ */
+struct file_reader
+{
+ struct reader_buffer rb;
+ FILE *file;
+ char *fname;
+ unsigned char *buffer;
+ unsigned char *mmapaddr;
+ unsigned char *mmapptr;
+ size_t bsz;
+ size_t cbsz;
+ size_t mmapsize;
+ size_t strbeg;
+ int fd;
+ char elsymb;
+};
+
+/*
+ * Structure to be used in file merge process.
+ */
+struct file_header
+{
+ struct file_reader *fr;
+ struct sort_list_item *si; /* current top line */
+ size_t file_pos;
+};
+
+/*
+ * List elements of "cleanable" files list.
+ */
+struct CLEANABLE_FILE
+{
+ char *fn;
+ LIST_ENTRY(CLEANABLE_FILE) files;
+};
+
+/*
+ * List header of "cleanable" files list.
+ */
+static LIST_HEAD(CLEANABLE_FILES,CLEANABLE_FILE) tmp_files;
+
+/*
+ * Semaphore to protect the tmp file list.
+ * We use semaphore here because it is signal-safe, according to POSIX.
+ * And semaphore does not require pthread library.
+ */
+#ifndef __APPLE__
+static sem_t tmp_files_sem;
+#else
+static semaphore_t tmp_files_sem;
+#endif
+
+static void mt_sort(struct sort_list *list,
+ int (*sort_func)(void *, size_t, size_t,
+ int (*)(const void *, const void *)), const char* fn);
+
+/*
+ * Init tmp files list
+ */
+void
+init_tmp_files(void)
+{
+
+ LIST_INIT(&tmp_files);
+#ifndef __APPLE__
+ sem_init(&tmp_files_sem, 0, 1);
+#else
+ {
+ mach_port_t self = mach_task_self();
+ kern_return_t ret = semaphore_create(self, &tmp_files_sem, SYNC_POLICY_FIFO, 1);
+ if (ret != KERN_SUCCESS) {
+ err(2,NULL);
+ }
+ }
+#endif
+}
+
+/*
+ * Save name of a tmp file for signal cleanup
+ */
+void
+tmp_file_atexit(const char *tmp_file)
+{
+
+ if (tmp_file) {
+#ifndef __APPLE__
+ sem_wait(&tmp_files_sem);
+#else
+ semaphore_wait(tmp_files_sem);
+#endif
+ struct CLEANABLE_FILE *item =
+ sort_malloc(sizeof(struct CLEANABLE_FILE));
+ item->fn = sort_strdup(tmp_file);
+ LIST_INSERT_HEAD(&tmp_files, item, files);
+#ifndef __APPLE__
+ sem_post(&tmp_files_sem);
+#else
+ semaphore_signal(tmp_files_sem);
+#endif
+ }
+}
+
+/*
+ * Clear tmp files
+ */
+void
+clear_tmp_files(void)
+{
+ struct CLEANABLE_FILE *item;
+
+#ifndef __APPLE__
+ sem_wait(&tmp_files_sem);
+#else
+ semaphore_wait(tmp_files_sem);
+#endif
+ LIST_FOREACH(item,&tmp_files,files) {
+ if ((item) && (item->fn))
+ unlink(item->fn);
+ }
+#ifndef __APPLE__
+ sem_post(&tmp_files_sem);
+#else
+ semaphore_signal(tmp_files_sem);
+#endif
+}
+
+/*
+ * Check whether a file is a temporary file
+ */
+static bool
+file_is_tmp(const char* fn)
+{
+ struct CLEANABLE_FILE *item;
+ bool ret = false;
+
+ if (fn) {
+#ifndef __APPLE__
+ sem_wait(&tmp_files_sem);
+#else
+ semaphore_wait(tmp_files_sem);
+#endif
+ LIST_FOREACH(item,&tmp_files,files) {
+ if ((item) && (item->fn))
+ if (strcmp(item->fn, fn) == 0) {
+ ret = true;
+ break;
+ }
+ }
+#ifndef __APPLE__
+ sem_post(&tmp_files_sem);
+#else
+ semaphore_signal(tmp_files_sem);
+#endif
+ }
+
+ return (ret);
+}
+
+/*
+ * Generate new temporary file name
+ */
+char *
+new_tmp_file_name(void)
+{
+ static size_t tfcounter = 0;
+ static const char *fn = ".bsdsort.";
+ char *ret;
+ size_t sz;
+
+ sz = strlen(tmpdir) + 1 + strlen(fn) + 32 + 1;
+ ret = sort_malloc(sz);
+
+ sprintf(ret, "%s/%s%d.%lu", tmpdir, fn, (int) getpid(), (unsigned long)(tfcounter++));
+ tmp_file_atexit(ret);
+ return (ret);
+}
+
+/*
+ * Initialize file list
+ */
+void
+file_list_init(struct file_list *fl, bool tmp)
+{
+
+ if (fl) {
+ fl->count = 0;
+ fl->sz = 0;
+ fl->fns = NULL;
+ fl->tmp = tmp;
+ }
+}
+
+/*
+ * Add a file name to the list
+ */
+void
+file_list_add(struct file_list *fl, char *fn, bool allocate)
+{
+
+ if (fl && fn) {
+ if (fl->count >= fl->sz || (fl->fns == NULL)) {
+ fl->sz = (fl->sz) * 2 + 1;
+ fl->fns = sort_realloc(fl->fns, fl->sz *
+ sizeof(char *));
+ }
+ fl->fns[fl->count] = allocate ? sort_strdup(fn) : fn;
+ fl->count += 1;
+ }
+}
+
+/*
+ * Populate file list from array of file names
+ */
+void
+file_list_populate(struct file_list *fl, int argc, char **argv, bool allocate)
+{
+
+ if (fl && argv) {
+ int i;
+
+ for (i = 0; i < argc; i++)
+ file_list_add(fl, argv[i], allocate);
+ }
+}
+
+/*
+ * Clean file list data and delete the files,
+ * if this is a list of temporary files
+ */
+void
+file_list_clean(struct file_list *fl)
+{
+
+ if (fl) {
+ if (fl->fns) {
+ size_t i;
+
+ for (i = 0; i < fl->count; i++) {
+ if (fl->fns[i]) {
+ if (fl->tmp)
+ unlink(fl->fns[i]);
+ sort_free(fl->fns[i]);
+ fl->fns[i] = 0;
+ }
+ }
+ sort_free(fl->fns);
+ fl->fns = NULL;
+ }
+ fl->sz = 0;
+ fl->count = 0;
+ fl->tmp = false;
+ }
+}
+
+/*
+ * Init sort list
+ */
+void
+sort_list_init(struct sort_list *l)
+{
+
+ if (l) {
+ l->count = 0;
+ l->size = 0;
+ l->memsize = sizeof(struct sort_list);
+ l->list = NULL;
+ }
+}
+
+/*
+ * Add string to sort list
+ */
+void
+sort_list_add(struct sort_list *l, struct bwstring *str)
+{
+
+ if (l && str) {
+ size_t indx = l->count;
+
+ if ((l->list == NULL) || (indx >= l->size)) {
+ size_t newsize = (l->size + 1) + 1024;
+
+ l->list = sort_realloc(l->list,
+ sizeof(struct sort_list_item*) * newsize);
+ l->memsize += (newsize - l->size) *
+ sizeof(struct sort_list_item*);
+ l->size = newsize;
+ }
+ l->list[indx] = sort_list_item_alloc();
+ sort_list_item_set(l->list[indx], str);
+ l->memsize += sort_list_item_size(l->list[indx]);
+ l->count += 1;
+ }
+}
+
+/*
+ * Clean sort list data
+ */
+void
+sort_list_clean(struct sort_list *l)
+{
+
+ if (l) {
+ if (l->list) {
+ size_t i;
+
+ for (i = 0; i < l->count; i++) {
+ struct sort_list_item *item;
+
+ item = l->list[i];
+
+ if (item) {
+ sort_list_item_clean(item);
+ sort_free(item);
+ l->list[i] = NULL;
+ }
+ }
+ sort_free(l->list);
+ l->list = NULL;
+ }
+ l->count = 0;
+ l->size = 0;
+ l->memsize = sizeof(struct sort_list);
+ }
+}
+
+/*
+ * Write sort list to file
+ */
+void
+sort_list_dump(struct sort_list *l, const char *fn)
+{
+
+ if (l && fn) {
+ FILE *f;
+
+ f = openfile(fn, "w");
+ if (f == NULL)
+ err(2, NULL);
+
+ if (l->list) {
+ size_t i;
+ if (!(sort_opts_vals.uflag)) {
+ for (i = 0; i < l->count; ++i)
+ bwsfwrite(l->list[i]->str, f,
+ sort_opts_vals.zflag);
+ } else {
+ struct sort_list_item *last_printed_item = NULL;
+ struct sort_list_item *item;
+ for (i = 0; i < l->count; ++i) {
+ item = l->list[i];
+ if ((last_printed_item == NULL) ||
+ list_coll(&last_printed_item, &item)) {
+ bwsfwrite(item->str, f, sort_opts_vals.zflag);
+ last_printed_item = item;
+ }
+ }
+ }
+ }
+
+ closefile(f, fn);
+ }
+}
+
+/*
+ * Checks if the given file is sorted. Stops at the first disorder,
+ * prints the disordered line and returns 1.
+ */
+int
+check(const char *fn)
+{
+ struct bwstring *s1, *s2, *s1disorder, *s2disorder;
+ struct file_reader *fr;
+ struct keys_array *ka1, *ka2;
+ int res;
+ size_t pos, posdisorder;
+
+ s1 = s2 = s1disorder = s2disorder = NULL;
+ ka1 = ka2 = NULL;
+
+ fr = file_reader_init(fn);
+
+ res = 0;
+ pos = 1;
+ posdisorder = 1;
+
+ if (fr == NULL) {
+ err(2, NULL);
+#ifndef __APPLE__
+ goto end;
+#endif
+ }
+
+ s1 = file_reader_readline(fr);
+ if (s1 == NULL)
+ goto end;
+
+ ka1 = keys_array_alloc();
+ preproc(s1, ka1);
+
+ s2 = file_reader_readline(fr);
+ if (s2 == NULL)
+ goto end;
+
+ ka2 = keys_array_alloc();
+ preproc(s2, ka2);
+
+ for (;;) {
+
+ if (debug_sort) {
+ bwsprintf(stdout, s2, "s1=<", ">");
+ bwsprintf(stdout, s1, "s2=<", ">");
+ }
+ int cmp = key_coll(ka2, ka1, 0);
+ if (debug_sort)
+ printf("; cmp1=%d", cmp);
+
+ if (!cmp && sort_opts_vals.complex_sort &&
+ !(sort_opts_vals.uflag) && !(sort_opts_vals.sflag)) {
+ cmp = top_level_str_coll(s2, s1);
+ if (debug_sort)
+ printf("; cmp2=%d", cmp);
+ }
+ if (debug_sort)
+ printf("\n");
+
+ if ((sort_opts_vals.uflag && (cmp <= 0)) || (cmp < 0)) {
+ if (!(sort_opts_vals.csilentflag)) {
+ s2disorder = bwsdup(s2);
+ posdisorder = pos;
+ if (debug_sort)
+ s1disorder = bwsdup(s1);
+ }
+ res = 1;
+ goto end;
+ }
+
+ pos++;
+
+ clean_keys_array(s1, ka1);
+ sort_free(ka1);
+ ka1 = ka2;
+ ka2 = NULL;
+
+ bwsfree(s1);
+ s1 = s2;
+
+ s2 = file_reader_readline(fr);
+ if (s2 == NULL)
+ goto end;
+
+ ka2 = keys_array_alloc();
+ preproc(s2, ka2);
+ }
+
+end:
+ if (ka1) {
+ clean_keys_array(s1, ka1);
+ sort_free(ka1);
+ }
+
+ if (s1)
+ bwsfree(s1);
+
+ if (ka2) {
+ clean_keys_array(s2, ka2);
+ sort_free(ka2);
+ }
+
+ if (s2)
+ bwsfree(s2);
+
+ if ((fn == NULL) || (*fn == 0) || (strcmp(fn, "-") == 0)) {
+ for (;;) {
+ s2 = file_reader_readline(fr);
+ if (s2 == NULL)
+ break;
+ bwsfree(s2);
+ }
+ }
+
+ file_reader_free(fr);
+
+ if (s2disorder) {
+ bws_disorder_warnx(s2disorder, fn, posdisorder);
+ if (s1disorder) {
+ bws_disorder_warnx(s1disorder, fn, posdisorder);
+ if (s1disorder != s2disorder)
+ bwsfree(s1disorder);
+ }
+ bwsfree(s2disorder);
+ s1disorder = NULL;
+ s2disorder = NULL;
+ }
+
+ if (res)
+ exit(res);
+
+ return (0);
+}
+
+/*
+ * Opens a file. If the given filename is "-", stdout will be
+ * opened.
+ */
+FILE *
+openfile(const char *fn, const char *mode)
+{
+ FILE *file;
+
+ if (strcmp(fn, "-") == 0) {
+ return ((mode && mode[0] == 'r') ? stdin : stdout);
+ } else {
+ mode_t orig_file_mask = 0;
+ int is_tmp = file_is_tmp(fn);
+
+ if (is_tmp && (mode[0] == 'w'))
+ orig_file_mask = umask(S_IWGRP | S_IWOTH |
+ S_IRGRP | S_IROTH);
+
+ if (is_tmp && (compress_program != NULL)) {
+ char *cmd;
+ size_t cmdsz;
+
+ cmdsz = strlen(fn) + 128;
+ cmd = sort_malloc(cmdsz);
+
+ fflush(stdout);
+
+ if (mode[0] == 'r')
+ snprintf(cmd, cmdsz - 1, "cat %s | %s -d",
+ fn, compress_program);
+ else if (mode[0] == 'w')
+ snprintf(cmd, cmdsz - 1, "%s > %s",
+ compress_program, fn);
+ else
+ err(2, "%s", getstr(7));
+
+ if ((file = popen(cmd, mode)) == NULL)
+ err(2, NULL);
+
+ sort_free(cmd);
+
+ } else
+ if ((file = fopen(fn, mode)) == NULL)
+ err(2, NULL);
+
+ if (is_tmp && (mode[0] == 'w'))
+ umask(orig_file_mask);
+ }
+
+ return (file);
+}
+
+/*
+ * Close file
+ */
+void
+closefile(FILE *f, const char *fn)
+{
+ if (f == NULL) {
+ ;
+ } else if (f == stdin) {
+ ;
+ } else if (f == stdout) {
+ fflush(f);
+ } else {
+ if (file_is_tmp(fn) && compress_program != NULL) {
+ if(pclose(f)<0)
+ err(2,NULL);
+ } else
+ fclose(f);
+ }
+}
+
+/*
+ * Reads a file into the internal buffer.
+ */
+struct file_reader *
+file_reader_init(const char *fsrc)
+{
+ struct file_reader *ret;
+
+ if (fsrc == NULL)
+ fsrc = "-";
+
+ ret = sort_malloc(sizeof(struct file_reader));
+ memset(ret, 0, sizeof(struct file_reader));
+
+ ret->elsymb = '\n';
+ if (sort_opts_vals.zflag)
+ ret->elsymb = 0;
+
+ ret->fname = sort_strdup(fsrc);
+
+ if (strcmp(fsrc, "-") && (compress_program == NULL) && use_mmap) {
+
+ do {
+ struct stat stat_buf;
+ void *addr;
+ size_t sz = 0;
+ int fd, flags;
+
+#if defined(__APPLE__)
+ flags = 0;
+#else
+ flags = MAP_NOCORE | MAP_NOSYNC;
+#endif
+
+ fd = open(fsrc, O_RDONLY);
+ if (fd < 0)
+ err(2, NULL);
+
+ if (fstat(fd, &stat_buf) < 0) {
+ close(fd);
+ break;
+ }
+
+ sz = stat_buf.st_size;
+
+#if defined(MAP_PREFAULT_READ)
+ flags |= MAP_PREFAULT_READ;
+#endif
+
+ addr = mmap(NULL, sz, PROT_READ, flags, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ break;
+ }
+
+ ret->fd = fd;
+ ret->mmapaddr = addr;
+ ret->mmapsize = sz;
+ ret->mmapptr = ret->mmapaddr;
+
+ } while (0);
+ }
+
+ if (ret->mmapaddr == NULL) {
+ ret->file = openfile(fsrc, "r");
+ if (ret->file == NULL)
+ err(2, NULL);
+
+ if (strcmp(fsrc, "-")) {
+ ret->cbsz = READ_CHUNK;
+ ret->buffer = sort_malloc(ret->cbsz);
+ ret->bsz = 0;
+ ret->strbeg = 0;
+
+ ret->bsz = fread(ret->buffer, 1, ret->cbsz, ret->file);
+ if (ret->bsz == 0) {
+ if (ferror(ret->file))
+ err(2, NULL);
+ }
+ }
+ }
+
+ return (ret);
+}
+
+struct bwstring *
+file_reader_readline(struct file_reader *fr)
+{
+ struct bwstring *ret = NULL;
+
+ if (fr->mmapaddr) {
+ unsigned char *mmapend;
+
+ mmapend = fr->mmapaddr + fr->mmapsize;
+ if (fr->mmapptr >= mmapend)
+ return (NULL);
+ else {
+ unsigned char *strend;
+ size_t sz;
+
+ sz = mmapend - fr->mmapptr;
+ strend = memchr(fr->mmapptr, fr->elsymb, sz);
+
+ if (strend == NULL) {
+ ret = bwscsbdup(fr->mmapptr, sz);
+ fr->mmapptr = mmapend;
+ } else {
+ ret = bwscsbdup(fr->mmapptr, strend -
+ fr->mmapptr);
+ fr->mmapptr = strend + 1;
+ }
+ }
+
+ } else if (fr->file != stdin) {
+ unsigned char *strend;
+ size_t bsz1, remsz, search_start;
+
+ search_start = 0;
+ remsz = 0;
+ strend = NULL;
+
+ if (fr->bsz > fr->strbeg)
+ remsz = fr->bsz - fr->strbeg;
+
+ /* line read cycle */
+ for (;;) {
+ if (remsz > search_start)
+ strend = memchr(fr->buffer + fr->strbeg +
+ search_start, fr->elsymb, remsz -
+ search_start);
+ else
+ strend = NULL;
+
+ if (strend)
+ break;
+ if (feof(fr->file))
+ break;
+
+ if (fr->bsz != fr->cbsz)
+ /* NOTREACHED */
+ err(2, "File read software error 1");
+
+ if (remsz > (READ_CHUNK >> 1)) {
+ search_start = fr->cbsz - fr->strbeg;
+ fr->cbsz += READ_CHUNK;
+ fr->buffer = sort_realloc(fr->buffer,
+ fr->cbsz);
+ bsz1 = fread(fr->buffer + fr->bsz, 1,
+ READ_CHUNK, fr->file);
+ if (bsz1 == 0) {
+ if (ferror(fr->file))
+ err(2, NULL);
+ break;
+ }
+ fr->bsz += bsz1;
+ remsz += bsz1;
+ } else {
+ if (remsz > 0 && fr->strbeg>0)
+ bcopy(fr->buffer + fr->strbeg,
+ fr->buffer, remsz);
+
+ fr->strbeg = 0;
+ search_start = remsz;
+ bsz1 = fread(fr->buffer + remsz, 1,
+ fr->cbsz - remsz, fr->file);
+ if (bsz1 == 0) {
+ if (ferror(fr->file))
+ err(2, NULL);
+ break;
+ }
+ fr->bsz = remsz + bsz1;
+ remsz = fr->bsz;
+ }
+ }
+
+ if (strend == NULL)
+ strend = fr->buffer + fr->bsz;
+
+ if ((fr->buffer + fr->strbeg <= strend) &&
+ (fr->strbeg < fr->bsz) && (remsz>0))
+ ret = bwscsbdup(fr->buffer + fr->strbeg, strend -
+ fr->buffer - fr->strbeg);
+
+ fr->strbeg = (strend - fr->buffer) + 1;
+
+ } else {
+ size_t len = 0;
+
+ ret = bwsfgetln(fr->file, &len, sort_opts_vals.zflag,
+ &(fr->rb));
+ }
+
+ return (ret);
+}
+
+static void
+file_reader_clean(struct file_reader *fr)
+{
+
+ if (fr) {
+ if (fr->mmapaddr)
+ munmap(fr->mmapaddr, fr->mmapsize);
+
+ if (fr->fd)
+ close(fr->fd);
+
+ if (fr->buffer)
+ sort_free(fr->buffer);
+
+ if (fr->file)
+ if (fr->file != stdin)
+ closefile(fr->file, fr->fname);
+
+ if(fr->fname)
+ sort_free(fr->fname);
+
+ memset(fr, 0, sizeof(struct file_reader));
+ }
+}
+
+void
+file_reader_free(struct file_reader *fr)
+{
+
+ if (fr) {
+ file_reader_clean(fr);
+ sort_free(fr);
+ }
+}
+
+int
+procfile(const char *fsrc, struct sort_list *list, struct file_list *fl)
+{
+ struct file_reader *fr;
+
+ fr = file_reader_init(fsrc);
+ if (fr == NULL)
+ err(2, NULL);
+
+ /* file browse cycle */
+ for (;;) {
+ struct bwstring *bws;
+
+ bws = file_reader_readline(fr);
+
+ if (bws == NULL)
+ break;
+
+ sort_list_add(list, bws);
+
+ if (list->memsize >= available_free_memory) {
+ char *fn;
+
+ fn = new_tmp_file_name();
+ sort_list_to_file(list, fn);
+ file_list_add(fl, fn, false);
+ sort_list_clean(list);
+ }
+ }
+
+ file_reader_free(fr);
+
+ return (0);
+}
+
+/*
+ * Compare file headers. Files with EOF always go to the end of the list.
+ */
+static int
+file_header_cmp(struct file_header *f1, struct file_header *f2)
+{
+
+ if (f1 == f2)
+ return (0);
+ else {
+ if (f1->fr == NULL) {
+ return ((f2->fr == NULL) ? 0 : +1);
+ } else if (f2->fr == NULL)
+ return (-1);
+ else {
+ int ret;
+
+ ret = list_coll(&(f1->si), &(f2->si));
+ if (!ret)
+ return ((f1->file_pos < f2->file_pos) ? -1 : +1);
+ return (ret);
+ }
+ }
+}
+
+/*
+ * Allocate and init file header structure
+ */
+static void
+file_header_init(struct file_header **fh, const char *fn, size_t file_pos)
+{
+
+ if (fh && fn) {
+ struct bwstring *line;
+
+ *fh = sort_malloc(sizeof(struct file_header));
+ (*fh)->file_pos = file_pos;
+ (*fh)->fr = file_reader_init(fn);
+ if ((*fh)->fr == NULL) {
+ perror(fn);
+ err(2, "%s", getstr(8));
+ }
+ line = file_reader_readline((*fh)->fr);
+ if (line == NULL) {
+ file_reader_free((*fh)->fr);
+ (*fh)->fr = NULL;
+ (*fh)->si = NULL;
+ } else {
+ (*fh)->si = sort_list_item_alloc();
+ sort_list_item_set((*fh)->si, line);
+ }
+ }
+}
+
+/*
+ * Close file
+ */
+static void
+file_header_close(struct file_header **fh)
+{
+
+ if (fh && *fh) {
+ if ((*fh)->fr) {
+ file_reader_free((*fh)->fr);
+ (*fh)->fr = NULL;
+ }
+ if ((*fh)->si) {
+ sort_list_item_clean((*fh)->si);
+ sort_free((*fh)->si);
+ (*fh)->si = NULL;
+ }
+ sort_free(*fh);
+ *fh = NULL;
+ }
+}
+
+/*
+ * Swap two array elements
+ */
+static void
+file_header_swap(struct file_header **fh, size_t i1, size_t i2)
+{
+ struct file_header *tmp;
+
+ tmp = fh[i1];
+ fh[i1] = fh[i2];
+ fh[i2] = tmp;
+}
+
+/* heap algorithm ==>> */
+
+/*
+ * See heap sort algorithm
+ * "Raises" last element to its right place
+ */
+static void
+file_header_heap_swim(struct file_header **fh, size_t indx)
+{
+
+ if (indx > 0) {
+ size_t parent_index;
+
+ parent_index = (indx - 1) >> 1;
+
+ if (file_header_cmp(fh[indx], fh[parent_index]) < 0) {
+ /* swap child and parent and continue */
+ file_header_swap(fh, indx, parent_index);
+ file_header_heap_swim(fh, parent_index);
+ }
+ }
+}
+
+/*
+ * Sink the top element to its correct position
+ */
+static void
+file_header_heap_sink(struct file_header **fh, size_t indx, size_t size)
+{
+ size_t left_child_index;
+ size_t right_child_index;
+
+ left_child_index = indx + indx + 1;
+ right_child_index = left_child_index + 1;
+
+ if (left_child_index < size) {
+ size_t min_child_index;
+
+ min_child_index = left_child_index;
+
+ if ((right_child_index < size) &&
+ (file_header_cmp(fh[left_child_index],
+ fh[right_child_index]) > 0))
+ min_child_index = right_child_index;
+ if (file_header_cmp(fh[indx], fh[min_child_index]) > 0) {
+ file_header_swap(fh, indx, min_child_index);
+ file_header_heap_sink(fh, min_child_index, size);
+ }
+ }
+}
+
+/* <<== heap algorithm */
+
+/*
+ * Adds element to the "left" end
+ */
+static void
+file_header_list_rearrange_from_header(struct file_header **fh, size_t size)
+{
+
+ file_header_heap_sink(fh, 0, size);
+}
+
+/*
+ * Adds element to the "right" end
+ */
+static void
+file_header_list_push(struct file_header *f, struct file_header **fh, size_t size)
+{
+
+ fh[size++] = f;
+ file_header_heap_swim(fh, size - 1);
+}
+
+struct last_printed
+{
+ struct bwstring *str;
+};
+
+/*
+ * Prints the current line of the file
+ */
+static void
+file_header_print(struct file_header *fh, FILE *f_out, struct last_printed *lp)
+{
+
+ if (fh && fh->fr && f_out && fh->si && fh->si->str) {
+ if (sort_opts_vals.uflag) {
+ if ((lp->str == NULL) || (str_list_coll(lp->str, &(fh->si)))) {
+ bwsfwrite(fh->si->str, f_out, sort_opts_vals.zflag);
+ if (lp->str)
+ bwsfree(lp->str);
+ lp->str = bwsdup(fh->si->str);
+ }
+ } else
+ bwsfwrite(fh->si->str, f_out, sort_opts_vals.zflag);
+ }
+}
+
+/*
+ * Read next line
+ */
+static void
+file_header_read_next(struct file_header *fh)
+{
+
+ if (fh && fh->fr) {
+ struct bwstring *tmp;
+
+ tmp = file_reader_readline(fh->fr);
+ if (tmp == NULL) {
+ file_reader_free(fh->fr);
+ fh->fr = NULL;
+ if (fh->si) {
+ sort_list_item_clean(fh->si);
+ sort_free(fh->si);
+ fh->si = NULL;
+ }
+ } else {
+ if (fh->si == NULL)
+ fh->si = sort_list_item_alloc();
+ sort_list_item_set(fh->si, tmp);
+ }
+ }
+}
+
+/*
+ * Merge array of "files headers"
+ */
+static void
+file_headers_merge(size_t fnum, struct file_header **fh, FILE *f_out)
+{
+ struct last_printed lp;
+ size_t i;
+
+ memset(&lp, 0, sizeof(lp));
+
+ /*
+ * construct the initial sort structure
+ */
+ for (i = 0; i < fnum; i++)
+ file_header_list_push(fh[i], fh, i);
+
+ while (fh[0]->fr) { /* unfinished files are always in front */
+ /* output the smallest line: */
+ file_header_print(fh[0], f_out, &lp);
+ /* read a new line, if possible: */
+ file_header_read_next(fh[0]);
+ /* re-arrange the list: */
+ file_header_list_rearrange_from_header(fh, fnum);
+ }
+
+ if (lp.str)
+ bwsfree(lp.str);
+}
+
+/*
+ * Merges the given files into the output file, which can be
+ * stdout.
+ */
+static void
+merge_files_array(size_t argc, char **argv, const char *fn_out)
+{
+
+ if (argv && fn_out) {
+ struct file_header **fh;
+ FILE *f_out;
+ size_t i;
+
+ f_out = openfile(fn_out, "w");
+
+ if (f_out == NULL)
+ err(2, NULL);
+
+ fh = sort_malloc((argc + 1) * sizeof(struct file_header *));
+
+ for (i = 0; i < argc; i++)
+ file_header_init(fh + i, argv[i], (size_t) i);
+
+ file_headers_merge(argc, fh, f_out);
+
+ for (i = 0; i < argc; i++)
+ file_header_close(fh + i);
+
+ sort_free(fh);
+
+ closefile(f_out, fn_out);
+ }
+}
+
+/*
+ * Shrinks the file list until its size smaller than max number of opened files
+ */
+static int
+shrink_file_list(struct file_list *fl)
+{
+
+ if ((fl == NULL) || (size_t) (fl->count) < max_open_files)
+ return (0);
+ else {
+ struct file_list new_fl;
+ size_t indx = 0;
+
+ file_list_init(&new_fl, true);
+ while (indx < fl->count) {
+ char *fnew;
+ size_t num;
+
+ num = fl->count - indx;
+ fnew = new_tmp_file_name();
+
+ if ((size_t) num >= max_open_files)
+ num = max_open_files - 1;
+ merge_files_array(num, fl->fns + indx, fnew);
+ if (fl->tmp) {
+ size_t i;
+
+ for (i = 0; i < num; i++)
+ unlink(fl->fns[indx + i]);
+ }
+ file_list_add(&new_fl, fnew, false);
+ indx += num;
+ }
+ fl->tmp = false; /* already taken care of */
+ file_list_clean(fl);
+
+ fl->count = new_fl.count;
+ fl->fns = new_fl.fns;
+ fl->sz = new_fl.sz;
+ fl->tmp = new_fl.tmp;
+
+ return (1);
+ }
+}
+
+/*
+ * Merge list of files
+ */
+void
+merge_files(struct file_list *fl, const char *fn_out)
+{
+
+ if (fl && fn_out) {
+ while (shrink_file_list(fl));
+
+ merge_files_array(fl->count, fl->fns, fn_out);
+ }
+}
+
+static const char *
+get_sort_method_name(int sm)
+{
+
+ if (sm == SORT_MERGESORT)
+ return "mergesort";
+ else if (sort_opts_vals.sort_method == SORT_RADIXSORT)
+ return "radixsort";
+ else if (sort_opts_vals.sort_method == SORT_HEAPSORT)
+ return "heapsort";
+ else
+ return "quicksort";
+}
+
+/*
+ * Wrapper for qsort
+ */
+static int sort_qsort(void *list, size_t count, size_t elem_size,
+ int (*cmp_func)(const void *, const void *))
+{
+
+ qsort(list, count, elem_size, cmp_func);
+ return (0);
+}
+
+/*
+ * Sort list of lines and writes it to the file
+ */
+void
+sort_list_to_file(struct sort_list *list, const char *outfile)
+{
+ struct sort_mods *sm = &(keys[0].sm);
+
+ if (!(sm->Mflag) && !(sm->Rflag) && !(sm->Vflag) && !(sm->Vflag) &&
+ !(sm->gflag) && !(sm->hflag) && !(sm->nflag)) {
+ if ((sort_opts_vals.sort_method == SORT_DEFAULT) && byte_sort)
+ sort_opts_vals.sort_method = SORT_RADIXSORT;
+
+ } else if (sort_opts_vals.sort_method == SORT_RADIXSORT)
+ err(2, "%s", getstr(9));
+
+ /*
+ * to handle stable sort and the unique cases in the
+ * right order, we need stable basic algorithm
+ */
+ if (sort_opts_vals.sflag) {
+ switch (sort_opts_vals.sort_method){
+ case SORT_MERGESORT:
+ break;
+ case SORT_RADIXSORT:
+ break;
+ case SORT_DEFAULT:
+ sort_opts_vals.sort_method = SORT_MERGESORT;
+ break;
+ default:
+ errx(2, "%s", getstr(10));
+ }
+ }
+
+ if (sort_opts_vals.sort_method == SORT_DEFAULT)
+ sort_opts_vals.sort_method = DEFAULT_SORT_ALGORITHM;
+
+ if (debug_sort)
+ printf("sort_method=%s\n",
+ get_sort_method_name(sort_opts_vals.sort_method));
+
+ switch (sort_opts_vals.sort_method){
+ case SORT_RADIXSORT:
+ rxsort(list->list, list->count);
+ sort_list_dump(list, outfile);
+ break;
+ case SORT_MERGESORT:
+ mt_sort(list, mergesort, outfile);
+ break;
+ case SORT_HEAPSORT:
+ mt_sort(list, heapsort, outfile);
+ break;
+ case SORT_QSORT:
+ mt_sort(list, sort_qsort, outfile);
+ break;
+ default:
+ mt_sort(list, DEFAULT_SORT_FUNC, outfile);
+ break;
+ }
+}
+
+/******************* MT SORT ************************/
+
+#if defined(SORT_THREADS)
+/* semaphore to count threads */
+#ifndef __APPLE__
+static sem_t mtsem;
+#else
+static semaphore_t mtsem;
+#endif
+
+/* current system sort function */
+static int (*g_sort_func)(void *, size_t, size_t,
+ int(*)(const void *, const void *));
+
+/*
+ * Sort cycle thread (in multi-threaded mode)
+ */
+static void*
+mt_sort_thread(void* arg)
+{
+ struct sort_list *list = arg;
+
+ g_sort_func(list->list, list->count, sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+
+#ifndef __APPLE__
+ sem_post(&mtsem);
+#else
+ semaphore_signal(mtsem);
+#endif
+
+ return (arg);
+}
+
+/*
+ * Compare sub-lists. Empty sub-lists always go to the end of the list.
+ */
+static int
+sub_list_cmp(struct sort_list *l1, struct sort_list *l2)
+{
+
+ if (l1 == l2)
+ return (0);
+ else {
+ if (l1->count == 0) {
+ return ((l2->count == 0) ? 0 : +1);
+ } else if (l2->count == 0) {
+ return (-1);
+ } else {
+ int ret;
+
+ ret = list_coll(&(l1->list[0]), &(l2->list[0]));
+ if (!ret)
+ return ((l1->sub_list_pos < l2->sub_list_pos) ?
+ -1 : +1);
+ return (ret);
+ }
+ }
+}
+
+/*
+ * Swap two array elements
+ */
+static void
+sub_list_swap(struct sort_list **sl, size_t i1, size_t i2)
+{
+ struct sort_list *tmp;
+
+ tmp = sl[i1];
+ sl[i1] = sl[i2];
+ sl[i2] = tmp;
+}
+
+/* heap algorithm ==>> */
+
+/*
+ * See heap sort algorithm
+ * "Raises" last element to its right place
+ */
+static void
+sub_list_swim(struct sort_list **sl, size_t indx)
+{
+
+ if (indx > 0) {
+ size_t parent_index;
+
+ parent_index = (indx - 1) >> 1;
+
+ if (sub_list_cmp(sl[indx], sl[parent_index]) < 0) {
+ /* swap child and parent and continue */
+ sub_list_swap(sl, indx, parent_index);
+ sub_list_swim(sl, parent_index);
+ }
+ }
+}
+
+/*
+ * Sink the top element to its correct position
+ */
+static void
+sub_list_sink(struct sort_list **sl, size_t indx, size_t size)
+{
+ size_t left_child_index;
+ size_t right_child_index;
+
+ left_child_index = indx + indx + 1;
+ right_child_index = left_child_index + 1;
+
+ if (left_child_index < size) {
+ size_t min_child_index;
+
+ min_child_index = left_child_index;
+
+ if ((right_child_index < size) &&
+ (sub_list_cmp(sl[left_child_index],
+ sl[right_child_index]) > 0))
+ min_child_index = right_child_index;
+ if (sub_list_cmp(sl[indx], sl[min_child_index]) > 0) {
+ sub_list_swap(sl, indx, min_child_index);
+ sub_list_sink(sl, min_child_index, size);
+ }
+ }
+}
+
+/* <<== heap algorithm */
+
+/*
+ * Adds element to the "right" end
+ */
+static void
+sub_list_push(struct sort_list *s, struct sort_list **sl, size_t size)
+{
+
+ sl[size++] = s;
+ sub_list_swim(sl, size - 1);
+}
+
+struct last_printed_item
+{
+ struct sort_list_item *item;
+};
+
+/*
+ * Prints the current line of the file
+ */
+static void
+sub_list_header_print(struct sort_list *sl, FILE *f_out,
+ struct last_printed_item *lp)
+{
+
+ if (sl && sl->count && f_out && sl->list[0]->str) {
+ if (sort_opts_vals.uflag) {
+ if ((lp->item == NULL) || (list_coll(&(lp->item),
+ &(sl->list[0])))) {
+ bwsfwrite(sl->list[0]->str, f_out,
+ sort_opts_vals.zflag);
+ lp->item = sl->list[0];
+ }
+ } else
+ bwsfwrite(sl->list[0]->str, f_out,
+ sort_opts_vals.zflag);
+ }
+}
+
+/*
+ * Read next line
+ */
+static void
+sub_list_next(struct sort_list *sl)
+{
+
+ if (sl && sl->count) {
+ sl->list += 1;
+ sl->count -= 1;
+ }
+}
+
+/*
+ * Merge sub-lists to a file
+ */
+static void
+merge_sub_lists(struct sort_list **sl, size_t n, FILE* f_out)
+{
+ struct last_printed_item lp;
+ size_t i;
+
+ memset(&lp,0,sizeof(lp));
+
+ /* construct the initial list: */
+ for (i = 0; i < n; i++)
+ sub_list_push(sl[i], sl, i);
+
+ while (sl[0]->count) { /* unfinished lists are always in front */
+ /* output the smallest line: */
+ sub_list_header_print(sl[0], f_out, &lp);
+ /* move to a new line, if possible: */
+ sub_list_next(sl[0]);
+ /* re-arrange the list: */
+ sub_list_sink(sl, 0, n);
+ }
+}
+
+/*
+ * Merge sub-lists to a file
+ */
+static void
+merge_list_parts(struct sort_list **parts, size_t n, const char *fn)
+{
+ FILE* f_out;
+
+ f_out = openfile(fn,"w");
+
+ merge_sub_lists(parts, n, f_out);
+
+ closefile(f_out, fn);
+}
+
+#endif /* defined(SORT_THREADS) */
+/*
+ * Multi-threaded sort algorithm "driver"
+ */
+static void
+mt_sort(struct sort_list *list,
+ int(*sort_func)(void *, size_t, size_t, int(*)(const void *, const void *)),
+ const char* fn)
+{
+#if defined(SORT_THREADS)
+ if (nthreads < 2 || list->count < MT_SORT_THRESHOLD) {
+ size_t nthreads_save = nthreads;
+ nthreads = 1;
+#endif
+ /* if single thread or small data, do simple sort */
+ sort_func(list->list, list->count,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+ sort_list_dump(list, fn);
+#if defined(SORT_THREADS)
+ nthreads = nthreads_save;
+ } else {
+ /* multi-threaded sort */
+ struct sort_list **parts;
+ size_t avgsize, cstart, i;
+
+ /* array of sub-lists */
+ parts = sort_malloc(sizeof(struct sort_list*) * nthreads);
+ cstart = 0;
+ avgsize = list->count / nthreads;
+
+ /* set global system sort function */
+ g_sort_func = sort_func;
+
+ /* set sublists */
+ for (i = 0; i < nthreads; ++i) {
+ size_t sz = 0;
+
+ parts[i] = sort_malloc(sizeof(struct sort_list));
+ parts[i]->list = list->list + cstart;
+ parts[i]->memsize = 0;
+ parts[i]->sub_list_pos = i;
+
+ sz = (i == nthreads - 1) ? list->count - cstart :
+ avgsize;
+
+ parts[i]->count = sz;
+
+ parts[i]->size = parts[i]->count;
+
+ cstart += sz;
+ }
+
+ /* init threads counting semaphore */
+#ifndef __APPLE__
+ sem_init(&mtsem, 0, 0);
+#else
+ {
+ mach_port_t self = mach_task_self();
+ kern_return_t ret = semaphore_create(self, &mtsem, SYNC_POLICY_FIFO, 0);
+ if (ret != KERN_SUCCESS) {
+ err(2,NULL);
+ }
+ }
+#endif
+
+ /* start threads */
+ for (i = 0; i < nthreads; ++i) {
+ pthread_t pth;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+#ifndef __APPLE__
+ pthread_attr_setdetachstate(&attr, PTHREAD_DETACHED);
+#else
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+#endif
+
+ for (;;) {
+ int res = pthread_create(&pth, &attr,
+ mt_sort_thread, parts[i]);
+
+ if (res >= 0)
+ break;
+ if (errno == EAGAIN) {
+#ifndef __APPLE__
+ pthread_yield();
+#else
+ sched_yield();
+#endif
+ continue;
+ }
+ err(2, NULL);
+ }
+
+ pthread_attr_destroy(&attr);
+ }
+
+ /* wait for threads completion */
+ for (i = 0; i < nthreads; ++i) {
+#ifndef __APPLE__
+ sem_wait(&mtsem);
+#else
+ semaphore_wait(mtsem);
+#endif
+ }
+ /* destroy the semaphore - we do not need it anymore */
+#ifndef __APPLE__
+ sem_destroy(&mtsem);
+#else
+ {
+ mach_port_t self = mach_task_self();
+ semaphore_destroy(self,mtsem);
+ }
+#endif
+
+ /* merge sorted sub-lists to the file */
+ merge_list_parts(parts, nthreads, fn);
+
+ /* free sub-lists data */
+ for (i = 0; i < nthreads; ++i) {
+ sort_free(parts[i]);
+ }
+ sort_free(parts);
+ }
+#endif /* defined(SORT_THREADS) */
+}
diff --git a/text_cmds/sort/file.h b/text_cmds/sort/file.h
new file mode 100644
index 0000000..e7a8c94
--- /dev/null
+++ b/text_cmds/sort/file.h
@@ -0,0 +1,126 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__SORT_FILE_H__)
+#define __SORT_FILE_H__
+
+#include "coll.h"
+#include "sort.h"
+
+#define SORT_DEFAULT 0
+#define SORT_QSORT 1
+#define SORT_MERGESORT 2
+#define SORT_HEAPSORT 3
+#define SORT_RADIXSORT 4
+
+#define DEFAULT_SORT_ALGORITHM SORT_MERGESORT
+#define DEFAULT_SORT_FUNC mergesort
+
+/*
+ * List of data to be sorted.
+ */
+struct sort_list
+{
+ struct sort_list_item **list;
+ unsigned long long memsize;
+ size_t count;
+ size_t size;
+ size_t sub_list_pos;
+};
+
+/*
+ * File reader object
+ */
+struct file_reader;
+
+/*
+ * List of files to be sorted
+ */
+struct file_list
+{
+ char **fns;
+ size_t count;
+ size_t sz;
+ bool tmp;
+};
+
+/* memory */
+
+/**/
+
+extern unsigned long long free_memory;
+extern unsigned long long available_free_memory;
+
+/* Are we using mmap ? */
+extern bool use_mmap;
+
+/* temporary file dir */
+
+extern const char *tmpdir;
+
+/*
+ * Max number of simultaneously open files (including the output file).
+ */
+extern size_t max_open_files;
+
+/*
+ * Compress program
+ */
+extern const char* compress_program;
+
+/* funcs */
+
+struct file_reader *file_reader_init(const char *fsrc);
+struct bwstring *file_reader_readline(struct file_reader *fr);
+void file_reader_free(struct file_reader *fr);
+
+void init_tmp_files(void);
+void clear_tmp_files(void);
+char *new_tmp_file_name(void);
+void tmp_file_atexit(const char *tmp_file);
+
+void file_list_init(struct file_list *fl, bool tmp);
+void file_list_add(struct file_list *fl, char *fn, bool allocate);
+void file_list_populate(struct file_list *fl, int argc, char **argv, bool allocate);
+void file_list_clean(struct file_list *fl);
+
+int check(const char *);
+void merge_files(struct file_list *fl, const char *fn_out);
+FILE *openfile(const char *, const char *);
+void closefile(FILE *, const char *);
+int procfile(const char *fn, struct sort_list *list, struct file_list *fl);
+
+void sort_list_init(struct sort_list *l);
+void sort_list_add(struct sort_list *l, struct bwstring *str);
+void sort_list_clean(struct sort_list *l);
+void sort_list_dump(struct sort_list *l, const char *fn);
+
+void sort_list_to_file(struct sort_list *list, const char *outfile);
+
+#endif /* __SORT_FILE_H__ */
diff --git a/text_cmds/sort/mem.c b/text_cmds/sort/mem.c
new file mode 100644
index 0000000..f59f751
--- /dev/null
+++ b/text_cmds/sort/mem.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.bin/sort/mem.c 281132 2015-04-06 02:35:55Z pfg $");
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mem.h"
+
+/*
+ * malloc() wrapper.
+ */
+void *
+sort_malloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(2, NULL);
+ return (ptr);
+}
+
+/*
+ * free() wrapper.
+ */
+void
+sort_free(const void *ptr)
+{
+
+ if (ptr)
+ free(__DECONST(void *, ptr));
+}
+
+/*
+ * realloc() wrapper.
+ */
+void *
+sort_realloc(void *ptr, size_t size)
+{
+
+ if ((ptr = realloc(ptr, size)) == NULL)
+ err(2, NULL);
+ return (ptr);
+}
+
+char *
+sort_strdup(const char *str)
+{
+ char *dup;
+
+ if ((dup = strdup(str)) == NULL)
+ err(2, NULL);
+ return (dup);
+}
diff --git a/text_cmds/sort/mem.h b/text_cmds/sort/mem.h
new file mode 100644
index 0000000..95ab1ac
--- /dev/null
+++ b/text_cmds/sort/mem.h
@@ -0,0 +1,45 @@
+/* $FreeBSD: head/usr.bin/sort/mem.h 264744 2014-04-21 22:52:18Z pfg $ */
+
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__SORT_MEM_H__)
+#define __SORT_MEM_H__
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/*
+ * mem.c
+ */
+void *sort_malloc(size_t);
+void sort_free(const void *ptr);
+void *sort_realloc(void *, size_t);
+char *sort_strdup(const char *);
+
+#endif /* __SORT_MEM_H__ */
diff --git a/text_cmds/sort/nls/C.msg b/text_cmds/sort/nls/C.msg
new file mode 100644
index 0000000..81300ba
--- /dev/null
+++ b/text_cmds/sort/nls/C.msg
@@ -0,0 +1,16 @@
+$ $FreeBSD: head/usr.bin/sort/nls/C.msg 235434 2012-05-14 09:55:23Z gabor $
+$
+$set 1
+$quote "
+1 "mutually exclusive flags"
+2 "extra argument not allowed with -c"
+3 "Unknown feature"
+4 "Wrong memory buffer specification"
+5 "0 field in key specs"
+6 "0 column in key specs"
+7 "Wrong file mode"
+8 "Cannot open file for reading"
+9 "Radix sort cannot be used with these sort options"
+10 "The chosen sort method cannot be used with stable and/or unique sort"
+11 "Invalid key position"
+12 "Usage: %s [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] [+POS1 [-POS2]] [-S memsize] [-T tmpdir] [-t separator] [-o outfile] [--batch-size size] [--files0-from file] [--heapsort] [--mergesort] [--radixsort] [--qsort] [--nthreads thread_no] [--human-numeric-sort] [--version-sort] [--random-sort [--random-source file]] [--compress-program program] [file ...]\n"
diff --git a/text_cmds/sort/nls/hu_HU.ISO8859-2.msg b/text_cmds/sort/nls/hu_HU.ISO8859-2.msg
new file mode 100644
index 0000000..c45862b
--- /dev/null
+++ b/text_cmds/sort/nls/hu_HU.ISO8859-2.msg
@@ -0,0 +1,16 @@
+$ $FreeBSD: head/usr.bin/sort/nls/hu_HU.ISO8859-2.msg 235434 2012-05-14 09:55:23Z gabor $
+$
+$set 1
+$quote "
+1 "egymást kizáró opciók"
+2 "extra argumentum a -%c opcióval"
+3 "Ismeretlen funkció\n"
+4 "Rossz memória puffer érték"
+5 "0 mezõ a kulcsspecifikációban\n"
+6 "0 oszlop a kulcsspecifikációban\n"
+7 "Helytelen fájl mód"
+8 "A fájl nem nyitható meg olvasásra"
+9 "A radix rendezés nem használható a megadott rendezési opciókkal"
+10 "A választott rendezési mód nem használható a --stable és --unique opciókkal"
+11 "Érvénytelen kulcs pozíció"
+12 "Használat: %s [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] [+POS1 [-POS2]] [-S memóriaméret] [-T ideiglenes_könyvtár] [-t elválasztó] [-o kimeneti_fájl] [--batch-size méret] [--files0-from fájl] [--heapsort] [--mergesort] [--radixsort] [--qsort] [--nthreads szálak_száma] [--human-numeric-sort] [--version-sort] [--random-sort [--random-source fájl]] [--compress-program program] [fájl ...]\n"
diff --git a/text_cmds/sort/radixsort.c b/text_cmds/sort/radixsort.c
new file mode 100644
index 0000000..8288a19
--- /dev/null
+++ b/text_cmds/sort/radixsort.c
@@ -0,0 +1,746 @@
+/*-
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <err.h>
+#include <langinfo.h>
+#include <math.h>
+#if defined(SORT_THREADS)
+#include <pthread.h>
+#ifndef __APPLE__
+#include <semaphore.h>
+#else
+#include <mach/mach_init.h>
+#include <mach/mach_error.h>
+#include <mach/semaphore.h>
+#include <mach/task.h>
+#endif
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <unistd.h>
+
+#include "coll.h"
+#include "radixsort.h"
+
+#define DEFAULT_SORT_FUNC_RADIXSORT mergesort
+
+#define TINY_NODE(sl) ((sl)->tosort_num < 65)
+#define SMALL_NODE(sl) ((sl)->tosort_num < 5)
+
+/* are we sorting in reverse order ? */
+static bool reverse_sort;
+
+/* sort sub-levels array size */
+static const size_t slsz = 256 * sizeof(struct sort_level*);
+
+/* one sort level structure */
+struct sort_level
+{
+ struct sort_level **sublevels;
+ struct sort_list_item **leaves;
+ struct sort_list_item **sorted;
+ struct sort_list_item **tosort;
+ size_t leaves_num;
+ size_t leaves_sz;
+ size_t level;
+ size_t real_sln;
+ size_t start_position;
+ size_t sln;
+ size_t tosort_num;
+ size_t tosort_sz;
+};
+
+/* stack of sort levels ready to be sorted */
+struct level_stack {
+ struct level_stack *next;
+ struct sort_level *sl;
+};
+
+static struct level_stack *g_ls;
+
+#if defined(SORT_THREADS)
+/* stack guarding mutex */
+static pthread_cond_t g_ls_cond;
+static pthread_mutex_t g_ls_mutex;
+
+/* counter: how many items are left */
+static size_t sort_left;
+/* guarding mutex */
+
+/* semaphore to count threads */
+#ifndef __APPLE__
+static sem_t mtsem;
+#else
+semaphore_t rmtsem;
+#endif
+
+/*
+ * Decrement items counter
+ */
+static inline void
+sort_left_dec(size_t n)
+{
+ pthread_mutex_lock(&g_ls_mutex);
+ sort_left -= n;
+ if (sort_left == 0 && nthreads > 1)
+ pthread_cond_broadcast(&g_ls_cond);
+ pthread_mutex_unlock(&g_ls_mutex);
+}
+
+/*
+ * Do we have something to sort ?
+ *
+ * This routine does not need to be locked.
+ */
+static inline bool
+have_sort_left(void)
+{
+ bool ret;
+
+ ret = (sort_left > 0);
+
+ return (ret);
+}
+
+#else
+
+#define sort_left_dec(n)
+
+#endif /* SORT_THREADS */
+
+/*
+ * Push sort level to the stack
+ */
+static inline void
+push_ls(struct sort_level *sl)
+{
+ struct level_stack *new_ls;
+
+ new_ls = sort_malloc(sizeof(struct level_stack));
+ new_ls->sl = sl;
+
+#if defined(SORT_THREADS)
+ if (nthreads > 1)
+ pthread_mutex_lock(&g_ls_mutex);
+#endif
+
+ new_ls->next = g_ls;
+ g_ls = new_ls;
+
+#if defined(SORT_THREADS)
+ if (nthreads > 1)
+ pthread_cond_signal(&g_ls_cond);
+#endif
+
+#if defined(SORT_THREADS)
+ if (nthreads > 1)
+ pthread_mutex_unlock(&g_ls_mutex);
+#endif
+}
+
+/*
+ * Pop sort level from the stack (single-threaded style)
+ */
+static inline struct sort_level*
+pop_ls_st(void)
+{
+ struct sort_level *sl;
+
+ if (g_ls) {
+ struct level_stack *saved_ls;
+
+ sl = g_ls->sl;
+ saved_ls = g_ls;
+ g_ls = g_ls->next;
+ sort_free(saved_ls);
+ } else
+ sl = NULL;
+
+ return (sl);
+}
+
+#if defined(SORT_THREADS)
+
+/*
+ * Pop sort level from the stack (multi-threaded style)
+ */
+static inline struct sort_level*
+pop_ls_mt(void)
+{
+ struct level_stack *saved_ls;
+ struct sort_level *sl;
+
+ pthread_mutex_lock(&g_ls_mutex);
+
+ for (;;) {
+ if (g_ls) {
+ sl = g_ls->sl;
+ saved_ls = g_ls;
+ g_ls = g_ls->next;
+ break;
+ }
+ sl = NULL;
+ saved_ls = NULL;
+
+ if (have_sort_left() == 0)
+ break;
+ pthread_cond_wait(&g_ls_cond, &g_ls_mutex);
+ }
+
+ pthread_mutex_unlock(&g_ls_mutex);
+
+ sort_free(saved_ls);
+
+ return (sl);
+}
+
+#endif /* defined(SORT_THREADS) */
+
+static void
+add_to_sublevel(struct sort_level *sl, struct sort_list_item *item, size_t indx)
+{
+ struct sort_level *ssl;
+
+ ssl = sl->sublevels[indx];
+
+ if (ssl == NULL) {
+ ssl = sort_malloc(sizeof(struct sort_level));
+ memset(ssl, 0, sizeof(struct sort_level));
+
+ ssl->level = sl->level + 1;
+ sl->sublevels[indx] = ssl;
+
+ ++(sl->real_sln);
+ }
+
+ if (++(ssl->tosort_num) > ssl->tosort_sz) {
+ ssl->tosort_sz = ssl->tosort_num + 128;
+ ssl->tosort = sort_realloc(ssl->tosort,
+ sizeof(struct sort_list_item*) * (ssl->tosort_sz));
+ }
+
+ ssl->tosort[ssl->tosort_num - 1] = item;
+}
+
+static inline void
+add_leaf(struct sort_level *sl, struct sort_list_item *item)
+{
+
+ if (++(sl->leaves_num) > sl->leaves_sz) {
+ sl->leaves_sz = sl->leaves_num + 128;
+ sl->leaves = sort_realloc(sl->leaves,
+ (sizeof(struct sort_list_item*) * (sl->leaves_sz)));
+ }
+ sl->leaves[sl->leaves_num - 1] = item;
+}
+
+static inline int
+get_wc_index(struct sort_list_item *sli, size_t level)
+{
+ const struct key_value *kv;
+ const struct bwstring *bws;
+
+ kv = get_key_from_keys_array(&sli->ka, 0);
+ bws = kv->k;
+
+ if ((BWSLEN(bws) > level))
+ return (unsigned char) BWS_GET(bws,level);
+ return (-1);
+}
+
+static void
+place_item(struct sort_level *sl, size_t item)
+{
+ struct sort_list_item *sli;
+ int c;
+
+ sli = sl->tosort[item];
+ c = get_wc_index(sli, sl->level);
+
+ if (c == -1)
+ add_leaf(sl, sli);
+ else
+ add_to_sublevel(sl, sli, c);
+}
+
+static void
+free_sort_level(struct sort_level *sl)
+{
+
+ if (sl) {
+ if (sl->leaves)
+ sort_free(sl->leaves);
+
+ if (sl->level > 0)
+ sort_free(sl->tosort);
+
+ if (sl->sublevels) {
+ struct sort_level *slc;
+ size_t sln;
+
+ sln = sl->sln;
+
+ for (size_t i = 0; i < sln; ++i) {
+ slc = sl->sublevels[i];
+ if (slc)
+ free_sort_level(slc);
+ }
+
+ sort_free(sl->sublevels);
+ }
+
+ sort_free(sl);
+ }
+}
+
+static void
+run_sort_level_next(struct sort_level *sl)
+{
+ struct sort_level *slc;
+ size_t i, sln, tosort_num;
+
+ if (sl->sublevels) {
+ sort_free(sl->sublevels);
+ sl->sublevels = NULL;
+ }
+
+ switch (sl->tosort_num) {
+ case 0:
+ goto end;
+ case (1):
+ sl->sorted[sl->start_position] = sl->tosort[0];
+ sort_left_dec(1);
+ goto end;
+ case (2):
+ if (list_coll_offset(&(sl->tosort[0]), &(sl->tosort[1]),
+ sl->level) > 0) {
+ sl->sorted[sl->start_position++] = sl->tosort[1];
+ sl->sorted[sl->start_position] = sl->tosort[0];
+ } else {
+ sl->sorted[sl->start_position++] = sl->tosort[0];
+ sl->sorted[sl->start_position] = sl->tosort[1];
+ }
+ sort_left_dec(2);
+
+ goto end;
+ default:
+ if (TINY_NODE(sl) || (sl->level > 15)) {
+ listcoll_t func;
+
+ func = get_list_call_func(sl->level);
+
+ sl->leaves = sl->tosort;
+ sl->leaves_num = sl->tosort_num;
+ sl->leaves_sz = sl->leaves_num;
+ sl->leaves = sort_realloc(sl->leaves,
+ (sizeof(struct sort_list_item *) *
+ (sl->leaves_sz)));
+ sl->tosort = NULL;
+ sl->tosort_num = 0;
+ sl->tosort_sz = 0;
+ sl->sln = 0;
+ sl->real_sln = 0;
+ if (sort_opts_vals.sflag) {
+ if (mergesort(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) func) == -1)
+ /* NOTREACHED */
+ err(2, "Radix sort error 3");
+ } else
+ DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) func);
+
+ memcpy(sl->sorted + sl->start_position,
+ sl->leaves, sl->leaves_num *
+ sizeof(struct sort_list_item*));
+
+ sort_left_dec(sl->leaves_num);
+
+ goto end;
+ } else {
+ sl->tosort_sz = sl->tosort_num;
+ sl->tosort = sort_realloc(sl->tosort,
+ sizeof(struct sort_list_item*) * (sl->tosort_sz));
+ }
+ }
+
+ sl->sln = 256;
+ sl->sublevels = sort_malloc(slsz);
+ memset(sl->sublevels, 0, slsz);
+
+ sl->real_sln = 0;
+
+ tosort_num = sl->tosort_num;
+ for (i = 0; i < tosort_num; ++i)
+ place_item(sl, i);
+
+ sort_free(sl->tosort);
+ sl->tosort = NULL;
+ sl->tosort_num = 0;
+ sl->tosort_sz = 0;
+
+ if (sl->leaves_num > 1) {
+ if (keys_num > 1) {
+ if (sort_opts_vals.sflag) {
+ mergesort(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+ } else {
+ DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+ }
+ } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
+ DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll_by_str_only);
+ }
+ }
+
+ sl->leaves_sz = sl->leaves_num;
+ sl->leaves = sort_realloc(sl->leaves, (sizeof(struct sort_list_item *) *
+ (sl->leaves_sz)));
+
+ if (!reverse_sort) {
+ memcpy(sl->sorted + sl->start_position, sl->leaves,
+ sl->leaves_num * sizeof(struct sort_list_item*));
+ sl->start_position += sl->leaves_num;
+ sort_left_dec(sl->leaves_num);
+
+ sort_free(sl->leaves);
+ sl->leaves = NULL;
+ sl->leaves_num = 0;
+ sl->leaves_sz = 0;
+
+ sln = sl->sln;
+
+ for (i = 0; i < sln; ++i) {
+ slc = sl->sublevels[i];
+
+ if (slc) {
+ slc->sorted = sl->sorted;
+ slc->start_position = sl->start_position;
+ sl->start_position += slc->tosort_num;
+ if (SMALL_NODE(slc))
+ run_sort_level_next(slc);
+ else
+ push_ls(slc);
+ sl->sublevels[i] = NULL;
+ }
+ }
+
+ } else {
+ size_t n;
+
+ sln = sl->sln;
+
+ for (i = 0; i < sln; ++i) {
+ n = sln - i - 1;
+ slc = sl->sublevels[n];
+
+ if (slc) {
+ slc->sorted = sl->sorted;
+ slc->start_position = sl->start_position;
+ sl->start_position += slc->tosort_num;
+ if (SMALL_NODE(slc))
+ run_sort_level_next(slc);
+ else
+ push_ls(slc);
+ sl->sublevels[n] = NULL;
+ }
+ }
+
+ memcpy(sl->sorted + sl->start_position, sl->leaves,
+ sl->leaves_num * sizeof(struct sort_list_item*));
+ sort_left_dec(sl->leaves_num);
+ }
+
+end:
+ free_sort_level(sl);
+}
+
+/*
+ * Single-threaded sort cycle
+ */
+static void
+run_sort_cycle_st(void)
+{
+ struct sort_level *slc;
+
+ for (;;) {
+ slc = pop_ls_st();
+ if (slc == NULL) {
+ break;
+ }
+ run_sort_level_next(slc);
+ }
+}
+
+#if defined(SORT_THREADS)
+
+/*
+ * Multi-threaded sort cycle
+ */
+static void
+run_sort_cycle_mt(void)
+{
+ struct sort_level *slc;
+
+ for (;;) {
+ slc = pop_ls_mt();
+ if (slc == NULL)
+ break;
+ run_sort_level_next(slc);
+ }
+}
+
+/*
+ * Sort cycle thread (in multi-threaded mode)
+ */
+static void*
+sort_thread(void* arg)
+{
+
+ run_sort_cycle_mt();
+
+#ifndef __APPLE__
+ sem_post(&mtsem);
+#else
+ semaphore_signal(rmtsem);
+#endif
+
+ return (arg);
+}
+
+#endif /* defined(SORT_THREADS) */
+
+static void
+run_top_sort_level(struct sort_level *sl)
+{
+ struct sort_level *slc;
+
+ reverse_sort = sort_opts_vals.kflag ? keys[0].sm.rflag :
+ default_sort_mods->rflag;
+
+ sl->start_position = 0;
+ sl->sln = 256;
+ sl->sublevels = sort_malloc(slsz);
+ memset(sl->sublevels, 0, slsz);
+
+ for (size_t i = 0; i < sl->tosort_num; ++i)
+ place_item(sl, i);
+
+ if (sl->leaves_num > 1) {
+ if (keys_num > 1) {
+ if (sort_opts_vals.sflag) {
+ mergesort(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+ } else {
+ DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll);
+ }
+ } else if (!sort_opts_vals.sflag && sort_opts_vals.complex_sort) {
+ DEFAULT_SORT_FUNC_RADIXSORT(sl->leaves, sl->leaves_num,
+ sizeof(struct sort_list_item *),
+ (int(*)(const void *, const void *)) list_coll_by_str_only);
+ }
+ }
+
+ if (!reverse_sort) {
+ memcpy(sl->tosort + sl->start_position, sl->leaves,
+ sl->leaves_num * sizeof(struct sort_list_item*));
+ sl->start_position += sl->leaves_num;
+ sort_left_dec(sl->leaves_num);
+
+ for (size_t i = 0; i < sl->sln; ++i) {
+ slc = sl->sublevels[i];
+
+ if (slc) {
+ slc->sorted = sl->tosort;
+ slc->start_position = sl->start_position;
+ sl->start_position += slc->tosort_num;
+ push_ls(slc);
+ sl->sublevels[i] = NULL;
+ }
+ }
+
+ } else {
+ size_t n;
+
+ for (size_t i = 0; i < sl->sln; ++i) {
+
+ n = sl->sln - i - 1;
+ slc = sl->sublevels[n];
+
+ if (slc) {
+ slc->sorted = sl->tosort;
+ slc->start_position = sl->start_position;
+ sl->start_position += slc->tosort_num;
+ push_ls(slc);
+ sl->sublevels[n] = NULL;
+ }
+ }
+
+ memcpy(sl->tosort + sl->start_position, sl->leaves,
+ sl->leaves_num * sizeof(struct sort_list_item*));
+
+ sort_left_dec(sl->leaves_num);
+ }
+
+#if defined(SORT_THREADS)
+ if (nthreads < 2) {
+#endif
+ run_sort_cycle_st();
+#if defined(SORT_THREADS)
+ } else {
+ size_t i;
+
+ for(i = 0; i < nthreads; ++i) {
+ pthread_attr_t attr;
+ pthread_t pth;
+
+ pthread_attr_init(&attr);
+#ifndef __APPLE__
+ pthread_attr_setdetachstate(&attr,
+ PTHREAD_DETACHED);
+#else
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+#endif
+
+ for (;;) {
+ int res = pthread_create(&pth, &attr,
+ sort_thread, NULL);
+ if (res >= 0)
+ break;
+ if (errno == EAGAIN) {
+#ifndef __APPLE__
+ pthread_yield();
+#else
+ sched_yield();
+#endif
+ continue;
+ }
+ err(2, NULL);
+ }
+
+ pthread_attr_destroy(&attr);
+ }
+
+ for(i = 0; i < nthreads; ++i)
+#ifndef __APPLE__
+ sem_wait(&mtsem);
+#else
+ semaphore_wait(rmtsem);
+#endif
+ }
+#endif /* defined(SORT_THREADS) */
+}
+
+static void
+run_sort(struct sort_list_item **base, size_t nmemb)
+{
+ struct sort_level *sl;
+
+#if defined(SORT_THREADS)
+ size_t nthreads_save = nthreads;
+ if (nmemb < MT_SORT_THRESHOLD)
+ nthreads = 1;
+
+ if (nthreads > 1) {
+ pthread_mutexattr_t mattr;
+
+ pthread_mutexattr_init(&mattr);
+#ifndef __APPLE__
+ pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
+#endif
+
+ pthread_mutex_init(&g_ls_mutex, &mattr);
+ pthread_cond_init(&g_ls_cond, NULL);
+
+ pthread_mutexattr_destroy(&mattr);
+
+#ifndef __APPLE__
+ sem_init(&mtsem, 0, 0);
+#else
+ {
+ mach_port_t self = mach_task_self();
+ kern_return_t ret = semaphore_create(self, &rmtsem, SYNC_POLICY_FIFO, 0);
+ if (ret != KERN_SUCCESS) {
+ err(2,NULL);
+ }
+ }
+#endif
+
+ }
+#endif
+
+ sl = sort_malloc(sizeof(struct sort_level));
+ memset(sl, 0, sizeof(struct sort_level));
+
+ sl->tosort = base;
+ sl->tosort_num = nmemb;
+ sl->tosort_sz = nmemb;
+
+#if defined(SORT_THREADS)
+ sort_left = nmemb;
+#endif
+
+ run_top_sort_level(sl);
+
+ free_sort_level(sl);
+
+#if defined(SORT_THREADS)
+ if (nthreads > 1) {
+#ifndef __APPLE__
+ sem_destroy(&mtsem);
+#else
+ {
+ mach_port_t self = mach_task_self();
+ semaphore_destroy(self,rmtsem);
+ }
+#endif
+ pthread_mutex_destroy(&g_ls_mutex);
+ }
+ nthreads = nthreads_save;
+#endif
+}
+
+void
+rxsort(struct sort_list_item **base, size_t nmemb)
+{
+
+ run_sort(base, nmemb);
+}
diff --git a/text_cmds/sort/radixsort.h b/text_cmds/sort/radixsort.h
new file mode 100644
index 0000000..8743728
--- /dev/null
+++ b/text_cmds/sort/radixsort.h
@@ -0,0 +1,38 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__SORT_RADIX_H__)
+#define __SORT_RADIX_H__
+
+#include "coll.h"
+#include "sort.h"
+
+void rxsort(struct sort_list_item **base, size_t nmemb);
+
+#endif /* __SORT_RADIX_H__ */
diff --git a/text_cmds/sort/sort.1.in b/text_cmds/sort/sort.1.in
new file mode 100644
index 0000000..a2234e8
--- /dev/null
+++ b/text_cmds/sort/sort.1.in
@@ -0,0 +1,639 @@
+.\" $OpenBSD: sort.1,v 1.45 2015/03/19 13:51:10 jmc Exp $
+.\" $FreeBSD: head/usr.bin/sort/sort.1.in 305613 2016-09-08 14:50:23Z gabor $
+.\"
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sort.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd March 19, 2015
+.Dt SORT 1
+.Os
+.Sh NAME
+.Nm sort
+.Nd sort or merge records (lines) of text and binary files
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl bcCdfghiRMmnrsuVz
+.Sm off
+.Op Fl k\ \& Ar field1 Op , Ar field2
+.Sm on
+.Op Fl S Ar memsize
+.Ek
+.Op Fl T Ar dir
+.Op Fl t Ar char
+.Op Fl o Ar output
+.Op Ar file ...
+.Nm
+.Fl Fl help
+.Nm
+.Fl Fl version
+.Sh DESCRIPTION
+The
+.Nm
+utility sorts text and binary files by lines.
+A line is a record separated from the subsequent record by a
+newline (default) or NUL \'\\0\' character (-z option).
+A record can contain any printable or unprintable characters.
+Comparisons are based on one or more sort keys extracted from
+each line of input, and are performed lexicographically,
+according to the current locale's collating rules and the
+specified command-line options that can tune the actual
+sorting behavior.
+By default, if keys are not given,
+.Nm
+uses entire lines for comparison.
+.Pp
+The command line options are as follows:
+.Bl -tag -width Ds
+.It Fl c , Fl Fl check , Fl C , Fl Fl check=silent|quiet
+Check that the single input file is sorted.
+If the file is not sorted,
+.Nm
+produces the appropriate error messages and exits with code 1,
+otherwise returns 0.
+If
+.Fl C
+or
+.Fl Fl check=silent
+is specified,
+.Nm
+produces no output.
+This is a "silent" version of
+.Fl c .
+.It Fl m , Fl Fl merge
+Merge only.
+The input files are assumed to be pre-sorted.
+If they are not sorted the output order is undefined.
+.It Fl o Ar output , Fl Fl output Ns = Ns Ar output
+Print the output to the
+.Ar output
+file instead of the standard output.
+.It Fl S Ar size , Fl Fl buffer-size Ns = Ns Ar size
+Use
+.Ar size
+for the maximum size of the memory buffer.
+Size modifiers %,b,K,M,G,T,P,E,Z,Y can be used.
+If a memory limit is not explicitly specified,
+.Nm
+takes up to about 90% of available memory.
+If the file size is too big to fit into the memory buffer,
+the temporary disk files are used to perform the sorting.
+.It Fl T Ar dir , Fl Fl temporary-directory Ns = Ns Ar dir
+Store temporary files in the directory
+.Ar dir .
+The default path is the value of the environment variable
+.Ev TMPDIR
+or
+.Pa /var/tmp
+if
+.Ev TMPDIR
+is not defined.
+.It Fl u , Fl Fl unique
+Unique keys.
+Suppress all lines that have a key that is equal to an already
+processed one.
+This option, similarly to
+.Fl s ,
+implies a stable sort.
+If used with
+.Fl c
+or
+.Fl C ,
+.Nm
+also checks that there are no lines with duplicate keys.
+.It Fl s
+Stable sort.
+This option maintains the original record order of records that have
+an equal key.
+This is a non-standard feature, but it is widely accepted and used.
+.It Fl Fl version
+Print the version and silently exits.
+.It Fl Fl help
+Print the help text and silently exits.
+.El
+.Pp
+The following options override the default ordering rules.
+When ordering options appear independently of key field
+specifications, they apply globally to all sort keys.
+When attached to a specific key (see
+.Fl k ) ,
+the ordering options override all global ordering options for
+the key they are attached to.
+.Bl -tag -width indent
+.It Fl b , Fl Fl ignore-leading-blanks
+Ignore leading blank characters when comparing lines.
+.It Fl d , Fl Fl dictionary-order
+Consider only blank spaces and alphanumeric characters in comparisons.
+.It Fl f , Fl Fl ignore-case
+Convert all lowercase characters to their uppercase equivalent
+before comparison, that is, perform case-independent sorting.
+.It Fl g , Fl Fl general-numeric-sort , Fl Fl sort=general-numeric
+Sort by general numerical value.
+As opposed to
+.Fl n ,
+this option handles general floating points.
+It has a more
+permissive format than that allowed by
+.Fl n
+but it has a significant performance drawback.
+.It Fl h , Fl Fl human-numeric-sort , Fl Fl sort=human-numeric
+Sort by numerical value, but take into account the SI suffix,
+if present.
+Sort first by numeric sign (negative, zero, or
+positive); then by SI suffix (either empty, or `k' or `K', or one
+of `MGTPEZY', in that order); and finally by numeric value.
+The SI suffix must immediately follow the number.
+For example, '12345K' sorts before '1M', because M is "larger" than K.
+This sort option is useful for sorting the output of a single invocation
+of 'df' command with
+.Fl h
+or
+.Fl H
+options (human-readable).
+.It Fl i , Fl Fl ignore-nonprinting
+Ignore all non-printable characters.
+.It Fl M , Fl Fl month-sort , Fl Fl sort=month
+Sort by month abbreviations.
+Unknown strings are considered smaller than the month names.
+.It Fl n , Fl Fl numeric-sort , Fl Fl sort=numeric
+Sort fields numerically by arithmetic value.
+Fields are supposed to have optional blanks in the beginning, an
+optional minus sign, zero or more digits (including decimal point and
+possible thousand separators).
+.It Fl R , Fl Fl random-sort , Fl Fl sort=random
+Sort by a random order.
+This is a random permutation of the inputs except that
+the equal keys sort together.
+It is implemented by hashing the input keys and sorting
+the hash values.
+The hash function is chosen randomly.
+The hash function is randomized by
+.Cm /dev/random
+content, or by file content if it is specified by
+.Fl Fl random-source .
+Even if multiple sort fields are specified,
+the same random hash function is used for all of them.
+.It Fl r , Fl Fl reverse
+Sort in reverse order.
+.It Fl V , Fl Fl version-sort
+Sort version numbers.
+The input lines are treated as file names in form
+PREFIX VERSION SUFFIX, where SUFFIX matches the regular expression
+"(\.([A-Za-z~][A-Za-z0-9~]*)?)*".
+The files are compared by their prefixes and versions (leading
+zeros are ignored in version numbers, see example below).
+If an input string does not match the pattern, then it is compared
+using the byte compare function.
+All string comparisons are performed in C locale, the locale
+environment setting is ignored.
+.Bl -tag -width indent
+.It Example:
+.It $ ls sort* | sort -V
+.It sort-1.022.tgz
+.It sort-1.23.tgz
+.It sort-1.23.1.tgz
+.It sort-1.024.tgz
+.It sort-1.024.003.
+.It sort-1.024.003.tgz
+.It sort-1.024.07.tgz
+.It sort-1.024.009.tgz
+.El
+.El
+.Pp
+The treatment of field separators can be altered using these options:
+.Bl -tag -width indent
+.It Fl b , Fl Fl ignore-leading-blanks
+Ignore leading blank space when determining the start
+and end of a restricted sort key (see
+.Fl k ) .
+If
+.Fl b
+is specified before the first
+.Fl k
+option, it applies globally to all key specifications.
+Otherwise,
+.Fl b
+can be attached independently to each
+.Ar field
+argument of the key specifications.
+.Fl b .
+.It Xo
+.Fl k Ar field1 Ns Op , Ns Ar field2 ,
+.Fl Fl key Ns = Ns Ar field1 Ns Op , Ns Ar field2
+.Xc
+Define a restricted sort key that has the starting position
+.Ar field1 ,
+and optional ending position
+.Ar field2
+of a key field.
+The
+.Fl k
+option may be specified multiple times,
+in which case subsequent keys are compared when earlier keys compare equal.
+The
+.Fl k
+option replaces the obsolete options
+.Cm \(pl Ns Ar pos1
+and
+.Fl Ns Ar pos2 ,
+but the old notation is also supported.
+.It Fl t Ar char , Fl Fl field-separator Ns = Ns Ar char
+Use
+.Ar char
+as a field separator character.
+The initial
+.Ar char
+is not considered to be part of a field when determining key offsets.
+Each occurrence of
+.Ar char
+is significant (for example,
+.Dq Ar charchar
+delimits an empty field).
+If
+.Fl t
+is not specified, the default field separator is a sequence of
+blank space characters, and consecutive blank spaces do
+.Em not
+delimit an empty field, however, the initial blank space
+.Em is
+considered part of a field when determining key offsets.
+To use NUL as field separator, use
+.Fl t
+\'\\0\'.
+.It Fl z , Fl Fl zero-terminated
+Use NUL as record separator.
+By default, records in the files are supposed to be separated by
+the newline characters.
+With this option, NUL (\'\\0\') is used as a record separator character.
+.El
+.Pp
+Other options:
+.Bl -tag -width indent
+.It Fl Fl batch-size Ns = Ns Ar num
+Specify maximum number of files that can be opened by
+.Nm
+at once.
+This option affects behavior when having many input files or using
+temporary files.
+The default value is 16.
+.It Fl Fl compress-program Ns = Ns Ar PROGRAM
+Use PROGRAM to compress temporary files.
+PROGRAM must compress standard input to standard output, when called
+without arguments.
+When called with argument
+.Fl d
+it must decompress standard input to standard output.
+If PROGRAM fails,
+.Nm
+must exit with error.
+An example of PROGRAM that can be used here is bzip2.
+.It Fl Fl random-source Ns = Ns Ar filename
+In random sort, the file content is used as the source of the 'seed' data
+for the hash function choice.
+Two invocations of random sort with the same seed data will use
+the same hash function and will produce the same result if the input is
+also identical.
+By default, file
+.Cm /dev/random
+is used.
+.It Fl Fl debug
+Print some extra information about the sorting process to the
+standard output.
+%%THREADS%%.It Fl Fl parallel
+%%THREADS%%Set the maximum number of execution threads.
+%%THREADS%%Default number equals to the number of CPUs.
+.It Fl Fl files0-from Ns = Ns Ar filename
+Take the input file list from the file
+.Ar filename .
+The file names must be separated by NUL
+(like the output produced by the command "find ... -print0").
+.It Fl Fl radixsort
+Try to use radix sort, if the sort specifications allow.
+The radix sort can only be used for trivial locales (C and POSIX),
+and it cannot be used for numeric or month sort.
+Radix sort is very fast and stable.
+.It Fl Fl mergesort
+Use mergesort.
+This is a universal algorithm that can always be used,
+but it is not always the fastest.
+.It Fl Fl qsort
+Try to use quick sort, if the sort specifications allow.
+This sort algorithm cannot be used with
+.Fl u
+and
+.Fl s .
+.It Fl Fl heapsort
+Try to use heap sort, if the sort specifications allow.
+This sort algorithm cannot be used with
+.Fl u
+and
+.Fl s .
+.It Fl Fl mmap
+Try to use file memory mapping system call.
+It may increase speed in some cases.
+.El
+.Pp
+The following operands are available:
+.Bl -tag -width indent
+.It Ar file
+The pathname of a file to be sorted, merged, or checked.
+If no
+.Ar file
+operands are specified, or if a
+.Ar file
+operand is
+.Fl ,
+the standard input is used.
+.El
+.Pp
+A field is defined as a maximal sequence of characters other than the
+field separator and record separator (newline by default).
+Initial blank spaces are included in the field unless
+.Fl b
+has been specified;
+the first blank space of a sequence of blank spaces acts as the field
+separator and is included in the field (unless
+.Fl t
+is specified).
+For example, all blank spaces at the beginning of a line are
+considered to be part of the first field.
+.Pp
+Fields are specified by the
+.Sm off
+.Fl k\ \& Ar field1 Op , Ar field2
+.Sm on
+command-line option.
+If
+.Ar field2
+is missing, the end of the key defaults to the end of the line.
+.Pp
+The arguments
+.Ar field1
+and
+.Ar field2
+have the form
+.Em m.n
+.Em (m,n > 0)
+and can be followed by one or more of the modifiers
+.Cm b , d , f , i ,
+.Cm n , g , M
+and
+.Cm r ,
+which correspond to the options discussed above.
+When
+.Cm b
+is specified it applies only to
+.Ar field1
+or
+.Ar field2
+where it is specified while the rest of the modifiers
+apply to the whole key field regardless if they are
+specified only with
+.Ar field1
+or
+.Ar field2
+or both.
+A
+.Ar field1
+position specified by
+.Em m.n
+is interpreted as the
+.Em n Ns th
+character from the beginning of the
+.Em m Ns th
+field.
+A missing
+.Em \&.n
+in
+.Ar field1
+means
+.Ql \&.1 ,
+indicating the first character of the
+.Em m Ns th
+field; if the
+.Fl b
+option is in effect,
+.Em n
+is counted from the first non-blank character in the
+.Em m Ns th
+field;
+.Em m Ns \&.1b
+refers to the first non-blank character in the
+.Em m Ns th
+field.
+.No 1\&. Ns Em n
+refers to the
+.Em n Ns th
+character from the beginning of the line;
+if
+.Em n
+is greater than the length of the line, the field is taken to be empty.
+.Pp
+.Em n Ns th
+positions are always counted from the field beginning, even if the field
+is shorter than the number of specified positions.
+Thus, the key can really start from a position in a subsequent field.
+.Pp
+A
+.Ar field2
+position specified by
+.Em m.n
+is interpreted as the
+.Em n Ns th
+character (including separators) from the beginning of the
+.Em m Ns th
+field.
+A missing
+.Em \&.n
+indicates the last character of the
+.Em m Ns th
+field;
+.Em m
+= \&0
+designates the end of a line.
+Thus the option
+.Fl k Ar v.x,w.y
+is synonymous with the obsolete option
+.Cm \(pl Ns Ar v-\&1.x-\&1
+.Fl Ns Ar w-\&1.y ;
+when
+.Em y
+is omitted,
+.Fl k Ar v.x,w
+is synonymous with
+.Cm \(pl Ns Ar v-\&1.x-\&1
+.Fl Ns Ar w\&.0 .
+The obsolete
+.Cm \(pl Ns Ar pos1
+.Fl Ns Ar pos2
+option is still supported, except for
+.Fl Ns Ar w\&.0b ,
+which has no
+.Fl k
+equivalent.
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev LC_COLLATE
+Locale settings to be used to determine the collation for
+sorting records.
+.It Ev LC_CTYPE
+Locale settings to be used to case conversion and classification
+of characters, that is, which characters are considered
+whitespaces, etc.
+.It Ev LC_MESSAGES
+Locale settings that determine the language of output messages
+that
+.Nm
+prints out.
+.It Ev LC_NUMERIC
+Locale settings that determine the number format used in numeric sort.
+.It Ev LC_TIME
+Locale settings that determine the month format used in month sort.
+.It Ev LC_ALL
+Locale settings that override all of the above locale settings.
+This environment variable can be used to set all these settings
+to the same value at once.
+.It Ev LANG
+Used as a last resort to determine different kinds of locale-specific
+behavior if neither the respective environment variable, nor
+.Ev LC_ALL
+are set.
+%%NLS%%.It Ev NLSPATH
+%%NLS%%Path to NLS catalogs.
+.It Ev TMPDIR
+Path to the directory in which temporary files will be stored.
+Note that
+.Ev TMPDIR
+may be overridden by the
+.Fl T
+option.
+.It Ev GNUSORT_NUMERIC_COMPATIBILITY
+If defined
+.Fl t
+will not override the locale numeric symbols, that is, thousand
+separators and decimal separators.
+By default, if we specify
+.Fl t
+with the same symbol as the thousand separator or decimal point,
+the symbol will be treated as the field separator.
+Older behavior was less definite; the symbol was treated as both field
+separator and numeric separator, simultaneously.
+This environment variable enables the old behavior.
+.It Ev GNUSORT_COMPATIBLE_BLANKS
+Use 'space' symbols as field separators (as modern GNU sort does).
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/tmp/.bsdsort.PID.*
+Temporary files.
+.It Pa /dev/random
+Default seed file for the random sort.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility shall exit with one of the following values:
+.Pp
+.Bl -tag -width flag -compact
+.It 0
+Successfully sorted the input files or if used with
+.Fl c
+or
+.Fl C ,
+the input file already met the sorting criteria.
+.It 1
+On disorder (or non-uniqueness) with the
+.Fl c
+or
+.Fl C
+options.
+.It 2
+An error occurred.
+.El
+.Sh SEE ALSO
+.Xr comm 1 ,
+.Xr join 1 ,
+.Xr uniq 1
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl ghRMSsTVz
+are extensions to the POSIX specification.
+.Pp
+All long options are extensions to the specification, some of them are
+provided for compatibility with GNU versions and some of them are
+own extensions.
+.Pp
+The old key notations
+.Cm \(pl Ns Ar pos1
+and
+.Fl Ns Ar pos2
+come from older versions of
+.Nm
+and are still supported but their use is highly discouraged.
+.Sh HISTORY
+A
+.Nm
+command first appeared in
+.At v3 .
+.Sh AUTHORS
+.An Gabor Kovesdan Aq Mt gabor@FreeBSD.org ,
+.Pp
+.An Oleg Moskalenko Aq Mt mom040267@gmail.com
+.Sh NOTES
+This implementation of
+.Nm
+has no limits on input line length (other than imposed by available
+memory) or any restrictions on bytes allowed within lines.
+.Pp
+The performance depends highly on locale settings,
+efficient choice of sort keys and key complexity.
+The fastest sort is with locale C, on whole lines,
+with option
+.Fl s .
+In general, locale C is the fastest, then single-byte
+locales follow and multi-byte locales as the slowest but
+the correct collation order is always respected.
+As for the key specification, the simpler to process the
+lines the faster the search will be.
+.Pp
+When sorting by arithmetic value, using
+.Fl n
+results in much better performance than
+.Fl g
+so its use is encouraged
+whenever possible.
diff --git a/text_cmds/sort/sort.c b/text_cmds/sort/sort.c
new file mode 100644
index 0000000..9200606
--- /dev/null
+++ b/text_cmds/sort/sort.c
@@ -0,0 +1,1325 @@
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#ifndef __APPLE__
+#include <md5.h>
+#endif
+#include <regex.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "coll.h"
+#include "file.h"
+#include "sort.h"
+
+#ifndef WITHOUT_NLS
+#include <nl_types.h>
+nl_catd catalog;
+#endif
+
+#define OPTIONS "bcCdfghik:Mmno:RrsS:t:T:uVz"
+
+#define DEFAULT_RANDOM_SORT_SEED_FILE ("/dev/random")
+#define MAX_DEFAULT_RANDOM_SEED_DATA_SIZE (1024)
+
+static bool need_random;
+static const char *random_source = DEFAULT_RANDOM_SORT_SEED_FILE;
+static const void *random_seed;
+static size_t random_seed_size;
+
+MD5_CTX md5_ctx;
+
+/*
+ * Default messages to use when NLS is disabled or no catalogue
+ * is found.
+ */
+const char *nlsstr[] = { "",
+/* 1*/"mutually exclusive flags",
+/* 2*/"extra argument not allowed with -c",
+/* 3*/"Unknown feature",
+/* 4*/"Wrong memory buffer specification",
+/* 5*/"0 field in key specs",
+/* 6*/"0 column in key specs",
+/* 7*/"Wrong file mode",
+/* 8*/"Cannot open file for reading",
+/* 9*/"Radix sort cannot be used with these sort options",
+/*10*/"The chosen sort method cannot be used with stable and/or unique sort",
+/*11*/"Invalid key position",
+/*12*/"Usage: %s [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] "
+ "[+POS1 [-POS2]] [-S memsize] [-T tmpdir] [-t separator] "
+ "[-o outfile] [--batch-size size] [--files0-from file] "
+ "[--heapsort] [--mergesort] [--radixsort] [--qsort] "
+ "[--mmap] "
+#if defined(SORT_THREADS)
+ "[--parallel thread_no] "
+#endif
+ "[--human-numeric-sort] "
+ "[--version-sort] [--random-sort [--random-source file]] "
+ "[--compress-program program] [file ...]\n" };
+
+struct sort_opts sort_opts_vals;
+
+bool debug_sort;
+bool need_hint;
+
+int (*isblank_f)(int c) = isblank;
+int (*iswblank_f)(wint_t c) = iswblank;
+
+#if defined(SORT_THREADS)
+unsigned int ncpu = 1;
+size_t nthreads = 1;
+#endif
+
+static bool gnusort_numeric_compatibility;
+
+static struct sort_mods default_sort_mods_object;
+struct sort_mods * const default_sort_mods = &default_sort_mods_object;
+
+static bool print_symbols_on_debug;
+
+/*
+ * Arguments from file (when file0-from option is used:
+ */
+static size_t argc_from_file0 = (size_t)-1;
+static char **argv_from_file0;
+
+/*
+ * Placeholder symbols for options which have no single-character equivalent
+ */
+enum
+{
+ SORT_OPT = CHAR_MAX + 1,
+ HELP_OPT,
+ FF_OPT,
+ BS_OPT,
+ VERSION_OPT,
+ DEBUG_OPT,
+#if defined(SORT_THREADS)
+ PARALLEL_OPT,
+#endif
+ RANDOMSOURCE_OPT,
+ COMPRESSPROGRAM_OPT,
+ QSORT_OPT,
+ MERGESORT_OPT,
+ HEAPSORT_OPT,
+ RADIXSORT_OPT,
+ MMAP_OPT
+};
+
+#define NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS 6
+static const char mutually_exclusive_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] = { 'M', 'n', 'g', 'R', 'h', 'V' };
+
+static struct option long_options[] = {
+ { "batch-size", required_argument, NULL, BS_OPT },
+ { "buffer-size", required_argument, NULL, 'S' },
+ { "check", optional_argument, NULL, 'c' },
+ { "check=silent|quiet", optional_argument, NULL, 'C' },
+ { "compress-program", required_argument, NULL, COMPRESSPROGRAM_OPT },
+ { "debug", no_argument, NULL, DEBUG_OPT },
+ { "dictionary-order", no_argument, NULL, 'd' },
+ { "field-separator", required_argument, NULL, 't' },
+ { "files0-from", required_argument, NULL, FF_OPT },
+ { "general-numeric-sort", no_argument, NULL, 'g' },
+ { "heapsort", no_argument, NULL, HEAPSORT_OPT },
+ { "help",no_argument, NULL, HELP_OPT },
+ { "human-numeric-sort", no_argument, NULL, 'h' },
+ { "ignore-leading-blanks", no_argument, NULL, 'b' },
+ { "ignore-case", no_argument, NULL, 'f' },
+ { "ignore-nonprinting", no_argument, NULL, 'i' },
+ { "key", required_argument, NULL, 'k' },
+ { "merge", no_argument, NULL, 'm' },
+ { "mergesort", no_argument, NULL, MERGESORT_OPT },
+ { "mmap", no_argument, NULL, MMAP_OPT },
+ { "month-sort", no_argument, NULL, 'M' },
+ { "numeric-sort", no_argument, NULL, 'n' },
+ { "output", required_argument, NULL, 'o' },
+#if defined(SORT_THREADS)
+ { "parallel", required_argument, NULL, PARALLEL_OPT },
+#endif
+ { "qsort", no_argument, NULL, QSORT_OPT },
+ { "radixsort", no_argument, NULL, RADIXSORT_OPT },
+ { "random-sort", no_argument, NULL, 'R' },
+ { "random-source", required_argument, NULL, RANDOMSOURCE_OPT },
+ { "reverse", no_argument, NULL, 'r' },
+ { "sort", required_argument, NULL, SORT_OPT },
+ { "stable", no_argument, NULL, 's' },
+ { "temporary-directory",required_argument, NULL, 'T' },
+ { "unique", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, VERSION_OPT },
+ { "version-sort",no_argument, NULL, 'V' },
+ { "zero-terminated", no_argument, NULL, 'z' },
+ { NULL, no_argument, NULL, 0 }
+};
+
+void fix_obsolete_keys(int *argc, char **argv);
+
+/*
+ * Check where sort modifier is present
+ */
+static bool
+sort_modifier_empty(struct sort_mods *sm)
+{
+
+ if (sm == NULL)
+ return (true);
+ return (!(sm->Mflag || sm->Vflag || sm->nflag || sm->gflag ||
+ sm->rflag || sm->Rflag || sm->hflag || sm->dflag || sm->fflag || sm->iflag));
+}
+
+/*
+ * Print out usage text.
+ */
+static void
+usage(bool opt_err)
+{
+ FILE *out;
+
+ out = opt_err ? stderr : stdout;
+
+ fprintf(out, getstr(12), getprogname());
+ if (opt_err)
+ exit(2);
+ exit(0);
+}
+
+/*
+ * Read input file names from a file (file0-from option).
+ */
+static void
+read_fns_from_file0(const char *fn)
+{
+ FILE *f;
+ char *line = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
+
+ if (fn == NULL)
+ return;
+
+ f = fopen(fn, "r");
+ if (f == NULL)
+ err(2, "%s", fn);
+
+ while ((linelen = getdelim(&line, &linesize, '\0', f)) != -1) {
+ if (*line != '\0') {
+ if (argc_from_file0 == (size_t) - 1)
+ argc_from_file0 = 0;
+ ++argc_from_file0;
+ argv_from_file0 = sort_realloc(argv_from_file0,
+ argc_from_file0 * sizeof(char *));
+ if (argv_from_file0 == NULL)
+ err(2, NULL);
+ argv_from_file0[argc_from_file0 - 1] = line;
+ } else {
+ free(line);
+ }
+ line = NULL;
+ linesize = 0;
+ }
+ if (ferror(f))
+ err(2, "%s: getdelim", fn);
+
+ closefile(f, fn);
+}
+
+/*
+ * Check how much RAM is available for the sort.
+ */
+static void
+set_hw_params(void)
+{
+ long pages, psize;
+
+#if defined(SORT_THREADS)
+ ncpu = 1;
+#endif
+
+ pages = sysconf(_SC_PHYS_PAGES);
+ if (pages < 1) {
+ perror("sysconf pages");
+ pages = 1;
+ }
+ psize = sysconf(_SC_PAGESIZE);
+ if (psize < 1) {
+ perror("sysconf psize");
+ psize = 4096;
+ }
+#if defined(SORT_THREADS)
+ ncpu = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpu < 1)
+ ncpu = 1;
+ else if(ncpu > 32)
+ ncpu = 32;
+
+ nthreads = ncpu;
+#endif
+
+ free_memory = (unsigned long long) pages * (unsigned long long) psize;
+ available_free_memory = free_memory / 2;
+
+ if (available_free_memory < 1024)
+ available_free_memory = 1024;
+}
+
+/*
+ * Convert "plain" symbol to wide symbol, with default value.
+ */
+static void
+conv_mbtowc(wchar_t *wc, const char *c, const wchar_t def)
+{
+
+ if (wc && c) {
+ int res;
+
+ res = mbtowc(wc, c, MB_CUR_MAX);
+ if (res < 1)
+ *wc = def;
+ }
+}
+
+/*
+ * Set current locale symbols.
+ */
+static void
+set_locale(void)
+{
+ struct lconv *lc;
+ const char *locale;
+
+ setlocale(LC_ALL, "");
+
+ lc = localeconv();
+
+ if (lc) {
+ /* obtain LC_NUMERIC info */
+ /* Convert to wide char form */
+ conv_mbtowc(&symbol_decimal_point, lc->decimal_point,
+ symbol_decimal_point);
+ conv_mbtowc(&symbol_thousands_sep, lc->thousands_sep,
+ symbol_thousands_sep);
+ conv_mbtowc(&symbol_positive_sign, lc->positive_sign,
+ symbol_positive_sign);
+ conv_mbtowc(&symbol_negative_sign, lc->negative_sign,
+ symbol_negative_sign);
+ }
+
+ if (getenv("GNUSORT_NUMERIC_COMPATIBILITY"))
+ gnusort_numeric_compatibility = true;
+
+ locale = setlocale(LC_COLLATE, NULL);
+
+ if (locale) {
+ char *tmpl;
+ const char *cclocale;
+
+ tmpl = sort_strdup(locale);
+ cclocale = setlocale(LC_COLLATE, "C");
+ if (cclocale && !strcmp(cclocale, tmpl))
+ byte_sort = true;
+ else {
+ const char *pclocale;
+
+ pclocale = setlocale(LC_COLLATE, "POSIX");
+ if (pclocale && !strcmp(pclocale, tmpl))
+ byte_sort = true;
+ }
+ setlocale(LC_COLLATE, tmpl);
+ sort_free(tmpl);
+ }
+}
+
+/*
+ * Set directory temporary files.
+ */
+static void
+set_tmpdir(void)
+{
+ char *td;
+
+ td = getenv("TMPDIR");
+ if (td != NULL)
+ tmpdir = sort_strdup(td);
+}
+
+/*
+ * Parse -S option.
+ */
+static unsigned long long
+parse_memory_buffer_value(const char *value)
+{
+
+ if (value == NULL)
+ return (available_free_memory);
+ else {
+ char *endptr;
+ unsigned long long membuf;
+
+ endptr = NULL;
+ errno = 0;
+ membuf = strtoll(value, &endptr, 10);
+
+ if (errno != 0) {
+ warn("%s",getstr(4));
+ membuf = available_free_memory;
+ } else {
+ switch (*endptr){
+ case 'Y':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'Z':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'E':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'P':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'T':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'G':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'M':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case '\0':
+ case 'K':
+ membuf *= 1024;
+ /* FALLTHROUGH */
+ case 'b':
+ break;
+ case '%':
+ membuf = (available_free_memory * membuf) /
+ 100;
+ break;
+ default:
+ warnc(EINVAL, "%s", optarg);
+ membuf = available_free_memory;
+ }
+ }
+ return (membuf);
+ }
+}
+
+/*
+ * Signal handler that clears the temporary files.
+ */
+static void
+sig_handler(int sig __unused, siginfo_t *siginfo __unused,
+ void *context __unused)
+{
+
+ clear_tmp_files();
+ exit(-1);
+}
+
+/*
+ * Set signal handler on panic signals.
+ */
+static void
+set_signal_handler(void)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = &sig_handler;
+ sa.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGINT, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGQUIT, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGABRT, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGBUS, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGSEGV, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGUSR1, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+ if (sigaction(SIGUSR2, &sa, NULL) < 0) {
+ perror("sigaction");
+ return;
+ }
+}
+
+/*
+ * Print "unknown" message and exit with status 2.
+ */
+static void
+unknown(const char *what)
+{
+
+ errx(2, "%s: %s", getstr(3), what);
+}
+
+/*
+ * Check whether contradictory input options are used.
+ */
+static void
+check_mutually_exclusive_flags(char c, bool *mef_flags)
+{
+ int fo_index, mec;
+ bool found_others, found_this;
+
+ found_others = found_this = false;
+ fo_index = 0;
+
+ for (int i = 0; i < NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS; i++) {
+ mec = mutually_exclusive_flags[i];
+
+ if (mec != c) {
+ if (mef_flags[i]) {
+ if (found_this)
+ errx(1, "%c:%c: %s", c, mec, getstr(1));
+ found_others = true;
+ fo_index = i;
+ }
+ } else {
+ if (found_others)
+ errx(1, "%c:%c: %s", c, mutually_exclusive_flags[fo_index], getstr(1));
+ mef_flags[i] = true;
+ found_this = true;
+ }
+ }
+}
+
+/*
+ * Initialise sort opts data.
+ */
+static void
+set_sort_opts(void)
+{
+
+ memset(&default_sort_mods_object, 0,
+ sizeof(default_sort_mods_object));
+ memset(&sort_opts_vals, 0, sizeof(sort_opts_vals));
+ default_sort_mods_object.func =
+ get_sort_func(&default_sort_mods_object);
+}
+
+/*
+ * Set a sort modifier on a sort modifiers object.
+ */
+static bool
+set_sort_modifier(struct sort_mods *sm, int c)
+{
+
+ if (sm) {
+ switch (c){
+ case 'b':
+ sm->bflag = true;
+ break;
+ case 'd':
+ sm->dflag = true;
+ break;
+ case 'f':
+ sm->fflag = true;
+ break;
+ case 'g':
+ sm->gflag = true;
+ need_hint = true;
+ break;
+ case 'i':
+ sm->iflag = true;
+ break;
+ case 'R':
+ sm->Rflag = true;
+ need_random = true;
+ break;
+ case 'M':
+ initialise_months();
+ sm->Mflag = true;
+ need_hint = true;
+ break;
+ case 'n':
+ sm->nflag = true;
+ need_hint = true;
+ print_symbols_on_debug = true;
+ break;
+ case 'r':
+ sm->rflag = true;
+ break;
+ case 'V':
+ sm->Vflag = true;
+ break;
+ case 'h':
+ sm->hflag = true;
+ need_hint = true;
+ print_symbols_on_debug = true;
+ break;
+ default:
+ return false;
+ }
+ sort_opts_vals.complex_sort = true;
+ sm->func = get_sort_func(sm);
+ }
+ return (true);
+}
+
+/*
+ * Parse POS in -k option.
+ */
+static int
+parse_pos(const char *s, struct key_specs *ks, bool *mef_flags, bool second)
+{
+ regmatch_t pmatch[4];
+ regex_t re;
+ char *c, *f;
+ const char *sregexp = "^([0-9]+)(\\.[0-9]+)?([bdfirMngRhV]+)?$";
+ size_t len, nmatch;
+ int ret;
+
+ ret = -1;
+ nmatch = 4;
+ c = f = NULL;
+
+ if (regcomp(&re, sregexp, REG_EXTENDED) != 0)
+ return (-1);
+
+ if (regexec(&re, s, nmatch, pmatch, 0) != 0)
+ goto end;
+
+ if (pmatch[0].rm_eo <= pmatch[0].rm_so)
+ goto end;
+
+ if (pmatch[1].rm_eo <= pmatch[1].rm_so)
+ goto end;
+
+ len = pmatch[1].rm_eo - pmatch[1].rm_so;
+ f = sort_malloc((len + 1) * sizeof(char));
+
+ strncpy(f, s + pmatch[1].rm_so, len);
+ f[len] = '\0';
+
+ if (second) {
+ errno = 0;
+ ks->f2 = (size_t) strtoul(f, NULL, 10);
+ if (errno != 0)
+ err(2, "-k");
+ if (ks->f2 == 0) {
+ warn("%s",getstr(5));
+ goto end;
+ }
+ } else {
+ errno = 0;
+ ks->f1 = (size_t) strtoul(f, NULL, 10);
+ if (errno != 0)
+ err(2, "-k");
+ if (ks->f1 == 0) {
+ warn("%s",getstr(5));
+ goto end;
+ }
+ }
+
+ if (pmatch[2].rm_eo > pmatch[2].rm_so) {
+ len = pmatch[2].rm_eo - pmatch[2].rm_so - 1;
+ c = sort_malloc((len + 1) * sizeof(char));
+
+ strncpy(c, s + pmatch[2].rm_so + 1, len);
+ c[len] = '\0';
+
+ if (second) {
+ errno = 0;
+ ks->c2 = (size_t) strtoul(c, NULL, 10);
+ if (errno != 0)
+ err(2, "-k");
+ } else {
+ errno = 0;
+ ks->c1 = (size_t) strtoul(c, NULL, 10);
+ if (errno != 0)
+ err(2, "-k");
+ if (ks->c1 == 0) {
+ warn("%s",getstr(6));
+ goto end;
+ }
+ }
+ } else {
+ if (second)
+ ks->c2 = 0;
+ else
+ ks->c1 = 1;
+ }
+
+ if (pmatch[3].rm_eo > pmatch[3].rm_so) {
+ regoff_t i = 0;
+
+ for (i = pmatch[3].rm_so; i < pmatch[3].rm_eo; i++) {
+ check_mutually_exclusive_flags(s[i], mef_flags);
+ if (s[i] == 'b') {
+ if (second)
+ ks->pos2b = true;
+ else
+ ks->pos1b = true;
+ } else if (!set_sort_modifier(&(ks->sm), s[i]))
+ goto end;
+ }
+ }
+
+ ret = 0;
+
+end:
+
+ if (c)
+ sort_free(c);
+ if (f)
+ sort_free(f);
+ regfree(&re);
+
+ return (ret);
+}
+
+/*
+ * Parse -k option value.
+ */
+static int
+parse_k(const char *s, struct key_specs *ks)
+{
+ int ret = -1;
+ bool mef_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] =
+ { false, false, false, false, false, false };
+
+ if (s && *s) {
+ char *sptr;
+
+ sptr = strchr(s, ',');
+ if (sptr) {
+ size_t size1;
+ char *pos1, *pos2;
+
+ size1 = sptr - s;
+
+ if (size1 < 1)
+ return (-1);
+ pos1 = sort_malloc((size1 + 1) * sizeof(char));
+
+ strncpy(pos1, s, size1);
+ pos1[size1] = '\0';
+
+ ret = parse_pos(pos1, ks, mef_flags, false);
+
+ sort_free(pos1);
+ if (ret < 0)
+ return (ret);
+
+ pos2 = sort_strdup(sptr + 1);
+ ret = parse_pos(pos2, ks, mef_flags, true);
+ sort_free(pos2);
+ } else
+ ret = parse_pos(s, ks, mef_flags, false);
+ }
+
+ return (ret);
+}
+
+/*
+ * Parse POS in +POS -POS option.
+ */
+static int
+parse_pos_obs(const char *s, int *nf, int *nc, char* sopts)
+{
+ regex_t re;
+ regmatch_t pmatch[4];
+ char *c, *f;
+ const char *sregexp = "^([0-9]+)(\\.[0-9]+)?([A-Za-z]+)?$";
+ int ret;
+ size_t len, nmatch;
+
+ ret = -1;
+ nmatch = 4;
+ c = f = NULL;
+ *nc = *nf = 0;
+
+ if (regcomp(&re, sregexp, REG_EXTENDED) != 0)
+ return (-1);
+
+ if (regexec(&re, s, nmatch, pmatch, 0) != 0)
+ goto end;
+
+ if (pmatch[0].rm_eo <= pmatch[0].rm_so)
+ goto end;
+
+ if (pmatch[1].rm_eo <= pmatch[1].rm_so)
+ goto end;
+
+ len = pmatch[1].rm_eo - pmatch[1].rm_so;
+ f = sort_malloc((len + 1) * sizeof(char));
+
+ strncpy(f, s + pmatch[1].rm_so, len);
+ f[len] = '\0';
+
+ errno = 0;
+ *nf = (size_t) strtoul(f, NULL, 10);
+ if (errno != 0)
+ errx(2, "%s", getstr(11));
+
+ if (pmatch[2].rm_eo > pmatch[2].rm_so) {
+ len = pmatch[2].rm_eo - pmatch[2].rm_so - 1;
+ c = sort_malloc((len + 1) * sizeof(char));
+
+ strncpy(c, s + pmatch[2].rm_so + 1, len);
+ c[len] = '\0';
+
+ errno = 0;
+ *nc = (size_t) strtoul(c, NULL, 10);
+ if (errno != 0)
+ errx(2, "%s", getstr(11));
+ }
+
+ if (pmatch[3].rm_eo > pmatch[3].rm_so) {
+
+ len = pmatch[3].rm_eo - pmatch[3].rm_so;
+
+ strncpy(sopts, s + pmatch[3].rm_so, len);
+ sopts[len] = '\0';
+ }
+
+ ret = 0;
+
+end:
+ if (c)
+ sort_free(c);
+ if (f)
+ sort_free(f);
+ regfree(&re);
+
+ return (ret);
+}
+
+/*
+ * "Translate" obsolete +POS1 -POS2 syntax into new -kPOS1,POS2 syntax
+ */
+void
+fix_obsolete_keys(int *argc, char **argv)
+{
+ char sopt[129];
+
+ for (int i = 1; i < *argc; i++) {
+ char *arg1;
+
+ arg1 = argv[i];
+
+ if (strlen(arg1) > 1 && arg1[0] == '+') {
+ int c1, f1;
+ char sopts1[128];
+
+ sopts1[0] = 0;
+ c1 = f1 = 0;
+
+ if (parse_pos_obs(arg1 + 1, &f1, &c1, sopts1) < 0)
+ continue;
+ else {
+ f1 += 1;
+ c1 += 1;
+ if (i + 1 < *argc) {
+ char *arg2 = argv[i + 1];
+
+ if (strlen(arg2) > 1 &&
+ arg2[0] == '-') {
+ int c2, f2;
+ char sopts2[128];
+
+ sopts2[0] = 0;
+ c2 = f2 = 0;
+
+ if (parse_pos_obs(arg2 + 1,
+ &f2, &c2, sopts2) >= 0) {
+ if (c2 > 0)
+ f2 += 1;
+ sprintf(sopt, "-k%d.%d%s,%d.%d%s",
+ f1, c1, sopts1, f2, c2, sopts2);
+ argv[i] = sort_strdup(sopt);
+ for (int j = i + 1; j + 1 < *argc; j++)
+ argv[j] = argv[j + 1];
+ *argc -= 1;
+ continue;
+ }
+ }
+ }
+ sprintf(sopt, "-k%d.%d%s", f1, c1, sopts1);
+ argv[i] = sort_strdup(sopt);
+ }
+ }
+ }
+}
+
+/*
+ * Set random seed
+ */
+static void
+set_random_seed(void)
+{
+ if (need_random) {
+
+ if (strcmp(random_source, DEFAULT_RANDOM_SORT_SEED_FILE) == 0) {
+ FILE* fseed;
+ MD5_CTX ctx;
+ char rsd[MAX_DEFAULT_RANDOM_SEED_DATA_SIZE];
+ size_t sz = 0;
+
+ fseed = openfile(random_source, "r");
+ while (!feof(fseed)) {
+ int cr;
+
+ cr = fgetc(fseed);
+ if (cr == EOF)
+ break;
+
+ rsd[sz++] = (char) cr;
+
+ if (sz >= MAX_DEFAULT_RANDOM_SEED_DATA_SIZE)
+ break;
+ }
+
+ closefile(fseed, random_source);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, rsd, sz);
+
+ random_seed = MD5End(&ctx, NULL);
+ random_seed_size = strlen(random_seed);
+
+ } else {
+ MD5_CTX ctx;
+ char *b;
+
+ MD5Init(&ctx);
+ b = MD5File(random_source, NULL);
+ if (b == NULL)
+ err(2, NULL);
+
+ random_seed = b;
+ random_seed_size = strlen(b);
+ }
+
+ MD5Init(&md5_ctx);
+ if(random_seed_size>0) {
+ MD5Update(&md5_ctx, random_seed, random_seed_size);
+ }
+ }
+}
+
+/*
+ * Main function.
+ */
+int
+main(int argc, char **argv)
+{
+ char *outfile, *real_outfile;
+ int c, result;
+ bool mef_flags[NUMBER_OF_MUTUALLY_EXCLUSIVE_FLAGS] =
+ { false, false, false, false, false, false };
+
+ result = 0;
+ outfile = sort_strdup("-");
+ real_outfile = NULL;
+
+ if(getenv("GNUSORT_COMPATIBLE_BLANKS")) {
+ isblank_f = isspace;
+ iswblank_f = iswspace;
+ }
+
+ struct sort_mods *sm = &default_sort_mods_object;
+
+ init_tmp_files();
+
+ set_signal_handler();
+
+ set_hw_params();
+ set_locale();
+ set_tmpdir();
+ set_sort_opts();
+
+ fix_obsolete_keys(&argc, argv);
+
+ while (((c = getopt_long(argc, argv, OPTIONS, long_options, NULL))
+ != -1)) {
+
+ check_mutually_exclusive_flags(c, mef_flags);
+
+ if (!set_sort_modifier(sm, c)) {
+
+ switch (c) {
+ case 'c':
+ sort_opts_vals.cflag = true;
+ if (optarg) {
+ if (!strcmp(optarg, "diagnose-first"))
+ ;
+ else if (!strcmp(optarg, "silent") ||
+ !strcmp(optarg, "quiet"))
+ sort_opts_vals.csilentflag = true;
+ else if (*optarg)
+ unknown(optarg);
+ }
+ break;
+ case 'C':
+ sort_opts_vals.cflag = true;
+ sort_opts_vals.csilentflag = true;
+ break;
+ case 'k':
+ {
+ sort_opts_vals.complex_sort = true;
+ sort_opts_vals.kflag = true;
+
+ keys_num++;
+ keys = sort_realloc(keys, keys_num *
+ sizeof(struct key_specs));
+ memset(&(keys[keys_num - 1]), 0,
+ sizeof(struct key_specs));
+
+ if (parse_k(optarg, &(keys[keys_num - 1]))
+ < 0) {
+ errc(2, EINVAL, "-k %s", optarg);
+ }
+
+ break;
+ }
+ case 'm':
+ sort_opts_vals.mflag = true;
+ break;
+ case 'o':
+ outfile = sort_realloc(outfile, (strlen(optarg) + 1));
+ strcpy(outfile, optarg);
+ break;
+ case 's':
+ sort_opts_vals.sflag = true;
+ break;
+ case 'S':
+ available_free_memory =
+ parse_memory_buffer_value(optarg);
+ break;
+ case 'T':
+ tmpdir = sort_strdup(optarg);
+ break;
+ case 't':
+ while (strlen(optarg) > 1) {
+ if (optarg[0] != '\\') {
+ errc(2, EINVAL, "%s", optarg);
+ }
+ optarg += 1;
+ if (*optarg == '0') {
+ *optarg = 0;
+ break;
+ }
+ }
+ sort_opts_vals.tflag = true;
+ sort_opts_vals.field_sep = btowc(optarg[0]);
+ if (sort_opts_vals.field_sep == WEOF) {
+ errno = EINVAL;
+ err(2, NULL);
+ }
+ if (!gnusort_numeric_compatibility) {
+ if (symbol_decimal_point == sort_opts_vals.field_sep)
+ symbol_decimal_point = WEOF;
+ if (symbol_thousands_sep == sort_opts_vals.field_sep)
+ symbol_thousands_sep = WEOF;
+ if (symbol_negative_sign == sort_opts_vals.field_sep)
+ symbol_negative_sign = WEOF;
+ if (symbol_positive_sign == sort_opts_vals.field_sep)
+ symbol_positive_sign = WEOF;
+ }
+ break;
+ case 'u':
+ sort_opts_vals.uflag = true;
+ /* stable sort for the correct unique val */
+ sort_opts_vals.sflag = true;
+ break;
+ case 'z':
+ sort_opts_vals.zflag = true;
+ break;
+ case SORT_OPT:
+ if (optarg) {
+ if (!strcmp(optarg, "general-numeric"))
+ set_sort_modifier(sm, 'g');
+ else if (!strcmp(optarg, "human-numeric"))
+ set_sort_modifier(sm, 'h');
+ else if (!strcmp(optarg, "numeric"))
+ set_sort_modifier(sm, 'n');
+ else if (!strcmp(optarg, "month"))
+ set_sort_modifier(sm, 'M');
+ else if (!strcmp(optarg, "random"))
+ set_sort_modifier(sm, 'R');
+ else
+ unknown(optarg);
+ }
+ break;
+#if defined(SORT_THREADS)
+ case PARALLEL_OPT:
+ nthreads = (size_t)(atoi(optarg));
+ if (nthreads < 1)
+ nthreads = 1;
+ if (nthreads > 1024)
+ nthreads = 1024;
+ break;
+#endif
+ case QSORT_OPT:
+ sort_opts_vals.sort_method = SORT_QSORT;
+ break;
+ case MERGESORT_OPT:
+ sort_opts_vals.sort_method = SORT_MERGESORT;
+ break;
+ case MMAP_OPT:
+ use_mmap = true;
+ break;
+ case HEAPSORT_OPT:
+ sort_opts_vals.sort_method = SORT_HEAPSORT;
+ break;
+ case RADIXSORT_OPT:
+ sort_opts_vals.sort_method = SORT_RADIXSORT;
+ break;
+ case RANDOMSOURCE_OPT:
+ random_source = strdup(optarg);
+ break;
+ case COMPRESSPROGRAM_OPT:
+ compress_program = strdup(optarg);
+ break;
+ case FF_OPT:
+ read_fns_from_file0(optarg);
+ break;
+ case BS_OPT:
+ {
+ errno = 0;
+ long mof = strtol(optarg, NULL, 10);
+ if (errno != 0)
+ err(2, "--batch-size");
+ if (mof >= 2)
+ max_open_files = (size_t) mof + 1;
+ }
+ break;
+ case VERSION_OPT:
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ /* NOTREACHED */
+ break;
+ case DEBUG_OPT:
+ debug_sort = true;
+ break;
+ case HELP_OPT:
+ usage(false);
+ /* NOTREACHED */
+ break;
+ default:
+ usage(true);
+ /* NOTREACHED */
+ }
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argv_from_file0) {
+ argc = argc_from_file0;
+ argv = argv_from_file0;
+ }
+
+#ifndef WITHOUT_NLS
+ catalog = catopen("sort", NL_CAT_LOCALE);
+#endif
+
+ if (sort_opts_vals.cflag && sort_opts_vals.mflag)
+ errx(1, "%c:%c: %s", 'm', 'c', getstr(1));
+
+#ifndef WITHOUT_NLS
+ catclose(catalog);
+#endif
+
+ if (keys_num == 0) {
+ keys_num = 1;
+ keys = sort_realloc(keys, sizeof(struct key_specs));
+ memset(&(keys[0]), 0, sizeof(struct key_specs));
+ keys[0].c1 = 1;
+ keys[0].pos1b = default_sort_mods->bflag;
+ keys[0].pos2b = default_sort_mods->bflag;
+ memcpy(&(keys[0].sm), default_sort_mods,
+ sizeof(struct sort_mods));
+ }
+
+ for (size_t i = 0; i < keys_num; i++) {
+ struct key_specs *ks;
+
+ ks = &(keys[i]);
+
+ if (sort_modifier_empty(&(ks->sm)) && !(ks->pos1b) &&
+ !(ks->pos2b)) {
+ ks->pos1b = sm->bflag;
+ ks->pos2b = sm->bflag;
+ memcpy(&(ks->sm), sm, sizeof(struct sort_mods));
+ }
+
+ ks->sm.func = get_sort_func(&(ks->sm));
+ }
+
+ if (debug_sort) {
+ printf("Memory to be used for sorting: %llu\n",available_free_memory);
+#if defined(SORT_THREADS)
+ printf("Number of CPUs: %d\n",(int)ncpu);
+ nthreads = 1;
+#endif
+ printf("Using collate rules of %s locale\n",
+ setlocale(LC_COLLATE, NULL));
+ if (byte_sort)
+ printf("Byte sort is used\n");
+ if (print_symbols_on_debug) {
+ printf("Decimal Point: <%lc>\n", symbol_decimal_point);
+ if (symbol_thousands_sep)
+ printf("Thousands separator: <%lc>\n",
+ symbol_thousands_sep);
+ printf("Positive sign: <%lc>\n", symbol_positive_sign);
+ printf("Negative sign: <%lc>\n", symbol_negative_sign);
+ }
+ }
+
+ set_random_seed();
+
+ /* Case when the outfile equals one of the input files: */
+ if (strcmp(outfile, "-")) {
+
+ for(int i = 0; i < argc; ++i) {
+ if (strcmp(argv[i], outfile) == 0) {
+ real_outfile = sort_strdup(outfile);
+ for(;;) {
+ char* tmp = sort_malloc(strlen(outfile) +
+ strlen(".tmp") + 1);
+
+ strcpy(tmp, outfile);
+ strcpy(tmp + strlen(tmp), ".tmp");
+ sort_free(outfile);
+ outfile = tmp;
+ if (access(outfile, F_OK) < 0)
+ break;
+ }
+ tmp_file_atexit(outfile);
+ }
+ }
+ }
+
+#if defined(SORT_THREADS)
+ if ((argc < 1) || (strcmp(outfile, "-") == 0) || (*outfile == 0))
+ nthreads = 1;
+#endif
+
+ if (!sort_opts_vals.cflag && !sort_opts_vals.mflag) {
+ struct file_list fl;
+ struct sort_list list;
+
+ sort_list_init(&list);
+ file_list_init(&fl, true);
+
+ if (argc < 1)
+ procfile("-", &list, &fl);
+ else {
+ while (argc > 0) {
+ procfile(*argv, &list, &fl);
+ --argc;
+ ++argv;
+ }
+ }
+
+ if (fl.count < 1)
+ sort_list_to_file(&list, outfile);
+ else {
+ if (list.count > 0) {
+ char *flast = new_tmp_file_name();
+
+ sort_list_to_file(&list, flast);
+ file_list_add(&fl, flast, false);
+ }
+ merge_files(&fl, outfile);
+ }
+
+ file_list_clean(&fl);
+
+ /*
+ * We are about to exit the program, so we can ignore
+ * the clean-up for speed
+ *
+ * sort_list_clean(&list);
+ */
+
+ } else if (sort_opts_vals.cflag) {
+ result = (argc == 0) ? (check("-")) : (check(*argv));
+ } else if (sort_opts_vals.mflag) {
+ struct file_list fl;
+
+ file_list_init(&fl, false);
+ file_list_populate(&fl, argc, argv, true);
+ merge_files(&fl, outfile);
+ file_list_clean(&fl);
+ }
+
+ if (real_outfile) {
+ unlink(real_outfile);
+ if (rename(outfile, real_outfile) < 0)
+ err(2, NULL);
+ sort_free(real_outfile);
+ }
+
+ sort_free(outfile);
+
+ return (result);
+}
diff --git a/text_cmds/sort/sort.h b/text_cmds/sort/sort.h
new file mode 100644
index 0000000..d31b7cb
--- /dev/null
+++ b/text_cmds/sort/sort.h
@@ -0,0 +1,139 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(__BSD_SORT_H__)
+#define __BSD_SORT_H__
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <wchar.h>
+
+#include <sys/types.h>
+#ifdef __APPLE__
+#include "commoncrypto.h"
+#else
+#include <md5.h>
+#endif
+
+#ifdef __APPLE__
+#define VERSION "2.3-Apple (" SORT_VERSION ")"
+#else
+#define VERSION "2.3-FreeBSD"
+#endif
+
+#ifdef WITHOUT_NLS
+#define getstr(n) nlsstr[n]
+#else
+#include <nl_types.h>
+
+extern nl_catd catalog;
+#define getstr(n) catgets(catalog, 1, n, nlsstr[n])
+#endif
+
+extern const char *nlsstr[];
+
+#if defined(SORT_THREADS)
+#define MT_SORT_THRESHOLD (10000)
+extern unsigned int ncpu;
+extern size_t nthreads;
+#endif
+
+/*
+ * If true, we output some debug information.
+ */
+extern bool debug_sort;
+
+/*
+ * MD5 context for random hash function
+ */
+extern MD5_CTX md5_ctx;
+
+/*
+ * sort.c
+ */
+
+/*
+ * This structure holds main sort options which are NOT affecting the sort ordering.
+ */
+struct sort_opts
+{
+ wint_t field_sep;
+ int sort_method;
+ bool cflag;
+ bool csilentflag;
+ bool kflag;
+ bool mflag;
+ bool sflag;
+ bool uflag;
+ bool zflag;
+ bool tflag;
+ bool complex_sort;
+};
+
+/*
+ * Key value structure forward declaration
+ */
+struct key_value;
+
+/*
+ * Cmp function
+ */
+typedef int (*cmpcoll_t)(struct key_value *kv1, struct key_value *kv2, size_t offset);
+
+/*
+ * This structure holds "sort modifiers" - options which are affecting the sort ordering.
+ */
+struct sort_mods
+{
+ cmpcoll_t func;
+ bool bflag;
+ bool dflag;
+ bool fflag;
+ bool gflag;
+ bool iflag;
+ bool Mflag;
+ bool nflag;
+ bool rflag;
+ bool Rflag;
+ bool Vflag;
+ bool hflag;
+};
+
+extern bool need_hint;
+
+extern struct sort_opts sort_opts_vals;
+
+extern struct sort_mods * const default_sort_mods;
+
+extern int (*isblank_f)(int c);
+extern int (*iswblank_f)(wint_t c);
+
+#endif /* __BSD_SORT_H__ */
diff --git a/text_cmds/sort/testsuite/README.txt b/text_cmds/sort/testsuite/README.txt
new file mode 100644
index 0000000..865094f
--- /dev/null
+++ b/text_cmds/sort/testsuite/README.txt
@@ -0,0 +1,19 @@
+To run the tests:
+
+1) Adjust the variable TESTED_SORT in the file run.sh - the value must point
+to the binary that is to be tested.
+
+2) Adjust the value ORIG_SORT in the file run.sh - the value must point to the binary that is assumed
+to be working correctly. The tested sort binary will be checked against this program.
+
+3) Run:
+
+$ cd <...>/testsuite/
+$ ./run.sh
+
+4) Wait for many hours, it is running about 23 hours on my laptop.
+
+5) Check the output and check the existence of the file errors.log in the current directory.
+If the test run has been successful, then there must be no file errors.log.
+
+
diff --git a/text_cmds/sort/testsuite/bigsample.txt.xz b/text_cmds/sort/testsuite/bigsample.txt.xz
new file mode 100644
index 0000000..ca120b7
--- /dev/null
+++ b/text_cmds/sort/testsuite/bigsample.txt.xz
Binary files differ
diff --git a/text_cmds/sort/testsuite/run.sh b/text_cmds/sort/testsuite/run.sh
new file mode 100755
index 0000000..db13a34
--- /dev/null
+++ b/text_cmds/sort/testsuite/run.sh
@@ -0,0 +1,436 @@
+#!/bin/sh
+
+#export GNUSORT_NUMERIC_COMPATIBILITY=x
+#export GNUSORT_COMPATIBLE_BLANKS=x
+
+TESTED_SORT=../../text_cmds/sort/sort
+ORIG_SORT=../../text_cmds_orig/sort/sort
+
+FILECMP=cmp
+
+INPUT_FILE=sample.txt
+BIG_INPUT_FILE=bigsample.txt
+
+ERRORS_FILE=errors.log
+
+OUT_DIR=tmp
+
+# clean
+
+rm -rf ${OUT_DIR}
+mkdir -p ${OUT_DIR}
+rm -rf ${ERRORS_FILE}
+
+# ru_RU.KOI8-R C ru_RU.ISO-8859-5 en_US.ISO8859-15 zh_HK.Big5HKSCS
+#
+# ru KOI-8 is an "irregular" locale with non-trivial ordering.
+# zh* is a 2-bytes locale.
+
+for lang in en_US.UTF-8 C en_US.ISO8859-15 zh_HK.Big5HKSCS ru_RU.KOI8-R ru_RU.ISO-8859-5
+do
+
+ export LANG=${lang}
+
+ for KEYS in -srh -sfrudb -Vs -sM -siz
+ do
+
+ echo ${LANG} ${KEYS}
+
+ if [ ${LANG} = "ru_RU.KOI8-R" ] && [ ${KEYS} = "-srh" ] ; then
+
+ # numeric sorting in ru_RU.KOI8-R incompatible because the thousands separator bug fixed,
+ # for better compatibility with the new GNU sort.
+ # (ru_RU.KOI8-R uses space as thousands separator)
+
+ continue
+ fi
+
+ time ${ORIG_SORT} ${KEYS} ${BIG_INPUT_FILE} -o ${OUT_DIR}/big_orig
+
+ for PARALLEL in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+ do
+
+ echo --parallel ${PARALLEL}
+
+ time ${TESTED_SORT} --parallel ${PARALLEL} ${KEYS} ${BIG_INPUT_FILE} -o ${OUT_DIR}/big_new
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} big crash --parallel ${PARALLEL} >> ${ERRORS_FILE}
+ exit
+ fi
+
+ if ! ${FILECMP} ${OUT_DIR}/big_new ${OUT_DIR}/big_orig >${OUT_DIR}/res.0.0.big 2>&1 ; then
+ echo ${LANG} ${KEYS} big error --parallel ${PARALLEL} >> ${ERRORS_FILE}
+ fi
+ time ${TESTED_SORT} --parallel ${PARALLEL} -c ${KEYS} ${OUT_DIR}/big_new
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -c big error --parallel ${PARALLEL} >> ${ERRORS_FILE}
+ fi
+ rm -rf ${OUT_DIR}/res.0.0.big
+ rm -rf ${OUT_DIR}/big_new
+ done
+
+ rm -rf ${OUT_DIR}/big_orig
+
+ ${TESTED_SORT} ${KEYS} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.0.0 2>&1 ; then
+ echo ${LANG} ${KEYS} error >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.0.0
+
+ ${TESTED_SORT} ${KEYS} -t " " ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t " " crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t " " ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.0.0 2>&1 ; then
+ echo ${LANG} ${KEYS} error -t " " >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t " " ${KEYS} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo $? ${LANG} ${KEYS} -t " " -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.0.0
+
+ ${TESTED_SORT} ${KEYS} -t "|" ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t "|" crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t "|" ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.0.0 2>&1 ; then
+ echo ${LANG} ${KEYS} error -t "|" >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t "|" ${KEYS} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -c -t "|" error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.0.0
+
+ ${TESTED_SORT} ${KEYS} -t '\0' ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t 0 crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t '\0' ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.0.0 2>&1 ; then
+ echo ${LANG} ${KEYS} error -t '\0' >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t '\0' ${KEYS} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -c -t '\0' error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.0.0
+
+ for f1 in 1 2 3 4 5 6 7 8 9
+ do
+ for c1 in 1 2 3 4 5 10 15 20 25 30
+ do
+ echo ${LANG} ${KEYS} ${f1} ${c1}
+
+ ${TESTED_SORT} ${KEYS} +${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} crash +- >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} +${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} error +- >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} +${f1}.${c1} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} -c error +- >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+
+ ${TESTED_SORT} ${KEYS} -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} error >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} -k${f1}.${c1} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+
+ ${TESTED_SORT} ${KEYS} -k${f1}.${c1}b ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -k${f1}.${c1}b ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b error >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} -k${f1}.${c1}b ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+
+ ${TESTED_SORT} ${KEYS} -t " " -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t -k${f1}.${c1} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t " " -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} error -t " " >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t " " ${KEYS} -k${f1}.${c1} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} -t " " -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+
+ if [ ${LANG} != "ru_RU.KOI8-R" ] ; then
+
+ # numeric sorting in ru_RU.KOI8-R incompatible because the thousands separator bug fixed,
+ # for better compatibility with the new GNU sort.
+ # (ru_RU.KOI8-R uses space as thousands separator)
+
+ ${TESTED_SORT} ${KEYS} -t " " -k${f1}.${c1}n ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}n crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t " " -k${f1}.${c1}n ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} error -t " " n >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t " " ${KEYS} -k${f1}.${c1}n ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} -c -t " " n error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+ fi
+
+ ${TESTED_SORT} ${KEYS} -t "|" -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t "|" -k${f1}.${c1} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t "|" -k${f1}.${c1} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} error -t "|" >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t "|" ${KEYS} -k${f1}.${c1} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1} -c -t "|" error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}
+
+ for f2 in 1 2 3 4 5 6 7 8 9 10
+ do
+ for c2 in 0 1 2 3 4 5 10 15 20 25 30
+ do
+ echo ${LANG} ${KEYS} ${f1} ${c1} ${f2} ${c2}
+
+ ${TESTED_SORT} ${KEYS} +${f1}.${c1} -${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} -${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} +${f1}.${c1} -${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} -${f2}.${c2} error +- >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} +${f1}.${c1} -${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} +${f1}.${c1} -${f2}.${c2} -c error +- >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.${f2}.${c2} error >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} -k${f1}.${c1},${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.b.${f2}.${c2} error >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} -k${f1}.${c1}b,${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -c error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -t " " -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t " " -k${f1}.${c1},${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t " " -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.${f2}.${c2} error -t " " >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t " " ${KEYS} -k${f1}.${c1},${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c -t " " error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ if [ ${LANG} != "ru_RU.KOI8-R" ] ; then
+
+ # numeric sorting in ru_RU.KOI8-R incompatible because the thousands separator bug fixed,
+ # for better compatibility with the new GNU sort.
+ # (ru_RU.KOI8-R uses space as thousands separator)
+
+ ${TESTED_SORT} ${KEYS} -t " " -k${f1}.${c1}n,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t " " -k${f1}.${c1}n,${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t " " -k${f1}.${c1}n,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -t " " -k${f1}.${c1}.${f2}.${c2} error n >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t " " ${KEYS} -k${f1}.${c1}n,${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c -t " " n error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -t '\0' -k${f1}.${c1}n,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t '\0' -k${f1}.${c1}n,${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t '\0' -k${f1}.${c1}n,${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.${f2}.${c2} error -t '\0' n >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t '\0' ${KEYS} -k${f1}.${c1}n,${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c -t '\0' n error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+ fi
+
+ ${TESTED_SORT} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.${f2}.${c2} error -t "|" >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t "|" ${KEYS} -k${f1}.${c1},${f2}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c -t "|" error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} -k${f2}.${c1},${f1}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -t "|" -k${f1}.${c1},${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.${f2}.${c2} error -t "|" 2k >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c -t "|" ${KEYS} -k${f1}.${c1},${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1},${f2}.${c2} -c -t "|" 2k error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ ${TESTED_SORT} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -k${f2}.${c1},${f1}.${c2} crash >> ${ERRORS_FILE}
+ exit
+ fi
+ ${ORIG_SORT} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${INPUT_FILE} -o ${OUT_DIR}/sik2
+ if ! ${FILECMP} ${OUT_DIR}/sik1 ${OUT_DIR}/sik2 >${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2} 2>&1 ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}.b.${f2}.${c2} error 2k >> ${ERRORS_FILE}
+ fi
+ ${TESTED_SORT} -c ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -k${f2}.${c1},${f1}.${c2} ${OUT_DIR}/sik1
+ ER=$?
+ if ! [ ${ER} -eq 0 ] ; then
+ echo ${LANG} ${KEYS} -k${f1}.${c1}b,${f2}.${c2} -c 2k error >> ${ERRORS_FILE}
+ fi
+ rm ${OUT_DIR}/res.${f1}.${c1}.${f2}.${c2}
+
+ done
+ done
+ done
+ done
+ done
+done
+
+if [ -f ${ERRORS_FILE} ] ; then
+ echo TEST FAILED
+else
+ echo TEST SUCCEEDED
+fi
diff --git a/text_cmds/sort/testsuite/sample.txt b/text_cmds/sort/testsuite/sample.txt
new file mode 100644
index 0000000..74d1477
--- /dev/null
+++ b/text_cmds/sort/testsuite/sample.txt
Binary files differ
diff --git a/text_cmds/sort/vsort.c b/text_cmds/sort/vsort.c
new file mode 100644
index 0000000..abc8647
--- /dev/null
+++ b/text_cmds/sort/vsort.c
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.bin/sort/vsort.c 281132 2015-04-06 02:35:55Z pfg $");
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sort.h"
+#include "vsort.h"
+
+static inline bool
+isdigit_clocale(wchar_t c)
+{
+
+ return (c >= L'0' && c <= L'9');
+}
+
+static inline bool
+isalpha_clocale(wchar_t c)
+{
+
+ return ((c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z'));
+}
+
+static inline bool
+isalnum_clocale(wchar_t c)
+{
+
+ return ((c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') ||
+ (c >= L'0' && c <= L'9'));
+}
+
+/*
+ * Find string suffix of format: (\.[A-Za-z~][A-Za-z0-9~]*)*$
+ * Set length of string before suffix.
+ */
+static void
+find_suffix(bwstring_iterator si, bwstring_iterator se, size_t *len)
+{
+ wchar_t c;
+ size_t clen;
+ bool expect_alpha, sfx;
+
+ sfx = false;
+ expect_alpha = false;
+ *len = 0;
+ clen = 0;
+
+ while ((si < se) && (c = bws_get_iter_value(si))) {
+ if (expect_alpha) {
+ expect_alpha = false;
+ if (!isalpha_clocale(c) && (c != L'~'))
+ sfx = false;
+ } else if (c == L'.') {
+ expect_alpha = true;
+ if (!sfx) {
+ sfx = true;
+ *len = clen;
+ }
+ } else if (!isalnum_clocale(c) && (c != L'~'))
+ sfx = false;
+
+ si = bws_iterator_inc(si, 1);
+ ++clen;
+ }
+
+ /* This code must be here to make the implementation compatible
+ * with WORDING of GNU sort documentation.
+ * But the GNU sort implementation is not following its own
+ * documentation. GNU sort allows empty file extensions
+ * (just dot with nothing after); but the regular expression in
+ * their documentation does not allow empty file extensions.
+ * We chose to make our implementation compatible with GNU sort
+ * implementation. If they will ever fix their bug, this code
+ * must be uncommented. Or they may choose to fix the info page,
+ * then the code stays commented.
+ *
+ if (expect_alpha)
+ sfx = false;
+ */
+
+ if (!sfx)
+ *len = clen;
+}
+
+static inline int
+cmp_chars(wchar_t c1, wchar_t c2)
+{
+
+ if (c1 == c2)
+ return (0);
+
+ if (c1 == L'~')
+ return (-1);
+ if (c2 == L'~')
+ return (+1);
+
+ if (isdigit_clocale(c1) || !c1)
+ return ((isdigit_clocale(c2) || !c2) ? 0 : -1);
+
+ if (isdigit_clocale(c2) || !c2)
+ return (+1);
+
+ if (isalpha_clocale(c1))
+ return ((isalpha_clocale(c2)) ? ((int) c1 - (int) c2) : -1);
+
+ if (isalpha_clocale(c2))
+ return (+1);
+
+ return ((int) c1 - (int) c2);
+}
+
+static int
+cmpversions(bwstring_iterator si1, bwstring_iterator se1,
+ bwstring_iterator si2, bwstring_iterator se2)
+{
+ int cmp, diff;
+
+ while ((si1 < se1) || (si2 < se2)) {
+ diff = 0;
+
+ while (((si1 < se1) &&
+ !isdigit_clocale(bws_get_iter_value(si1))) ||
+ ((si2 < se2) && !isdigit_clocale(bws_get_iter_value(si2)))) {
+ wchar_t c1, c2;
+
+ c1 = (si1 < se1) ? bws_get_iter_value(si1) : 0;
+ c2 = (si2 < se2) ? bws_get_iter_value(si2) : 0;
+
+ cmp = cmp_chars(c1, c2);
+ if (cmp)
+ return (cmp);
+
+ if (si1 < se1)
+ si1 = bws_iterator_inc(si1, 1);
+ if (si2 < se2)
+ si2 = bws_iterator_inc(si2, 1);
+ }
+
+ while (bws_get_iter_value(si1) == L'0')
+ si1 = bws_iterator_inc(si1, 1);
+
+ while (bws_get_iter_value(si2) == L'0')
+ si2 = bws_iterator_inc(si2, 1);
+
+ while (isdigit_clocale(bws_get_iter_value(si1)) &&
+ isdigit_clocale(bws_get_iter_value(si2))) {
+ if (!diff)
+ diff = ((int)bws_get_iter_value(si1) -
+ (int)bws_get_iter_value(si2));
+ si1 = bws_iterator_inc(si1, 1);
+ si2 = bws_iterator_inc(si2, 1);
+ }
+
+ if (isdigit_clocale(bws_get_iter_value(si1)))
+ return (1);
+
+ if (isdigit_clocale(bws_get_iter_value(si2)))
+ return (-1);
+
+ if (diff)
+ return (diff);
+ }
+
+ return (0);
+}
+
+/*
+ * Compare two version strings
+ */
+int
+vcmp(struct bwstring *s1, struct bwstring *s2)
+{
+ bwstring_iterator si1, si2;
+ wchar_t c1, c2;
+ size_t len1, len2, slen1, slen2;
+ int cmp_bytes, cmp_res;
+
+ if (s1 == s2)
+ return (0);
+
+ cmp_bytes = bwscmp(s1, s2, 0);
+ if (cmp_bytes == 0)
+ return (0);
+
+ len1 = slen1 = BWSLEN(s1);
+ len2 = slen2 = BWSLEN(s2);
+
+ if (slen1 < 1)
+ return (-1);
+ if (slen2 < 1)
+ return (+1);
+
+ si1 = bws_begin(s1);
+ si2 = bws_begin(s2);
+
+ c1 = bws_get_iter_value(si1);
+ c2 = bws_get_iter_value(si2);
+
+ if (c1 == L'.' && (slen1 == 1))
+ return (-1);
+
+ if (c2 == L'.' && (slen2 == 1))
+ return (+1);
+
+ if (slen1 == 2 && c1 == L'.' &&
+ bws_get_iter_value(bws_iterator_inc(si1, 1)) == L'.')
+ return (-1);
+ if (slen2 == 2 && c2 == L'.' &&
+ bws_get_iter_value(bws_iterator_inc(si2, 1)) == L'.')
+ return (+1);
+
+ if (c1 == L'.' && c2 != L'.')
+ return (-1);
+ if (c1 != L'.' && c2 == L'.')
+ return (+1);
+
+ if (c1 == L'.' && c2 == L'.') {
+ si1 = bws_iterator_inc(si1, 1);
+ si2 = bws_iterator_inc(si2, 1);
+ }
+
+ find_suffix(si1, bws_end(s1), &len1);
+ find_suffix(si2, bws_end(s2), &len2);
+
+ if ((len1 == len2) && (bws_iterator_cmp(si1, si2, len1) == 0))
+ return (cmp_bytes);
+
+ cmp_res = cmpversions(si1, bws_iterator_inc(si1, len1), si2,
+ bws_iterator_inc(si2, len2));
+
+ if (cmp_res == 0)
+ cmp_res = cmp_bytes;
+
+ return (cmp_res);
+}
diff --git a/text_cmds/sort/vsort.h b/text_cmds/sort/vsort.h
new file mode 100644
index 0000000..5ef45d5
--- /dev/null
+++ b/text_cmds/sort/vsort.h
@@ -0,0 +1,37 @@
+/* $FreeBSD: head/usr.bin/sort/vsort.h 264744 2014-04-21 22:52:18Z pfg $ */
+
+/*-
+ * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
+ * Copyright (C) 2012 Gabor Kovesdan <gabor@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _VSORT_H_
+#define _VSORT_H_
+
+#include "bwstring.h"
+
+int vcmp(struct bwstring *s1, struct bwstring *s2);
+
+#endif
diff --git a/text_cmds/split/split.1 b/text_cmds/split/split.1
new file mode 100644
index 0000000..39818e0
--- /dev/null
+++ b/text_cmds/split/split.1
@@ -0,0 +1,142 @@
+.\" Copyright (c) 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)split.1 8.3 (Berkeley) 4/16/94
+.\" $FreeBSD: src/usr.bin/split/split.1,v 1.17 2005/08/30 12:48:28 tjr Exp $
+.\"
+.Dd August 21, 2005
+.Dt SPLIT 1
+.Os
+.Sh NAME
+.Nm split
+.Nd split a file into pieces
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar suffix_length
+.Op Fl b Ar byte_count[k|m]
+.Op Fl l Ar line_count
+.Op Fl p Ar pattern
+.Op Ar file Op Ar name
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the given
+.Ar file
+and breaks it up into files of 1000 lines each.
+If
+.Ar file
+is a single dash
+.Pq Sq Fl
+or absent,
+.Nm
+reads from the standard input.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar suffix_length
+Use
+.Ar suffix_length
+letters to form the suffix of the file name.
+.It Fl b Ar byte_count[k|m]
+Create smaller files
+.Ar byte_count
+bytes in length.
+If
+.Dq Li k
+is appended to the number, the file is split into
+.Ar byte_count
+kilobyte pieces.
+If
+.Dq Li m
+is appended to the number, the file is split into
+.Ar byte_count
+megabyte pieces.
+.It Fl l Ar line_count
+Create smaller files
+.Ar n
+lines in length.
+.It Fl p Ar pattern
+The file is split whenever an input line matches
+.Ar pattern ,
+which is interpreted as an extended regular expression.
+The matching line will be the first line of the next output file.
+This option is incompatible with the
+.Fl b
+and
+.Fl l
+options.
+.El
+.Pp
+If additional arguments are specified, the first is used as the name
+of the input file which is to be split.
+If a second additional argument is specified, it is used as a prefix
+for the names of the files into which the file is split.
+In this case, each file into which the file is split is named by the
+prefix followed by a lexically ordered suffix using
+.Ar suffix_length
+characters in the range
+.Dq Li a-z .
+If
+.Fl a
+is not specified, two letters are used as the suffix.
+.Pp
+If the
+.Ar name
+argument is not specified, the file is split into lexically ordered
+files named with the prefix
+.Dq Li x
+and with suffixes as above.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_CTYPE
+and
+.Ev LC_COLLATE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr csplit 1 ,
+.Xr re_format 7
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v3 .
+.Sh BUGS
+The maximum line length for matching patterns is 65536.
diff --git a/text_cmds/split/split.c b/text_cmds/split/split.c
new file mode 100644
index 0000000..3c47a18
--- /dev/null
+++ b/text_cmds/split/split.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/split/split.c,v 1.17 2005/08/30 12:32:18 tjr Exp $");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)split.c 8.2 (Berkeley) 4/16/94";
+#endif
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <regex.h>
+#include <sysexits.h>
+
+#define DEFLINE 1000 /* Default num lines per file. */
+
+off_t bytecnt; /* Byte count to split on. */
+long numlines; /* Line count to split on. */
+int file_open; /* If a file open. */
+int ifd = -1, ofd = -1; /* Input/output file descriptors. */
+char bfr[MAXBSIZE]; /* I/O buffer. */
+char fname[MAXPATHLEN]; /* File name prefix. */
+regex_t rgx;
+int pflag;
+long sufflen = 2; /* File name suffix length. */
+
+void newfile(void);
+void split1(void);
+void split2(void);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ intmax_t bytecnti;
+ long scale;
+ int ch;
+ char *ep, *p;
+
+ setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "0123456789a:b:l:p:")) != -1)
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * Undocumented kludge: split was originally designed
+ * to take a number after a dash.
+ */
+ if (numlines == 0) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ numlines = strtol(++p, &ep, 10);
+ else
+ numlines =
+ strtol(argv[optind] + 1, &ep, 10);
+ if (numlines <= 0 || *ep)
+ errx(EX_USAGE,
+ "%s: illegal line count", optarg);
+ }
+ break;
+ case 'a': /* Suffix length */
+ if ((sufflen = strtol(optarg, &ep, 10)) <= 0 || *ep)
+ errx(EX_USAGE,
+ "%s: illegal suffix length", optarg);
+ break;
+ case 'b': /* Byte count. */
+ errno = 0;
+ if ((bytecnti = strtoimax(optarg, &ep, 10)) <= 0 ||
+ (*ep != '\0' && *ep != 'k' && *ep != 'm') ||
+ errno != 0)
+ errx(EX_USAGE,
+ "%s: illegal byte count", optarg);
+ if (*ep == 'k')
+ scale = 1024;
+ else if (*ep == 'm')
+ scale = 1024 * 1024;
+ else
+ scale = 1;
+ if (bytecnti > OFF_MAX / scale)
+ errx(EX_USAGE, "%s: offset too large", optarg);
+ bytecnt = (off_t)(bytecnti * scale);
+ break;
+ case 'p' : /* pattern matching. */
+ if (regcomp(&rgx, optarg, REG_EXTENDED|REG_NOSUB) != 0)
+ errx(EX_USAGE, "%s: illegal regexp", optarg);
+ pflag = 1;
+ break;
+ case 'l': /* Line count. */
+ if (numlines != 0)
+ usage();
+ if ((numlines = strtol(optarg, &ep, 10)) <= 0 || *ep)
+ errx(EX_USAGE,
+ "%s: illegal line count", optarg);
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (*argv != NULL) { /* Input file. */
+ if (strcmp(*argv, "-") == 0)
+ ifd = STDIN_FILENO;
+ else if ((ifd = open(*argv, O_RDONLY, 0)) < 0)
+ err(EX_NOINPUT, "%s", *argv);
+ ++argv;
+ }
+ if (*argv != NULL) /* File name prefix. */
+ if (strlcpy(fname, *argv++, sizeof(fname)) >= sizeof(fname))
+ errx(EX_USAGE, "file name prefix is too long");
+ if (*argv != NULL)
+ usage();
+
+ if (strlen(fname) + (unsigned long)sufflen >= sizeof(fname))
+ errx(EX_USAGE, "suffix is too long");
+ if (pflag && (numlines != 0 || bytecnt != 0))
+ usage();
+
+ if (numlines == 0)
+ numlines = DEFLINE;
+ else if (bytecnt != 0)
+ usage();
+
+ if (ifd == -1) /* Stdin by default. */
+ ifd = 0;
+
+ if (bytecnt) {
+ split1();
+ exit (0);
+ }
+ split2();
+ if (pflag)
+ regfree(&rgx);
+ exit(0);
+}
+
+/*
+ * split1 --
+ * Split the input by bytes.
+ */
+void
+split1(void)
+{
+ off_t bcnt;
+ char *C;
+ ssize_t dist, len;
+
+ for (bcnt = 0;;)
+ switch ((len = read(ifd, bfr, MAXBSIZE))) {
+ case 0:
+ exit(0);
+ case -1:
+ err(EX_IOERR, "read");
+ /* NOTREACHED */
+ default:
+ if (!file_open)
+ newfile();
+ if (bcnt + len >= bytecnt) {
+ dist = bytecnt - bcnt;
+ if (write(ofd, bfr, dist) != dist)
+ err(EX_IOERR, "write");
+ len -= dist;
+ for (C = bfr + dist; len >= bytecnt;
+ len -= bytecnt, C += bytecnt) {
+ newfile();
+ if (write(ofd,
+ C, bytecnt) != bytecnt)
+ err(EX_IOERR, "write");
+ }
+ if (len != 0) {
+ newfile();
+ if (write(ofd, C, len) != len)
+ err(EX_IOERR, "write");
+ } else
+ file_open = 0;
+ bcnt = len;
+ } else {
+ bcnt += len;
+ if (write(ofd, bfr, len) != len)
+ err(EX_IOERR, "write");
+ }
+ }
+}
+
+/*
+ * split2 --
+ * Split the input by lines.
+ */
+void
+split2(void)
+{
+ long lcnt = 0;
+ FILE *infp;
+
+ /* Stick a stream on top of input file descriptor */
+ if ((infp = fdopen(ifd, "r")) == NULL)
+ err(EX_NOINPUT, "fdopen");
+
+ /* Process input one line at a time */
+ while (fgets(bfr, sizeof(bfr), infp) != NULL) {
+ const int len = strlen(bfr);
+
+ /* If line is too long to deal with, just write it out */
+ if (bfr[len - 1] != '\n')
+ goto writeit;
+
+ /* Check if we need to start a new file */
+ if (pflag) {
+ regmatch_t pmatch;
+
+ pmatch.rm_so = 0;
+ pmatch.rm_eo = len - 1;
+ if (regexec(&rgx, bfr, 0, &pmatch, REG_STARTEND) == 0)
+ newfile();
+ } else if (lcnt++ == numlines) {
+ newfile();
+ lcnt = 1;
+ }
+
+writeit:
+ /* Open output file if needed */
+ if (!file_open)
+ newfile();
+
+ /* Write out line */
+ if (write(ofd, bfr, len) != len)
+ err(EX_IOERR, "write");
+ }
+
+ /* EOF or error? */
+ if (ferror(infp))
+ err(EX_IOERR, "read");
+ else
+ exit(0);
+}
+
+/*
+ * newfile --
+ * Open a new output file.
+ */
+void
+newfile(void)
+{
+ long i, maxfiles, tfnum;
+ static long fnum;
+ static int defname;
+ static char *fpnt;
+
+ if (ofd == -1) {
+ if (fname[0] == '\0') {
+ fname[0] = 'x';
+ fpnt = fname + 1;
+ defname = 1;
+ } else {
+ fpnt = fname + strlen(fname);
+ defname = 0;
+ }
+ ofd = fileno(stdout);
+ }
+
+ /* maxfiles = 26^sufflen, but don't use libm. */
+ for (maxfiles = 1, i = 0; i < sufflen; i++)
+ if ((maxfiles *= 26) <= 0)
+ errx(EX_USAGE, "suffix is too long (max %ld)", i);
+
+ if (fnum == maxfiles)
+ errx(EX_DATAERR, "too many files");
+
+ /* Generate suffix of sufflen letters */
+ tfnum = fnum;
+ i = sufflen - 1;
+ do {
+ fpnt[i] = tfnum % 26 + 'a';
+ tfnum /= 26;
+ } while (i-- > 0);
+ fpnt[sufflen] = '\0';
+
+ ++fnum;
+ if (!freopen(fname, "w", stdout))
+ err(EX_IOERR, "%s", fname);
+ file_open = 1;
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+"usage: split [-a sufflen] [-b byte_count] [-l line_count] [-p pattern]\n");
+ (void)fprintf(stderr,
+" [file [prefix]]\n");
+ exit(EX_USAGE);
+}
diff --git a/text_cmds/tail/extern.h b/text_cmds/tail/extern.h
new file mode 100644
index 0000000..d956680
--- /dev/null
+++ b/text_cmds/tail/extern.h
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+#define WR(p, size) do { \
+ if (write(STDOUT_FILENO, p, size) != (ssize_t)size) \
+ oerr(); \
+ } while(0)
+
+#define TAILMAPLEN (4<<20)
+
+struct mapinfo {
+ off_t mapoff;
+ off_t maxoff;
+ size_t maplen;
+ char *start;
+ int fd;
+};
+
+struct file_info {
+ FILE *fp;
+ char *file_name;
+ struct stat st;
+};
+
+typedef struct file_info file_info_t;
+
+enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
+
+void follow(file_info_t *, enum STYLE, off_t);
+void forward(FILE *, enum STYLE, off_t, struct stat *);
+void reverse(FILE *, enum STYLE, off_t, struct stat *);
+
+int bytes(FILE *, off_t);
+int lines(FILE *, off_t);
+
+void ierr(void);
+void oerr(void);
+int mapprint(struct mapinfo *, off_t, off_t);
+int maparound(struct mapinfo *, off_t);
+
+extern int Fflag, fflag, qflag, rflag, rval, no_files;
+extern const char *fname;
diff --git a/text_cmds/tail/forward.c b/text_cmds/tail/forward.c
new file mode 100644
index 0000000..172898a
--- /dev/null
+++ b/text_cmds/tail/forward.c
@@ -0,0 +1,520 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/event.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void rlines(FILE *, off_t, struct stat *);
+static void show(file_info_t *);
+static void set_events(file_info_t *files);
+
+/* defines for inner loop actions */
+#define USE_SLEEP 0
+#define USE_KQUEUE 1
+#define ADD_EVENTS 2
+
+struct kevent *ev;
+int action = USE_SLEEP;
+int kq;
+
+static const file_info_t *last;
+
+/*
+ * forward -- display the file, from an offset, forward.
+ *
+ * There are eight separate cases for this -- regular and non-regular
+ * files, by bytes or lines and from the beginning or end of the file.
+ *
+ * FBYTES byte offset from the beginning of the file
+ * REG seek
+ * NOREG read, counting bytes
+ *
+ * FLINES line offset from the beginning of the file
+ * REG read, counting lines
+ * NOREG read, counting lines
+ *
+ * RBYTES byte offset from the end of the file
+ * REG seek
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * RLINES
+ * REG mmap the file and step back until reach the correct offset.
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ */
+void
+forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+ int ch;
+
+ switch(style) {
+ case FBYTES:
+ if (off == 0)
+ break;
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size < off)
+ off = sbp->st_size;
+ if (fseeko(fp, off, SEEK_SET) == -1) {
+ ierr();
+ return;
+ }
+ } else while (off--)
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ break;
+ case FLINES:
+ if (off == 0)
+ break;
+ for (;;) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ if (ch == '\n' && !--off)
+ break;
+ }
+ break;
+ case RBYTES:
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size >= off &&
+ fseeko(fp, -off, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else
+ if (bytes(fp, off))
+ return;
+ break;
+ case RLINES:
+ if (S_ISREG(sbp->st_mode))
+ if (!off) {
+ if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else
+ rlines(fp, off, sbp);
+ else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else
+ if (lines(fp, off))
+ return;
+ break;
+ default:
+ break;
+ }
+
+ while ((ch = getc(fp)) != EOF)
+ if (putchar(ch) == EOF)
+ oerr();
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ (void)fflush(stdout);
+}
+
+/*
+ * rlines -- display the last offset lines of the file.
+ */
+static void
+rlines(fp, off, sbp)
+ FILE *fp;
+ off_t off;
+ struct stat *sbp;
+{
+#ifdef __APPLE__
+ /* Using mmap on network filesystems can frequently lead
+ to distress, and even on local file systems other processes
+ truncating the file can also lead to upset. */
+
+ /* Seek to sbp->st_blksize before the end of the file, find
+ all the newlines. If there are enough, print the last off
+ lines. Otherwise go back another sbp->st_blksize bytes,
+ and count newlines. Avoid re-reading blocks when possible. */
+
+ // +1 because we capture line ends and we want off line _starts_,
+ // +1 because the first line might be partial when try_at != 0
+ off_t search_for = off +2;
+ off_t try_at = sbp->st_size;
+ off_t last_try = sbp->st_size;
+ off_t found_this_pass = 0;
+ off_t found_total = 0;
+ off_t *found_at = calloc(search_for, sizeof(off_t));
+
+ flockfile(fp);
+
+ if (found_at == NULL) {
+ ierr();
+ goto done;
+ }
+
+ if (off == 0 || sbp->st_size == 0) {
+ goto done;
+ }
+
+ /* The last character is special. Check to make sure that it is a \n,
+ * and if not, subtract one from the number of \n we need to search for.
+ */
+ if (0 != fseeko(fp, sbp->st_size - 1, SEEK_SET)) {
+ ierr();
+ goto done;
+ }
+ if ('\n' != getc_unlocked(fp)) {
+ search_for--;
+ }
+
+ while(try_at != 0) {
+ found_this_pass = 0;
+
+ if (try_at < sbp->st_blksize) {
+ found_at[found_this_pass++] = 0;
+ try_at = 0;
+ } else {
+ last_try = try_at;
+ try_at -= sbp->st_blksize;
+ }
+
+ if (0 != fseeko(fp, try_at, SEEK_SET)) {
+ ierr();
+ goto done;
+ }
+
+ char ch;
+ while(EOF != (ch = getc_unlocked(fp))) {
+ if (ch == '\n') {
+ found_at[found_this_pass++ % search_for] = ftello(fp);
+ found_total++;
+ }
+ if (ftello(fp) == last_try && found_total < search_for) {
+ // We just reached the last block we scanned,
+ // and we know there arn't enough lines found
+ // so far to be happy, so we don't have to
+ // read it again.
+ break;
+ }
+ }
+
+ if (found_this_pass >= search_for || try_at == 0) {
+ off_t min = found_at[0];
+ int min_i = 0;
+ int i;
+ int lim = (found_this_pass < search_for) ? found_this_pass : search_for;
+ for(i = 1; i < lim; i++) {
+ if (found_at[i] < min) {
+ min = found_at[i];
+ min_i = i;
+ }
+ }
+
+ off_t target = min;
+
+ if (found_this_pass >= search_for) {
+ // min_i might be a partial line (unless
+ // try_at is 0). If we found search_for
+ // lines, min_i+1 is the first known full line
+ // _and_ because we look for an extra line we
+ // don't need to show it.
+ target = found_at[(min_i + 1) % search_for];
+ }
+
+ if (0 != fseeko(fp, target, SEEK_SET)) {
+ ierr();
+ goto done;
+ }
+
+ flockfile(stdout);
+ while(EOF != (ch = getc_unlocked(fp))) {
+ if (EOF == putchar_unlocked(ch)) {
+ funlockfile(stdout);
+ oerr();
+ goto done;
+ }
+ }
+ funlockfile(stdout);
+ goto done;
+ }
+ }
+
+done:
+ funlockfile(fp);
+ free(found_at);
+ return;
+#else
+ struct mapinfo map;
+ off_t curoff, size;
+ int i;
+
+ if (!(size = sbp->st_size))
+ return;
+ map.start = NULL;
+ map.fd = fileno(fp);
+ map.mapoff = map.maxoff = size;
+
+ /*
+ * Last char is special, ignore whether newline or not. Note that
+ * size == 0 is dealt with above, and size == 1 sets curoff to -1.
+ */
+ curoff = size - 2;
+ while (curoff >= 0) {
+ if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
+ ierr();
+ return;
+ }
+ for (i = curoff - map.mapoff; i >= 0; i--)
+ if (map.start[i] == '\n' && --off == 0)
+ break;
+ /* `i' is either the map offset of a '\n', or -1. */
+ curoff = map.mapoff + i;
+ if (i >= 0)
+ break;
+ }
+ curoff++;
+ if (mapprint(&map, curoff, size - curoff) != 0) {
+ ierr();
+ exit(1);
+ }
+
+ /* Set the file pointer to reflect the length displayed. */
+ if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
+ ierr();
+ return;
+ }
+ if (map.start != NULL && munmap(map.start, map.maplen)) {
+ ierr();
+ return;
+ }
+#endif
+}
+
+static void
+show(file_info_t *file)
+{
+ int ch;
+
+ while ((ch = getc(file->fp)) != EOF) {
+ if (last != file && no_files > 1) {
+ if (!qflag)
+ (void)printf("\n==> %s <==\n", file->file_name);
+ last = file;
+ }
+ if (putchar(ch) == EOF)
+ oerr();
+ }
+ (void)fflush(stdout);
+ if (ferror(file->fp)) {
+ file->fp = NULL;
+ fname = file->file_name;
+ ierr();
+ fname = NULL;
+ } else
+ clearerr(file->fp);
+}
+
+static void
+set_events(file_info_t *files)
+{
+ int i, n = 0;
+ file_info_t *file;
+ struct timespec ts;
+ struct statfs sf;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ action = USE_KQUEUE;
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (! file->fp)
+ continue;
+
+ if (fstatfs(fileno(file->fp), &sf) == 0 &&
+ (sf.f_flags & MNT_LOCAL) == 0) {
+ action = USE_SLEEP;
+ return;
+ }
+
+ if (Fflag && fileno(file->fp) != STDIN_FILENO) {
+ EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_DELETE | NOTE_RENAME, 0, 0);
+ n++;
+ }
+ EV_SET(&ev[n], fileno(file->fp), EVFILT_READ,
+ EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
+ n++;
+ }
+
+ if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
+ action = USE_SLEEP;
+ }
+}
+
+/*
+ * follow -- display the file, from an offset, forward.
+ *
+ */
+void
+follow(file_info_t *files, enum STYLE style, off_t off)
+{
+ int active, i, n = -1;
+ struct stat sb2;
+ file_info_t *file;
+ struct timespec ts;
+
+ /* Position each of the files */
+
+ file = files;
+ active = 0;
+ n = 0;
+ for (i = 0; i < no_files; i++, file++) {
+ if (file->fp) {
+ active = 1;
+ n++;
+ if (no_files > 1 && !qflag)
+ (void)printf("\n==> %s <==\n", file->file_name);
+ fname = file->file_name;
+ forward(file->fp, style, off, &file->st);
+ fname = NULL;
+ if (Fflag && fileno(file->fp) != STDIN_FILENO)
+ n++;
+ }
+ }
+ if (! active)
+ return;
+
+ last = --file;
+
+ kq = kqueue();
+ if (kq < 0)
+ err(1, "kqueue");
+ ev = malloc(n * sizeof(struct kevent));
+ if (! ev)
+ err(1, "Couldn't allocate memory for kevents.");
+ set_events(files);
+
+ for (;;) {
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (! file->fp)
+ continue;
+ if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) {
+ if (stat(file->file_name, &sb2) == 0 &&
+ (sb2.st_ino != file->st.st_ino ||
+ sb2.st_dev != file->st.st_dev ||
+ sb2.st_nlink == 0)) {
+ show(file);
+ file->fp = freopen(file->file_name, "r", file->fp);
+ if (file->fp == NULL) {
+ ierr();
+ continue;
+ } else {
+ memcpy(&file->st, &sb2, sizeof(struct stat));
+ set_events(files);
+ }
+ }
+ }
+ show(file);
+ }
+
+ switch (action) {
+ case USE_KQUEUE:
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ /*
+ * In the -F case we set a timeout to ensure that
+ * we re-stat the file at least once every second.
+ */
+ n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
+ if (n < 0)
+ err(1, "kevent");
+ if (n == 0) {
+ /* timeout */
+ break;
+ } else if (ev->filter == EVFILT_READ && ev->data < 0) {
+ /* file shrank, reposition to end */
+ if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
+ ierr();
+ continue;
+ }
+ }
+ break;
+
+ case USE_SLEEP:
+ (void) usleep(250000);
+ break;
+ }
+ }
+}
diff --git a/text_cmds/tail/misc.c b/text_cmds/tail/misc.c
new file mode 100644
index 0000000..584e3d1
--- /dev/null
+++ b/text_cmds/tail/misc.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+void
+ierr()
+{
+ warn("%s", fname);
+ rval = 1;
+}
+
+void
+oerr()
+{
+ err(1, "stdout");
+}
+
+/*
+ * Print `len' bytes from the file associated with `mip', starting at
+ * absolute file offset `startoff'. May move map window.
+ */
+int
+mapprint(struct mapinfo *mip, off_t startoff, off_t len)
+{
+ int n;
+
+ while (len > 0) {
+ if (startoff < mip->mapoff || startoff >= mip->mapoff +
+ (off_t)mip->maplen) {
+ if (maparound(mip, startoff) != 0)
+ return (1);
+ }
+ n = (mip->mapoff + mip->maplen) - startoff;
+ if (n > len)
+ n = len;
+ WR(mip->start + (startoff - mip->mapoff), n);
+ startoff += n;
+ len -= n;
+ }
+ return (0);
+}
+
+/*
+ * Move the map window so that it contains the byte at absolute file
+ * offset `offset'. The start of the map window will be TAILMAPLEN
+ * aligned.
+ */
+int
+maparound(struct mapinfo *mip, off_t offset)
+{
+
+ if (mip->start != NULL && munmap(mip->start, mip->maplen) != 0)
+ return (1);
+
+ mip->mapoff = offset & ~((off_t)TAILMAPLEN - 1);
+ mip->maplen = TAILMAPLEN;
+ if ((off_t)mip->maplen > mip->maxoff - mip->mapoff)
+ mip->maplen = mip->maxoff - mip->mapoff;
+ if (mip->maplen <= 0)
+ abort();
+ if ((mip->start = mmap(NULL, mip->maplen, PROT_READ, MAP_SHARED,
+ mip->fd, mip->mapoff)) == MAP_FAILED)
+ return (1);
+
+ return (0);
+}
diff --git a/text_cmds/tail/read.c b/text_cmds/tail/read.c
new file mode 100644
index 0000000..85d4d6e
--- /dev/null
+++ b/text_cmds/tail/read.c
@@ -0,0 +1,214 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+/*
+ * bytes -- read bytes to an offset from the end and display.
+ *
+ * This is the function that reads to a byte offset from the end of the input,
+ * storing the data in a wrap-around buffer which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the character closest to the beginning of the input to
+ * the end.
+ */
+int
+bytes(FILE *fp, off_t off)
+{
+ int ch, len, tlen;
+ char *ep, *p, *t;
+ int wrap;
+ char *sp;
+
+ if ((sp = p = malloc(off)) == NULL)
+ err(1, "malloc");
+
+ for (wrap = 0, ep = p + off; (ch = getc(fp)) != EOF;) {
+ *p = ch;
+ if (++p == ep) {
+ wrap = 1;
+ p = sp;
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ free(sp);
+ return 1;
+ }
+
+ if (rflag) {
+ for (t = p - 1, len = 0; t >= sp; --t, ++len)
+ if (*t == '\n' && len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (wrap) {
+ tlen = len;
+ for (t = ep - 1, len = 0; t >= p; --t, ++len)
+ if (*t == '\n') {
+ if (len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (tlen) {
+ WR(sp, tlen);
+ tlen = 0;
+ }
+ }
+ if (len)
+ WR(t + 1, len);
+ if (tlen)
+ WR(sp, tlen);
+ }
+ } else {
+ if (wrap && (len = ep - p))
+ WR(p, len);
+ len = p - sp;
+ if (len)
+ WR(sp, len);
+ }
+
+ free(sp);
+ return 0;
+}
+
+/*
+ * lines -- read lines to an offset from the end and display.
+ *
+ * This is the function that reads to a line offset from the end of the input,
+ * storing the data in an array of buffers which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the line closest to the beginning of the input to
+ * the end.
+ */
+int
+lines(FILE *fp, off_t off)
+{
+ struct {
+ int blen;
+ u_int len;
+ char *l;
+ } *llines;
+ int ch, rc;
+ char *p, *sp;
+ int blen, cnt, recno, wrap;
+
+ if ((llines = malloc(off * sizeof(*llines))) == NULL)
+ err(1, "malloc");
+ bzero(llines, off * sizeof(*llines));
+ sp = NULL;
+ blen = cnt = recno = wrap = 0;
+ rc = 0;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (++cnt > blen) {
+ if ((sp = realloc(sp, blen += 1024)) == NULL)
+ err(1, "realloc");
+ p = sp + cnt - 1;
+ }
+ *p++ = ch;
+ if (ch == '\n') {
+ if ((int)llines[recno].blen < cnt) {
+ llines[recno].blen = cnt + 256;
+ if ((llines[recno].l = realloc(llines[recno].l,
+ llines[recno].blen)) == NULL)
+ err(1, "realloc");
+ }
+ bcopy(sp, llines[recno].l, llines[recno].len = cnt);
+ cnt = 0;
+ p = sp;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ rc = 1;
+ goto done;
+ }
+ if (cnt) {
+ llines[recno].l = sp;
+ sp = NULL;
+ llines[recno].len = cnt;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+
+ if (rflag) {
+ for (cnt = recno - 1; cnt >= 0; --cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ if (wrap)
+ for (cnt = off - 1; cnt >= recno; --cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ } else {
+ if (wrap)
+ for (cnt = recno; cnt < off; ++cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ for (cnt = 0; cnt < recno; ++cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ }
+done:
+ for (cnt = 0; cnt < off; cnt++)
+ free(llines[cnt].l);
+ free(sp);
+ free(llines);
+ return (rc);
+}
diff --git a/text_cmds/tail/reverse.c b/text_cmds/tail/reverse.c
new file mode 100644
index 0000000..f0a52aa
--- /dev/null
+++ b/text_cmds/tail/reverse.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void r_buf(FILE *);
+static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
+
+/*
+ * reverse -- display input in reverse order by line.
+ *
+ * There are six separate cases for this -- regular and non-regular
+ * files by bytes, lines or the whole file.
+ *
+ * BYTES display N bytes
+ * REG mmap the file and display the lines
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * LINES display N lines
+ * REG mmap the file and display the lines
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ *
+ * FILE display the entire file
+ * REG mmap the file and display the lines
+ * NOREG cyclically read input into a linked list of buffers
+ */
+void
+reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+ if (style != REVERSE && off == 0)
+ return;
+
+ if (S_ISREG(sbp->st_mode))
+ r_reg(fp, style, off, sbp);
+ else
+ switch(style) {
+ case FBYTES:
+ case RBYTES:
+ bytes(fp, off);
+ break;
+ case FLINES:
+ case RLINES:
+ lines(fp, off);
+ break;
+ case REVERSE:
+ r_buf(fp);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * r_reg -- display a regular file in reverse order by line.
+ */
+static void
+r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+ struct mapinfo map;
+ off_t curoff, size, lineend;
+ int i;
+
+ if (!(size = sbp->st_size))
+ return;
+
+ map.start = NULL;
+ map.mapoff = map.maxoff = size;
+ map.fd = fileno(fp);
+
+ /*
+ * Last char is special, ignore whether newline or not. Note that
+ * size == 0 is dealt with above, and size == 1 sets curoff to -1.
+ */
+ curoff = size - 2;
+ lineend = size;
+ while (curoff >= 0) {
+ if (curoff < map.mapoff ||
+ curoff >= map.mapoff + (off_t)map.maplen) {
+ if (maparound(&map, curoff) != 0) {
+ ierr();
+ return;
+ }
+ }
+ for (i = curoff - map.mapoff; i >= 0; i--) {
+ if (style == RBYTES && --off == 0)
+ break;
+ if (map.start[i] == '\n')
+ break;
+ }
+ /* `i' is either the map offset of a '\n', or -1. */
+ curoff = map.mapoff + i;
+ if (i < 0)
+ continue;
+
+ /* Print the line and update offsets. */
+ if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
+ ierr();
+ return;
+ }
+ lineend = curoff + 1;
+ curoff--;
+
+ if (style == RLINES)
+ off--;
+
+ if (off == 0 && style != REVERSE) {
+ /* Avoid printing anything below. */
+ curoff = 0;
+ break;
+ }
+ }
+ if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
+ ierr();
+ return;
+ }
+ if (map.start != NULL && munmap(map.start, map.maplen))
+ ierr();
+}
+
+typedef struct bf {
+ struct bf *next;
+ struct bf *prev;
+ int len;
+ char *l;
+} BF;
+
+/*
+ * r_buf -- display a non-regular file in reverse order by line.
+ *
+ * This is the function that saves the entire input, storing the data in a
+ * doubly linked list of buffers and then displays them in reverse order.
+ * It has the usual nastiness of trying to find the newlines, as there's no
+ * guarantee that a newline occurs anywhere in the file, let alone in any
+ * particular buffer. If we run out of memory, input is discarded (and the
+ * user warned).
+ */
+static void
+r_buf(FILE *fp)
+{
+ BF *mark, *tl, *tr;
+ int ch, len, llen;
+ char *p;
+ off_t enomem;
+
+#define BSZ (128 * 1024)
+ for (mark = NULL, enomem = 0;;) {
+ /*
+ * Allocate a new block and link it into place in a doubly
+ * linked list. If out of memory, toss the LRU block and
+ * keep going.
+ */
+ if (enomem || (tl = malloc(sizeof(BF))) == NULL ||
+ (tl->l = malloc(BSZ)) == NULL) {
+ if (!mark)
+ err(1, "malloc");
+ tl = enomem ? tl->next : mark;
+ enomem += tl->len;
+ } else if (mark) {
+ tl->next = mark;
+ tl->prev = mark->prev;
+ mark->prev->next = tl;
+ mark->prev = tl;
+ } else {
+ mark = tl;
+ mark->next = mark->prev = mark;
+ }
+
+ /* Fill the block with input data. */
+ for (p = tl->l, len = 0;
+ len < BSZ && (ch = getc(fp)) != EOF; ++len)
+ *p++ = ch;
+
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+
+ /*
+ * If no input data for this block and we tossed some data,
+ * recover it.
+ */
+ if (!len && enomem) {
+ enomem -= tl->len;
+ tl = tl->prev;
+ break;
+ }
+
+ tl->len = len;
+ if (ch == EOF)
+ break;
+ }
+
+ if (enomem) {
+ warnx("warning: %jd bytes discarded", (intmax_t)enomem);
+ rval = 1;
+ }
+
+ /*
+ * Step through the blocks in the reverse order read. The last char
+ * is special, ignore whether newline or not.
+ */
+ for (mark = tl;;) {
+ for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
+ --p, ++llen)
+ if (*p == '\n') {
+ if (llen) {
+ WR(p + 1, llen);
+ llen = 0;
+ }
+ if (tl == mark)
+ continue;
+ for (tr = tl->next; tr->len; tr = tr->next) {
+ WR(tr->l, tr->len);
+ tr->len = 0;
+ if (tr == mark)
+ break;
+ }
+ }
+ tl->len = llen;
+ if ((tl = tl->prev) == mark)
+ break;
+ }
+ tl = tl->next;
+ if (tl->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+ while ((tl = tl->next)->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+}
diff --git a/text_cmds/tail/tail.1 b/text_cmds/tail/tail.1
new file mode 100644
index 0000000..251511a
--- /dev/null
+++ b/text_cmds/tail/tail.1
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tail.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 29, 2006
+.Dt TAIL 1
+.Os
+.Sh NAME
+.Nm tail
+.Nd display the last part of a file
+.Sh SYNOPSIS
+.Nm
+.Op Fl F | f | r
+.Op Fl q
+.Oo
+.Fl b Ar number | Fl c Ar number | Fl n Ar number
+.Oc
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the contents of
+.Ar file
+or, by default, its standard input, to the standard output.
+.Pp
+The display begins at a byte, line or 512-byte block location in the
+input.
+Numbers having a leading plus
+.Pq Ql +
+sign are relative to the beginning
+of the input, for example,
+.Dq Li "-c +2"
+starts the display at the second
+byte of the input.
+Numbers having a leading minus
+.Pq Ql -
+sign or no explicit sign are
+relative to the end of the input, for example,
+.Dq Li "-n 2"
+displays the last two lines of the input.
+The default starting location is
+.Dq Li "-n 10" ,
+or the last 10 lines of the input.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b Ar number
+The location is
+.Ar number
+512-byte blocks.
+.It Fl c Ar number
+The location is
+.Ar number
+bytes.
+.It Fl f
+The
+.Fl f
+option causes
+.Nm
+to not stop when end of file is reached, but rather to wait for additional
+data to be appended to the input.
+The
+.Fl f
+option is ignored if the standard input is a pipe, but not if it is a FIFO.
+.It Fl F
+The
+.Fl F
+option implies the
+.Fl f
+option, but
+.Nm
+will also check to see if the file being followed has been renamed or rotated.
+The file is closed and reopened when
+.Nm
+detects that the filename being read from has a new inode number.
+The
+.Fl F
+option is ignored if reading from standard input rather than a file.
+.It Fl n Ar number
+The location is
+.Ar number
+lines.
+.It Fl q
+Suppresses printing of headers when multiple files are being examined.
+.It Fl r
+The
+.Fl r
+option causes the input to be displayed in reverse order, by line.
+Additionally, this option changes the meaning of the
+.Fl b , c
+and
+.Fl n
+options.
+When the
+.Fl r
+option is specified, these options specify the number of bytes, lines
+or 512-byte blocks to display, instead of the bytes, lines or blocks
+from the beginning or end of the input from which to begin the display.
+The default for the
+.Fl r
+option is to display all of the input.
+.El
+.Pp
+If more than a single file is specified, each file is preceded by a
+header consisting of the string
+.Dq Li "==> " Ns Ar XXX Ns Li " <=="
+where
+.Ar XXX
+is the name of the file unless
+.Fl q
+flag is specified.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr cat 1 ,
+.Xr head 1 ,
+.Xr sed 1
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2-92
+specification.
+In particular, the
+.Fl F ,
+.Fl b
+and
+.Fl r
+options are extensions to that standard.
+.Pp
+The historic command line syntax of
+.Nm
+is supported by this implementation.
+The only difference between this implementation and historic versions
+of
+.Nm ,
+once the command line syntax translation has been done, is that the
+.Fl b ,
+.Fl c
+and
+.Fl n
+options modify the
+.Fl r
+option, i.e.,
+.Dq Li "-r -c 4"
+displays the last 4 characters of the last line
+of the input, while the historic tail (using the historic syntax
+.Dq Li -4cr )
+would ignore the
+.Fl c
+option and display the last 4 lines of the input.
+.Sh HISTORY
+A
+.Nm
+command appeared in PWB UNIX.
diff --git a/text_cmds/tail/tail.c b/text_cmds/tail/tail.c
new file mode 100644
index 0000000..57129ef
--- /dev/null
+++ b/text_cmds/tail/tail.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int Fflag, fflag, qflag, rflag, rval, no_files;
+const char *fname;
+
+file_info_t *files;
+
+static void obsolete(char **);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ FILE *fp;
+ off_t off;
+ enum STYLE style;
+ int i, ch, first;
+ file_info_t *file;
+ char *p;
+
+ /*
+ * Tail's options are weird. First, -n10 is the same as -n-10, not
+ * -n+10. Second, the number options are 1 based and not offsets,
+ * so -n+1 is the first line, and -c-1 is the last byte. Third, the
+ * number options for the -r option specify the number of things that
+ * get displayed, not the starting point in the file. The one major
+ * incompatibility in this version as compared to historical versions
+ * is that the 'r' option couldn't be modified by the -lbc options,
+ * i.e. it was always done in lines. This version treats -rc as a
+ * number of characters in reverse order. Finally, the default for
+ * -r is the entire file, not 10 lines.
+ */
+#define ARG(units, forward, backward) { \
+ if (style) \
+ usage(); \
+ off = strtoll(optarg, &p, 10) * (units); \
+ if (*p) \
+ errx(1, "illegal offset -- %s", optarg); \
+ switch(optarg[0]) { \
+ case '+': \
+ if (off) \
+ off -= (units); \
+ style = (forward); \
+ break; \
+ case '-': \
+ off = -off; \
+ /* FALLTHROUGH */ \
+ default: \
+ style = (backward); \
+ break; \
+ } \
+}
+
+ obsolete(argv);
+ style = NOTSET;
+ while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1)
+ switch(ch) {
+ case 'F': /* -F is superset of (and implies) -f */
+ Fflag = fflag = 1;
+ break;
+ case 'b':
+ ARG(512, FBYTES, RBYTES);
+ break;
+ case 'c':
+ ARG(1, FBYTES, RBYTES);
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'n':
+ ARG(1, FLINES, RLINES);
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ no_files = argc ? argc : 1;
+
+ /*
+ * If displaying in reverse, don't permit follow option, and convert
+ * style values.
+ */
+ if (rflag) {
+ if (fflag)
+ usage();
+ if (style == FBYTES)
+ style = RBYTES;
+ else if (style == FLINES)
+ style = RLINES;
+ }
+
+ /*
+ * If style not specified, the default is the whole file for -r, and
+ * the last 10 lines if not -r.
+ */
+ if (style == NOTSET) {
+ if (rflag) {
+ off = 0;
+ style = REVERSE;
+ } else {
+ off = 10;
+ style = RLINES;
+ }
+ }
+
+ if (fflag && !argc) {
+ /*
+ * Determine if input is a pipe. 4.4BSD will set the SOCKET
+ * bit in the st_mode field for pipes. Fix this then.
+ */
+ if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
+ errno == ESPIPE) {
+ errno = 0;
+ fflag = 0; /* POSIX.2 requires this. */
+ }
+ }
+
+ if (fflag) {
+ files = (struct file_info *) malloc(no_files * sizeof(struct file_info));
+ if (! files)
+ err(1, "Couldn't malloc space for file descriptors.");
+
+ for (file = files; (fname = argc ? *argv++ : "stdin"); file++) {
+ file->file_name = malloc(strlen(fname)+1);
+ if (! file->file_name)
+ errx(1, "Couldn't malloc space for file name.");
+ strncpy(file->file_name, fname, strlen(fname)+1);
+ file->fp = argc ? fopen(file->file_name, "r") : stdin;
+ if (file->fp == NULL ||
+ fstat(fileno(file->fp), &file->st)) {
+ file->fp = NULL;
+ ierr();
+ continue;
+ }
+ if (!argc)
+ break;
+ }
+ follow(files, style, off);
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ free(file->file_name);
+ }
+ free(files);
+ } else if (*argv) {
+ for (first = 1; (fname = *argv++);) {
+ if ((fp = fopen(fname, "r")) == NULL ||
+ fstat(fileno(fp), &sb)) {
+ ierr();
+ continue;
+ }
+ if (argc > 1 && !qflag) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", fname);
+ first = 0;
+ (void)fflush(stdout);
+ }
+
+#ifdef __APPLE__
+ /* 3849683: don't read a directory */
+ if (S_IFDIR == (sb.st_mode & S_IFMT))
+ continue;
+#endif
+
+ if (rflag)
+ reverse(fp, style, off, &sb);
+ else
+ forward(fp, style, off, &sb);
+ }
+ } else {
+ fname = "stdin";
+
+ if (fstat(fileno(stdin), &sb)) {
+ ierr();
+ exit(1);
+ }
+
+ if (rflag)
+ reverse(stdin, style, off, &sb);
+ else
+ forward(stdin, style, off, &sb);
+ }
+ exit(rval);
+}
+
+/*
+ * Convert the obsolete argument form into something that getopt can handle.
+ * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't
+ * the option argument for a -b, -c or -n option gets converted.
+ */
+static void
+obsolete(char *argv[])
+{
+ char *ap, *p, *t;
+ size_t len;
+ char *start;
+
+ while ((ap = *++argv)) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+
+ switch(*++ap) {
+ /* Old-style option. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+
+ /* Malloc space for dash, new option and argument. */
+ len = strlen(*argv);
+ if ((start = p = malloc(len + 3)) == NULL)
+ err(1, "malloc");
+ *p++ = '-';
+
+ /*
+ * Go to the end of the option argument. Save off any
+ * trailing options (-3lf) and translate any trailing
+ * output style characters.
+ */
+ t = *argv + len - 1;
+ if (*t == 'F' || *t == 'f' || *t == 'r') {
+ *p++ = *t;
+ *t-- = '\0';
+ }
+ switch(*t) {
+ case 'b':
+ *p++ = 'b';
+ *t = '\0';
+ break;
+ case 'c':
+ *p++ = 'c';
+ *t = '\0';
+ break;
+ case 'l':
+ *t = '\0';
+ /* FALLTHROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *p++ = 'n';
+ break;
+ default:
+ errx(1, "illegal option -- %s", *argv);
+ }
+ *p++ = *argv[0];
+ (void)strcpy(p, ap);
+ *argv = start;
+ continue;
+
+ /*
+ * Options w/ arguments, skip the argument and continue
+ * with the next option.
+ */
+ case 'b':
+ case 'c':
+ case 'n':
+ if (!ap[1])
+ ++argv;
+ /* FALLTHROUGH */
+ /* Options w/o arguments, continue with the next option. */
+ case 'F':
+ case 'f':
+ case 'r':
+ continue;
+
+ /* Illegal option, return and let getopt handle it. */
+ default:
+ return;
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
+ " [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/tests/Makefile b/text_cmds/tests/Makefile
new file mode 100644
index 0000000..ed613c5
--- /dev/null
+++ b/text_cmds/tests/Makefile
@@ -0,0 +1,6 @@
+PROJECT := text_cmds
+TEST_DIR := tests
+
+
+include $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.common
+include $(DEVELOPER_DIR)/AppleInternal/Makefiles/darwintest/Makefile.targets
diff --git a/text_cmds/tests/sort_vers.c b/text_cmds/tests/sort_vers.c
new file mode 100644
index 0000000..8a3fce8
--- /dev/null
+++ b/text_cmds/tests/sort_vers.c
@@ -0,0 +1,23 @@
+#include <darwintest.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <limits.h>
+
+T_DECL(versionstring, "Apple specific version string") {
+ char version[128];
+ FILE *sortfile = popen("/usr/bin/sort --version", "r");
+ T_ASSERT_NOTNULL(sortfile, "Getting version string");
+ T_ASSERT_NOTNULL(fgets(version, sizeof(version), sortfile), "Reading version string");
+ pclose(sortfile);
+ T_ASSERT_NOTNULL(strstr(version, "-Apple"), "Apple in version string");
+
+ char *num = strstr(version, "(");
+ char *endnum = strstr(version, ")");
+ T_ASSERT_NOTNULL(num, "Locating parens start");
+ T_ASSERT_NOTNULL(endnum, "Locating parens end");
+ T_ASSERT_GT(endnum, num, "end is after the start");
+ long applevers = strtol(num+1, &endnum, 10);
+ T_ASSERT_GT(applevers, 0, "Version greater than zero");
+ T_ASSERT_LT(applevers, LONG_MAX, "Version less than LONG_MAX");
+}
diff --git a/text_cmds/text_cmds.plist b/text_cmds/text_cmds.plist
new file mode 100644
index 0000000..8f0ca1d
--- /dev/null
+++ b/text_cmds/text_cmds.plist
@@ -0,0 +1,607 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>banner</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/banner/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co banner</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Compile fix (needs sys/types.h).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>cat</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/bin/cat/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cat</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>cksum</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/cksum/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cksum</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Compile fix (needs stdint.h).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>col</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/col/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co col</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>colrm</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/colrm/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co colrm</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>column</string>
+ <key>OpenSourceVersion</key>
+ <string>2011-11-06</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/column/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co column</string>
+ <key>OpenSourceImportDate</key>
+ <string>2012-10-23</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>comm</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/comm/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co comm</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>csplit</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/csplit/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co csplit</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Rename getline to csplit_getline (7556902).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>cut</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/cut/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cut</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>ed</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/bin/ed/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co ed</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Conformance fixes (3676514, 3738526, 3741027, 3751351, 3754089, 3936064, 3936075).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>ee</string>
+ <key>OpenSourceVersion</key>
+ <string>2011-12-17</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://svnweb.freebsd.org/base/head/contrib/ee/</string>
+ <key>OpenSourceSCM</key>
+ <string>svn co http://svn.freebsd.org/base/head/contrib/ee/</string>
+ <key>OpenSourceImportDate</key>
+ <string>2015-08-04</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>bsd</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>expand</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/expand/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co expand</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Clarify -t option in manpage (3973603).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>fmt</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/fmt/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co fmt</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>fold</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/fold/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co fold</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>bsdgrep</string>
+ <key>OpenSourceVersion</key>
+ <string>2012-01-15</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://wiki.freebsd.org/BSDgrep</string>
+ <key>OpenSourceSCM</key>
+ <string>cvs -d anoncvs@anoncvs1.FreeBSD.org:/home/ncvs co src/usr.bin/grep</string>
+ <key>OpenSourceImportDate</key>
+ <string>2012-01-23</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>9510115 (grep.c, util.c)</string>
+ <string>WITHOUT_FASTMATCH (grep.h, grep.c, util.c)</string>
+ <string>WITHOUT_LZMA (file.c, grep.c)</string>
+ <string>file.c: casts in grep_fgetln to fix warnings</string>
+ <string>file.c: adjust mmap flags</string>
+ <string>grep.c: enable REG_ENHANCED (except for -F mode)</string>
+ <string>grep.c: disable FILE_MMAP options</string>
+ <string>util.c: basename expects char *</string>
+ <string>util.c: ignore locale for binary files (10462853)</string>
+ <string>util.c: recursive stdin warning (10290183)</string>
+ <string>util.c: -w fix (10593340)</string>
+ <string>util.c: -c -q fix (10680370)</string>
+ <string>util.c, grep.h: avoid MAX_LINE_MATCHES limitation (10511988)</string>
+ <string>grep.c: Remove -P from usage statement (13475367)</string>
+ <string>util.c: Use mbtowc instead of sscanf (20248554)</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ <key>OpenSourceLicenseFile</key>
+ <string>bsdgrep.txt</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>head</string>
+ <key>OpenSourceVersion</key>
+ <string>2007-01-11</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/head/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co head</string>
+ <key>OpenSourceImportDate</key>
+ <string>2010-05-10</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Check ferror (6555390).</string>
+ </array>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>join</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/join/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co join</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Conformance fixes (3853584).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>lam</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/lam/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co lam</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>look</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/look/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co look</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>md5</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/sbin/md5/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co md5</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>XXX NOT MERGED YET XXX</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>nl</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/nl/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co nl</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Conformance fix in filter() (3849850).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>paste</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/paste/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co paste</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>pr</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/pr/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co pr</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Conformance fixes (3743086, 3936126).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>rev</string>
+ <key>OpenSourceVersion</key>
+ <string>2009-12-13</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/rev/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d anoncvs@anoncvs1.FreeBSD.org:/home/ncvs co rev</string>
+ <key>OpenSourceImportDate</key>
+ <string>2010-04-07</string>
+ <key>OpenSourceModifications</key>
+ <array/>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>rs</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/rs/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co rs</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Rename getline to rs_getline (7556902).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>sed</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/sed/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co sed</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Correctly document -y option (4134578).</string>
+ <string>Rewind stdin before exit (3681024).</string>
+ <string>Handle &apos;[&apos; delimiter (3733839).</string>
+ <string>Handle backslash with &apos;y&apos;, remove handy warning (3733839).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>sort</string>
+ <key>OpenSourceVersion</key>
+ <string>2017-09-27</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>https://svnweb.freebsd.org/base/head/usr.bin/sort/</string>
+ <key>OpenSourceSCM</key>
+ <string>svn co http://svn.freebsd.org/base/head/usr.bin/sort</string>
+ <key>OpenSourceImportDate</key>
+ <string>2017-09-27</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>file.c: mmap portability</string>
+ <string>file.c: fix unreachable code warning</string>
+ <string>coll.c, sort.c, sort.h: md5 portability</string>
+ <string>sort.c: update version output</string>
+ <string>sort.c: conformance failure with -k (28745165)</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>bsd</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>split</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/split/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co split</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>tail</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/tail/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co tail</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Don&apos;t read directories (3849683).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>tr</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/tr/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co tr</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Add missing character classes to man page (4115259).</string>
+ <string>Handle tr -s &apos;[a*]&apos; (3673947).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>ul</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/ul/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co ul</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>unexpand</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/unexpand/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co unexpand</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>uniq</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/uniq/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co uniq</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>Rename getline to uniq_getline (7556902).</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>unvis</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/unvis/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co unvis</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>vis</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/vis/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co vis</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>wc</string>
+ <key>OpenSourceVersion</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/wc/</string>
+ <key>OpenSourceCVS</key>
+ <string>cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co wc</string>
+ <key>OpenSourceImportDate</key>
+ <string>2005-09-16</string>
+ <key>OpenSourceLicense</key>
+ <string>BSD</string>
+ </dict>
+</array>
+</plist>
diff --git a/text_cmds/text_cmds.xcodeproj/project.pbxproj b/text_cmds/text_cmds.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..ac76c3b
--- /dev/null
+++ b/text_cmds/text_cmds.xcodeproj/project.pbxproj
@@ -0,0 +1,3890 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ FC7A7E91149875C30086576A /* executables */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC7A7E92149875C30086576A /* Build configuration list for PBXAggregateTarget "executables" */;
+ buildPhases = (
+ FC6C98FD149A989300DDCC47 /* Install open source info */,
+ FD3562041B828154008A70F6 /* Install grep variant links */,
+ );
+ dependencies = (
+ FC7A7E95149875E00086576A /* PBXTargetDependency */,
+ FC7A7E97149875E00086576A /* PBXTargetDependency */,
+ FC7A7E99149875E00086576A /* PBXTargetDependency */,
+ FC7A7E9B149875E00086576A /* PBXTargetDependency */,
+ FC7A7E9D149875E00086576A /* PBXTargetDependency */,
+ FC7A7E9F149875E00086576A /* PBXTargetDependency */,
+ FC7A7EA1149875E00086576A /* PBXTargetDependency */,
+ FC7A7EA3149875E00086576A /* PBXTargetDependency */,
+ FC7A7EA5149875E00086576A /* PBXTargetDependency */,
+ FD2BDC181B718D310053EE6B /* PBXTargetDependency */,
+ FC7A7EA7149875E00086576A /* PBXTargetDependency */,
+ FC7A7EA9149875E00086576A /* PBXTargetDependency */,
+ FC7A7EAB149875E00086576A /* PBXTargetDependency */,
+ FD3562061B8281A6008A70F6 /* PBXTargetDependency */,
+ FC7A7EAD149875E00086576A /* PBXTargetDependency */,
+ FC7A7EAF149875E00086576A /* PBXTargetDependency */,
+ FC7A7EB1149875E00086576A /* PBXTargetDependency */,
+ FC7A7EB3149875E00086576A /* PBXTargetDependency */,
+ FC7A7EB5149875E00086576A /* PBXTargetDependency */,
+ FC7A7EB7149875E00086576A /* PBXTargetDependency */,
+ FC7A7EB9149875E00086576A /* PBXTargetDependency */,
+ FC7A7EBB149875E00086576A /* PBXTargetDependency */,
+ FC7A7EBD149875E00086576A /* PBXTargetDependency */,
+ FC7A7EBF149875E00086576A /* PBXTargetDependency */,
+ FC7A7EC1149875E00086576A /* PBXTargetDependency */,
+ FC7A7EC3149875E00086576A /* PBXTargetDependency */,
+ FC7A7EC5149875E00086576A /* PBXTargetDependency */,
+ FC7A7EC7149875E00086576A /* PBXTargetDependency */,
+ FC7A7EC9149875E00086576A /* PBXTargetDependency */,
+ FC7A7ECB149875E00086576A /* PBXTargetDependency */,
+ FC7A7ECD149875E00086576A /* PBXTargetDependency */,
+ FC7A7ECF149875E00086576A /* PBXTargetDependency */,
+ FC7A7ED1149875E00086576A /* PBXTargetDependency */,
+ FC7A7ED3149875E00086576A /* PBXTargetDependency */,
+ FC7A7ED5149875E00086576A /* PBXTargetDependency */,
+ );
+ name = executables;
+ productName = executables;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ FC6C98FC149A94EB00DDCC47 /* libcurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC6C98FB149A94EB00DDCC47 /* libcurses.dylib */; };
+ FC7A7DE514986CD60086576A /* banner.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B4C149866AE0086576A /* banner.c */; };
+ FC7A7DE614986CDB0086576A /* cat.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B50149866AE0086576A /* cat.c */; };
+ FC7A7DE714986CE00086576A /* col.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B54149866AE0086576A /* col.c */; };
+ FC7A7DE814986CE40086576A /* colrm.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B59149866AE0086576A /* colrm.c */; };
+ FC7A7DE914986CE90086576A /* column.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B5D149866AE0086576A /* column.c */; };
+ FC7A7DEA14986CEE0086576A /* comm.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B61149866AE0086576A /* comm.c */; };
+ FC7A7DEB14986D0C0086576A /* csplit.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B65149866AE0086576A /* csplit.c */; };
+ FC7A7DEC14986D120086576A /* cut.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B69149866AE0086576A /* cut.c */; };
+ FC7A7DED14986D690086576A /* buf.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B6C149866AE0086576A /* buf.c */; };
+ FC7A7DEE14986D690086576A /* cbc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B6D149866AE0086576A /* cbc.c */; };
+ FC7A7DF014986D690086576A /* glbl.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B70149866AE0086576A /* glbl.c */; };
+ FC7A7DF114986D690086576A /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B71149866AE0086576A /* io.c */; };
+ FC7A7DF214986D690086576A /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B72149866AE0086576A /* main.c */; };
+ FC7A7DF314986D690086576A /* re.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B75149866AE0086576A /* re.c */; };
+ FC7A7DF414986D690086576A /* sub.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7B78149866AE0086576A /* sub.c */; };
+ FC7A7DF514986D690086576A /* undo.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C1E149866AF0086576A /* undo.c */; };
+ FC7A7DF614986D760086576A /* expand.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C21149866AF0086576A /* expand.c */; };
+ FC7A7DF714986D800086576A /* fmt.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C25149866AF0086576A /* fmt.c */; };
+ FC7A7DF814986D8B0086576A /* fold.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C29149866AF0086576A /* fold.c */; };
+ FC7A7DF914986D920086576A /* head.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C2D149866AF0086576A /* head.c */; };
+ FC7A7DFA14986DA10086576A /* join.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C31149866AF0086576A /* join.c */; };
+ FC7A7DFB14986DAB0086576A /* lam.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C35149866AF0086576A /* lam.c */; };
+ FC7A7DFC14986DBF0086576A /* look.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C39149866AF0086576A /* look.c */; };
+ FC7A7DFE14986DC80086576A /* commoncrypto.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C3D149866AF0086576A /* commoncrypto.c */; };
+ FC7A7E0014986DC80086576A /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C41149866AF0086576A /* md5.c */; };
+ FC7A7E0114986DD90086576A /* nl.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C45149866AF0086576A /* nl.c */; };
+ FC7A7E0214986DE40086576A /* paste.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C49149866AF0086576A /* paste.c */; };
+ FC7A7E0314986DF60086576A /* egetopt.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C4B149866AF0086576A /* egetopt.c */; };
+ FC7A7E0514986DF60086576A /* pr.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C4F149866AF0086576A /* pr.c */; };
+ FC7A7E0714986E040086576A /* rev.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C54149866AF0086576A /* rev.c */; };
+ FC7A7E0814986E0E0086576A /* rs.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C58149866AF0086576A /* rs.c */; };
+ FC7A7E0914986E1B0086576A /* compile.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C5A149866AF0086576A /* compile.c */; };
+ FC7A7E0C14986E1B0086576A /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C5D149866AF0086576A /* main.c */; };
+ FC7A7E0D14986E1B0086576A /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C5F149866AF0086576A /* misc.c */; };
+ FC7A7E0E14986E1B0086576A /* process.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C61149866AF0086576A /* process.c */; };
+ FC7A7E3714986E580086576A /* sort.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7C93149866AF0086576A /* sort.c */; };
+ FC7A7E5614986E630086576A /* split.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CB5149866AF0086576A /* split.c */; };
+ FC7A7E5814986E750086576A /* forward.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CB8149866AF0086576A /* forward.c */; };
+ FC7A7E5914986E750086576A /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CBA149866AF0086576A /* misc.c */; };
+ FC7A7E5A14986E750086576A /* read.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CBB149866AF0086576A /* read.c */; };
+ FC7A7E5B14986E750086576A /* reverse.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CBC149866AF0086576A /* reverse.c */; };
+ FC7A7E5C14986E750086576A /* tail.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CBE149866AF0086576A /* tail.c */; };
+ FC7A7E5D14986E880086576A /* cmap.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CC1149866AF0086576A /* cmap.c */; };
+ FC7A7E5F14986E880086576A /* cset.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CC3149866AF0086576A /* cset.c */; };
+ FC7A7E6214986E880086576A /* str.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CC7149866AF0086576A /* str.c */; };
+ FC7A7E6314986E880086576A /* tr.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CC9149866AF0086576A /* tr.c */; };
+ FC7A7E6414986E920086576A /* ul.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CCD149866AF0086576A /* ul.c */; };
+ FC7A7E6514986E9B0086576A /* unexpand.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CD0149866AF0086576A /* unexpand.c */; };
+ FC7A7E6614986EA40086576A /* uniq.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CD4149866AF0086576A /* uniq.c */; };
+ FC7A7E6714986EAC0086576A /* unvis.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CD8149866AF0086576A /* unvis.c */; };
+ FC7A7E6914986EB90086576A /* foldit.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CDB149866AF0086576A /* foldit.c */; };
+ FC7A7E6A14986EB90086576A /* vis.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CDE149866AF0086576A /* vis.c */; };
+ FC7A7E6B14986EC10086576A /* wc.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7A7CE2149866AF0086576A /* wc.c */; };
+ FC7A7E6C14986F0E0086576A /* banner.6 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B4B149866AE0086576A /* banner.6 */; };
+ FC7A7E6D14986F120086576A /* cat.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B4F149866AE0086576A /* cat.1 */; };
+ FC7A7E6E14986F180086576A /* col.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B53149866AE0086576A /* col.1 */; };
+ FC7A7E6F14986F1B0086576A /* colrm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B58149866AE0086576A /* colrm.1 */; };
+ FC7A7E7014986F200086576A /* column.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B5C149866AE0086576A /* column.1 */; };
+ FC7A7E7114986F230086576A /* comm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B60149866AE0086576A /* comm.1 */; };
+ FC7A7E7214986F270086576A /* csplit.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B64149866AE0086576A /* csplit.1 */; };
+ FC7A7E7314986F2A0086576A /* cut.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B68149866AE0086576A /* cut.1 */; };
+ FC7A7E7414986F2E0086576A /* ed.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B6E149866AE0086576A /* ed.1 */; };
+ FC7A7E7514986F330086576A /* red.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7B77149866AE0086576A /* red.1 */; };
+ FC7A7E7614986F3B0086576A /* expand.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C20149866AF0086576A /* expand.1 */; };
+ FC7A7E7714986F3E0086576A /* fmt.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C24149866AF0086576A /* fmt.1 */; };
+ FC7A7E7814986F400086576A /* fold.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C28149866AF0086576A /* fold.1 */; };
+ FC7A7E7914986F430086576A /* head.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C2C149866AF0086576A /* head.1 */; };
+ FC7A7E7A14986F4A0086576A /* join.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C30149866AF0086576A /* join.1 */; };
+ FC7A7E7B14986F4D0086576A /* lam.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C34149866AF0086576A /* lam.1 */; };
+ FC7A7E7C14986F500086576A /* look.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C38149866AF0086576A /* look.1 */; };
+ FC7A7E7D14986F540086576A /* md5.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C40149866AF0086576A /* md5.1 */; };
+ FC7A7E7E14986F5A0086576A /* nl.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C44149866AF0086576A /* nl.1 */; };
+ FC7A7E7F14986F5D0086576A /* paste.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C48149866AF0086576A /* paste.1 */; };
+ FC7A7E8014986F610086576A /* pr.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C4E149866AF0086576A /* pr.1 */; };
+ FC7A7E8114986F740086576A /* rev.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C53149866AF0086576A /* rev.1 */; };
+ FC7A7E8214986F790086576A /* rs.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C57149866AF0086576A /* rs.1 */; };
+ FC7A7E8314986F800086576A /* sed.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7C62149866AF0086576A /* sed.1 */; };
+ FC7A7E8514986FCF0086576A /* split.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CB4149866AF0086576A /* split.1 */; };
+ FC7A7E8614986FDA0086576A /* tail.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CBD149866AF0086576A /* tail.1 */; };
+ FC7A7E8814986FF20086576A /* ul.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CCC149866AF0086576A /* ul.1 */; };
+ FC7A7E89149870000086576A /* uniq.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CD3149866AF0086576A /* uniq.1 */; };
+ FC7A7E8E149870670086576A /* wc.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CE1149866AF0086576A /* wc.1 */; };
+ FC7A7E8F149870740086576A /* vis.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CDD149866AF0086576A /* vis.1 */; };
+ FC7A7E90149870820086576A /* unvis.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CD7149866AF0086576A /* unvis.1 */; };
+ FCD527B0149AA082001EBF77 /* tr.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC7A7CC8149866AF0086576A /* tr.1 */; };
+ FD3561FE1B827F9F008A70F6 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3561F81B827F9F008A70F6 /* file.c */; };
+ FD3561FF1B827F9F008A70F6 /* grep.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3561FA1B827F9F008A70F6 /* grep.c */; };
+ FD3562001B827F9F008A70F6 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3561FC1B827F9F008A70F6 /* queue.c */; };
+ FD3562011B827F9F008A70F6 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3561FD1B827F9F008A70F6 /* util.c */; };
+ FD3562021B827FC0008A70F6 /* grep.1 in Install Man Page */ = {isa = PBXBuildFile; fileRef = FD3561F91B827F9F008A70F6 /* grep.1 */; };
+ FDA49B951B718738003B4F3C /* ee.c in Sources */ = {isa = PBXBuildFile; fileRef = FDA49B941B718738003B4F3C /* ee.c */; };
+ FDA49B971B71876A003B4F3C /* ee.1 in Copy Files */ = {isa = PBXBuildFile; fileRef = FDA49B961B718762003B4F3C /* ee.1 */; };
+ FDF283681DAED0C000CF8C36 /* bwstring.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF283571DAED0C000CF8C36 /* bwstring.c */; };
+ FDF283691DAED0C000CF8C36 /* coll.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF283591DAED0C000CF8C36 /* coll.c */; };
+ FDF2836A1DAED0C000CF8C36 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF2835B1DAED0C000CF8C36 /* file.c */; };
+ FDF2836B1DAED0C000CF8C36 /* mem.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF2835D1DAED0C000CF8C36 /* mem.c */; };
+ FDF2836C1DAED0C000CF8C36 /* radixsort.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF283621DAED0C000CF8C36 /* radixsort.c */; };
+ FDF2836D1DAED0C000CF8C36 /* vsort.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF283661DAED0C000CF8C36 /* vsort.c */; };
+ FDF283711DAED7C700CF8C36 /* commoncrypto.c in Sources */ = {isa = PBXBuildFile; fileRef = FDF2836F1DAED7C700CF8C36 /* commoncrypto.c */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ FC7A7E94149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7B38149865C80086576A;
+ remoteInfo = banner;
+ };
+ FC7A7E96149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7CE4149867ED0086576A;
+ remoteInfo = cat;
+ };
+ FC7A7E98149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7CEC149869E90086576A;
+ remoteInfo = col;
+ };
+ FC7A7E9A149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7CF414986A160086576A;
+ remoteInfo = colrm;
+ };
+ FC7A7E9C149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7CFC14986A190086576A;
+ remoteInfo = column;
+ };
+ FC7A7E9E149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D0414986A1B0086576A;
+ remoteInfo = comm;
+ };
+ FC7A7EA0149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D0C14986A1E0086576A;
+ remoteInfo = csplit;
+ };
+ FC7A7EA2149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D1414986A210086576A;
+ remoteInfo = cut;
+ };
+ FC7A7EA4149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D1C14986A230086576A;
+ remoteInfo = ed;
+ };
+ FC7A7EA6149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D2414986A240086576A;
+ remoteInfo = expand;
+ };
+ FC7A7EA8149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D2C14986A280086576A;
+ remoteInfo = fmt;
+ };
+ FC7A7EAA149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D3414986A2B0086576A;
+ remoteInfo = fold;
+ };
+ FC7A7EAC149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D3C14986A2F0086576A;
+ remoteInfo = head;
+ };
+ FC7A7EAE149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D4414986A310086576A;
+ remoteInfo = join;
+ };
+ FC7A7EB0149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D4C14986A340086576A;
+ remoteInfo = lam;
+ };
+ FC7A7EB2149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D5414986A360086576A;
+ remoteInfo = look;
+ };
+ FC7A7EB4149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D5C14986A390086576A;
+ remoteInfo = md5;
+ };
+ FC7A7EB6149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D6414986A3C0086576A;
+ remoteInfo = nl;
+ };
+ FC7A7EB8149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D6C14986A3D0086576A;
+ remoteInfo = paste;
+ };
+ FC7A7EBA149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D7414986A400086576A;
+ remoteInfo = pr;
+ };
+ FC7A7EBC149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D7C14986A410086576A;
+ remoteInfo = rev;
+ };
+ FC7A7EBE149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D8414986A430086576A;
+ remoteInfo = rs;
+ };
+ FC7A7EC0149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D8C14986A450086576A;
+ remoteInfo = sed;
+ };
+ FC7A7EC2149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D9414986A470086576A;
+ remoteInfo = sort;
+ };
+ FC7A7EC4149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7D9C14986A480086576A;
+ remoteInfo = split;
+ };
+ FC7A7EC6149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DA414986A4A0086576A;
+ remoteInfo = tail;
+ };
+ FC7A7EC8149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DAC14986A500086576A;
+ remoteInfo = tr;
+ };
+ FC7A7ECA149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DB414986A530086576A;
+ remoteInfo = ul;
+ };
+ FC7A7ECC149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DBC14986A540086576A;
+ remoteInfo = unexpand;
+ };
+ FC7A7ECE149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DC414986A560086576A;
+ remoteInfo = uniq;
+ };
+ FC7A7ED0149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DCC14986A580086576A;
+ remoteInfo = unvis;
+ };
+ FC7A7ED2149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DD414986A5A0086576A;
+ remoteInfo = vis;
+ };
+ FC7A7ED4149875E00086576A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC7A7DDC14986A5C0086576A;
+ remoteInfo = wc;
+ };
+ FD2BDC171B718D310053EE6B /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FDA49B8D1B7186CE003B4F3C;
+ remoteInfo = ee;
+ };
+ FD3562051B8281A6008A70F6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FC7A7B30149865C80086576A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FD3561F11B827EE0008A70F6;
+ remoteInfo = grep;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ FC7A7B37149865C80086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man6/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E6C14986F0E0086576A /* banner.6 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7CE7149867ED0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E6D14986F120086576A /* cat.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7CEF149869E90086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E6E14986F180086576A /* col.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7CF714986A160086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E6F14986F1B0086576A /* colrm.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7CFF14986A190086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7014986F200086576A /* column.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D0714986A1B0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7114986F230086576A /* comm.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D0F14986A1E0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7214986F270086576A /* csplit.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D1714986A210086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7314986F2A0086576A /* cut.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D1F14986A230086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7414986F2E0086576A /* ed.1 in CopyFiles */,
+ FC7A7E7514986F330086576A /* red.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D2714986A240086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7614986F3B0086576A /* expand.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D2F14986A280086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7714986F3E0086576A /* fmt.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D3714986A2B0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7814986F400086576A /* fold.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D3F14986A2F0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7914986F430086576A /* head.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D4714986A310086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7A14986F4A0086576A /* join.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D4F14986A340086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7B14986F4D0086576A /* lam.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D5714986A360086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7C14986F500086576A /* look.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D5F14986A390086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7D14986F540086576A /* md5.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D6714986A3C0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7E14986F5A0086576A /* nl.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D6F14986A3D0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E7F14986F5D0086576A /* paste.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D7714986A400086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8014986F610086576A /* pr.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D7F14986A410086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8114986F740086576A /* rev.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D8714986A430086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8214986F790086576A /* rs.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D8F14986A450086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8314986F800086576A /* sed.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7D9F14986A480086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8514986FCF0086576A /* split.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DA714986A4A0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8614986FDA0086576A /* tail.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DAF14986A500086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FCD527B0149AA082001EBF77 /* tr.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DB714986A530086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8814986FF20086576A /* ul.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DBF14986A540086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DC714986A560086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E89149870000086576A /* uniq.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DCF14986A580086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E90149870820086576A /* unvis.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DD714986A5A0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8F149870740086576A /* vis.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC7A7DDF14986A5C0086576A /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC7A7E8E149870670086576A /* wc.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FD3561F01B827EE0008A70F6 /* Install Man Page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FD3562021B827FC0008A70F6 /* grep.1 in Install Man Page */,
+ );
+ name = "Install Man Page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FDA49B8C1B7186CE003B4F3C /* Copy Files */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(EE_PREFIX)/share/man/man1";
+ dstSubfolderSpec = 0;
+ files = (
+ FDA49B971B71876A003B4F3C /* ee.1 in Copy Files */,
+ );
+ name = "Copy Files";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 3EF506E11F7ABD7500421EB3 /* tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = tests; sourceTree = "<group>"; };
+ FC6C98FB149A94EB00DDCC47 /* libcurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurses.dylib; path = /usr/lib/libcurses.dylib; sourceTree = "<absolute>"; };
+ FC7A7B39149865C80086576A /* banner */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = banner; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7B4B149866AE0086576A /* banner.6 */ = {isa = PBXFileReference; lastKnownFileType = text; path = banner.6; sourceTree = "<group>"; };
+ FC7A7B4C149866AE0086576A /* banner.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = banner.c; sourceTree = "<group>"; };
+ FC7A7B4F149866AE0086576A /* cat.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cat.1; sourceTree = "<group>"; };
+ FC7A7B50149866AE0086576A /* cat.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cat.c; sourceTree = "<group>"; };
+ FC7A7B53149866AE0086576A /* col.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = col.1; sourceTree = "<group>"; };
+ FC7A7B54149866AE0086576A /* col.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = col.c; sourceTree = "<group>"; };
+ FC7A7B56149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+ FC7A7B58149866AE0086576A /* colrm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = colrm.1; sourceTree = "<group>"; };
+ FC7A7B59149866AE0086576A /* colrm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = colrm.c; sourceTree = "<group>"; };
+ FC7A7B5C149866AE0086576A /* column.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = column.1; sourceTree = "<group>"; };
+ FC7A7B5D149866AE0086576A /* column.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = column.c; sourceTree = "<group>"; };
+ FC7A7B60149866AE0086576A /* comm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = comm.1; sourceTree = "<group>"; };
+ FC7A7B61149866AE0086576A /* comm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = comm.c; sourceTree = "<group>"; };
+ FC7A7B64149866AE0086576A /* csplit.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = csplit.1; sourceTree = "<group>"; };
+ FC7A7B65149866AE0086576A /* csplit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = csplit.c; sourceTree = "<group>"; };
+ FC7A7B68149866AE0086576A /* cut.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cut.1; sourceTree = "<group>"; };
+ FC7A7B69149866AE0086576A /* cut.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cut.c; sourceTree = "<group>"; };
+ FC7A7B6C149866AE0086576A /* buf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = buf.c; sourceTree = "<group>"; };
+ FC7A7B6D149866AE0086576A /* cbc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cbc.c; sourceTree = "<group>"; };
+ FC7A7B6E149866AE0086576A /* ed.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ed.1; sourceTree = "<group>"; };
+ FC7A7B6F149866AE0086576A /* ed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ed.h; sourceTree = "<group>"; };
+ FC7A7B70149866AE0086576A /* glbl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = glbl.c; sourceTree = "<group>"; };
+ FC7A7B71149866AE0086576A /* io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = io.c; sourceTree = "<group>"; };
+ FC7A7B72149866AE0086576A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
+ FC7A7B74149866AE0086576A /* POSIX */ = {isa = PBXFileReference; lastKnownFileType = text; path = POSIX; sourceTree = "<group>"; };
+ FC7A7B75149866AE0086576A /* re.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = re.c; sourceTree = "<group>"; };
+ FC7A7B76149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+ FC7A7B77149866AE0086576A /* red.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = red.1; sourceTree = "<group>"; };
+ FC7A7B78149866AE0086576A /* sub.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sub.c; sourceTree = "<group>"; };
+ FC7A7B7A149866AE0086576A /* =.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = "=.err"; sourceTree = "<group>"; };
+ FC7A7B7B149866AE0086576A /* a.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = a.d; sourceTree = "<group>"; };
+ FC7A7B7C149866AE0086576A /* a.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = a.r; sourceTree = "<group>"; };
+ FC7A7B7D149866AE0086576A /* a.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = a.t; sourceTree = "<group>"; };
+ FC7A7B7E149866AE0086576A /* a1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = a1.err; sourceTree = "<group>"; };
+ FC7A7B7F149866AE0086576A /* a2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = a2.err; sourceTree = "<group>"; };
+ FC7A7B80149866AE0086576A /* addr.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = addr.d; sourceTree = "<group>"; };
+ FC7A7B81149866AE0086576A /* addr.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = addr.r; sourceTree = "<group>"; };
+ FC7A7B82149866AE0086576A /* addr.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr.t; sourceTree = "<group>"; };
+ FC7A7B83149866AE0086576A /* addr1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr1.err; sourceTree = "<group>"; };
+ FC7A7B84149866AE0086576A /* addr2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr2.err; sourceTree = "<group>"; };
+ FC7A7B85149866AE0086576A /* ascii.d.uu */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.d.uu; sourceTree = "<group>"; };
+ FC7A7B86149866AE0086576A /* ascii.r.uu */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.r.uu; sourceTree = "<group>"; };
+ FC7A7B87149866AE0086576A /* ascii.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.t; sourceTree = "<group>"; };
+ FC7A7B88149866AE0086576A /* bang1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = bang1.d; sourceTree = "<group>"; };
+ FC7A7B89149866AE0086576A /* bang1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang1.err; sourceTree = "<group>"; };
+ FC7A7B8A149866AE0086576A /* bang1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = bang1.r; sourceTree = "<group>"; };
+ FC7A7B8B149866AE0086576A /* bang1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang1.t; sourceTree = "<group>"; };
+ FC7A7B8C149866AE0086576A /* bang2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang2.err; sourceTree = "<group>"; };
+ FC7A7B8D149866AE0086576A /* c.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = c.d; sourceTree = "<group>"; };
+ FC7A7B8E149866AE0086576A /* c.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = c.r; sourceTree = "<group>"; };
+ FC7A7B8F149866AE0086576A /* c.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = c.t; sourceTree = "<group>"; };
+ FC7A7B90149866AE0086576A /* c1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = c1.err; sourceTree = "<group>"; };
+ FC7A7B91149866AE0086576A /* c2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = c2.err; sourceTree = "<group>"; };
+ FC7A7B92149866AE0086576A /* ckscripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ckscripts.sh; sourceTree = "<group>"; };
+ FC7A7B93149866AE0086576A /* d.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = d.d; sourceTree = "<group>"; };
+ FC7A7B94149866AE0086576A /* d.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = d.err; sourceTree = "<group>"; };
+ FC7A7B95149866AE0086576A /* d.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = d.r; sourceTree = "<group>"; };
+ FC7A7B96149866AE0086576A /* d.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = d.t; sourceTree = "<group>"; };
+ FC7A7B97149866AE0086576A /* e1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e1.d; sourceTree = "<group>"; };
+ FC7A7B98149866AE0086576A /* e1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e1.err; sourceTree = "<group>"; };
+ FC7A7B99149866AE0086576A /* e1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e1.r; sourceTree = "<group>"; };
+ FC7A7B9A149866AE0086576A /* e1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e1.t; sourceTree = "<group>"; };
+ FC7A7B9B149866AE0086576A /* e2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e2.d; sourceTree = "<group>"; };
+ FC7A7B9C149866AE0086576A /* e2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e2.err; sourceTree = "<group>"; };
+ FC7A7B9D149866AE0086576A /* e2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e2.r; sourceTree = "<group>"; };
+ FC7A7B9E149866AE0086576A /* e2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e2.t; sourceTree = "<group>"; };
+ FC7A7B9F149866AE0086576A /* e3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e3.d; sourceTree = "<group>"; };
+ FC7A7BA0149866AE0086576A /* e3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e3.err; sourceTree = "<group>"; };
+ FC7A7BA1149866AE0086576A /* e3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e3.r; sourceTree = "<group>"; };
+ FC7A7BA2149866AE0086576A /* e3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e3.t; sourceTree = "<group>"; };
+ FC7A7BA3149866AE0086576A /* e4.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e4.d; sourceTree = "<group>"; };
+ FC7A7BA4149866AE0086576A /* e4.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e4.r; sourceTree = "<group>"; };
+ FC7A7BA5149866AE0086576A /* e4.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e4.t; sourceTree = "<group>"; };
+ FC7A7BA6149866AE0086576A /* f1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = f1.err; sourceTree = "<group>"; };
+ FC7A7BA7149866AE0086576A /* f2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = f2.err; sourceTree = "<group>"; };
+ FC7A7BA8149866AE0086576A /* g1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g1.d; sourceTree = "<group>"; };
+ FC7A7BA9149866AE0086576A /* g1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g1.err; sourceTree = "<group>"; };
+ FC7A7BAA149866AE0086576A /* g1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g1.r; sourceTree = "<group>"; };
+ FC7A7BAB149866AE0086576A /* g1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g1.t; sourceTree = "<group>"; };
+ FC7A7BAC149866AE0086576A /* g2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g2.d; sourceTree = "<group>"; };
+ FC7A7BAD149866AE0086576A /* g2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g2.err; sourceTree = "<group>"; };
+ FC7A7BAE149866AE0086576A /* g2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g2.r; sourceTree = "<group>"; };
+ FC7A7BAF149866AE0086576A /* g2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g2.t; sourceTree = "<group>"; };
+ FC7A7BB0149866AE0086576A /* g3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g3.d; sourceTree = "<group>"; };
+ FC7A7BB1149866AE0086576A /* g3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g3.err; sourceTree = "<group>"; };
+ FC7A7BB2149866AE0086576A /* g3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g3.r; sourceTree = "<group>"; };
+ FC7A7BB3149866AE0086576A /* g3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g3.t; sourceTree = "<group>"; };
+ FC7A7BB4149866AE0086576A /* g4.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g4.d; sourceTree = "<group>"; };
+ FC7A7BB5149866AE0086576A /* g4.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g4.r; sourceTree = "<group>"; };
+ FC7A7BB6149866AE0086576A /* g4.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g4.t; sourceTree = "<group>"; };
+ FC7A7BB7149866AE0086576A /* g5.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g5.d; sourceTree = "<group>"; };
+ FC7A7BB8149866AE0086576A /* g5.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g5.r; sourceTree = "<group>"; };
+ FC7A7BB9149866AE0086576A /* g5.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g5.t; sourceTree = "<group>"; };
+ FC7A7BBA149866AE0086576A /* h.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = h.err; sourceTree = "<group>"; };
+ FC7A7BBB149866AE0086576A /* i.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = i.d; sourceTree = "<group>"; };
+ FC7A7BBC149866AE0086576A /* i.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = i.r; sourceTree = "<group>"; };
+ FC7A7BBD149866AE0086576A /* i.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = i.t; sourceTree = "<group>"; };
+ FC7A7BBE149866AE0086576A /* i1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i1.err; sourceTree = "<group>"; };
+ FC7A7BBF149866AE0086576A /* i2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i2.err; sourceTree = "<group>"; };
+ FC7A7BC0149866AE0086576A /* i3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i3.err; sourceTree = "<group>"; };
+ FC7A7BC1149866AE0086576A /* j.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = j.d; sourceTree = "<group>"; };
+ FC7A7BC2149866AE0086576A /* j.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = j.r; sourceTree = "<group>"; };
+ FC7A7BC3149866AE0086576A /* j.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = j.t; sourceTree = "<group>"; };
+ FC7A7BC4149866AE0086576A /* k.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = k.d; sourceTree = "<group>"; };
+ FC7A7BC5149866AE0086576A /* k.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = k.r; sourceTree = "<group>"; };
+ FC7A7BC6149866AE0086576A /* k.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = k.t; sourceTree = "<group>"; };
+ FC7A7BC7149866AE0086576A /* k1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k1.err; sourceTree = "<group>"; };
+ FC7A7BC8149866AE0086576A /* k2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k2.err; sourceTree = "<group>"; };
+ FC7A7BC9149866AE0086576A /* k3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k3.err; sourceTree = "<group>"; };
+ FC7A7BCA149866AE0086576A /* k4.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k4.err; sourceTree = "<group>"; };
+ FC7A7BCB149866AE0086576A /* l.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = l.d; sourceTree = "<group>"; };
+ FC7A7BCC149866AE0086576A /* l.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = l.r; sourceTree = "<group>"; };
+ FC7A7BCD149866AE0086576A /* l.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = l.t; sourceTree = "<group>"; };
+ FC7A7BCE149866AE0086576A /* m.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = m.d; sourceTree = "<group>"; };
+ FC7A7BCF149866AE0086576A /* m.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = m.err; sourceTree = "<group>"; };
+ FC7A7BD0149866AE0086576A /* m.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = m.r; sourceTree = "<group>"; };
+ FC7A7BD1149866AE0086576A /* m.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = m.t; sourceTree = "<group>"; };
+ FC7A7BD2149866AE0086576A /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ FC7A7BD3149866AE0086576A /* mkscripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = mkscripts.sh; sourceTree = "<group>"; };
+ FC7A7BD4149866AE0086576A /* n.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = n.d; sourceTree = "<group>"; };
+ FC7A7BD5149866AE0086576A /* n.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = n.r; sourceTree = "<group>"; };
+ FC7A7BD6149866AE0086576A /* n.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = n.t; sourceTree = "<group>"; };
+ FC7A7BD7149866AE0086576A /* nl.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl.err; sourceTree = "<group>"; };
+ FC7A7BD8149866AE0086576A /* nl1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = nl1.d; sourceTree = "<group>"; };
+ FC7A7BD9149866AE0086576A /* nl1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = nl1.r; sourceTree = "<group>"; };
+ FC7A7BDA149866AE0086576A /* nl1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl1.t; sourceTree = "<group>"; };
+ FC7A7BDB149866AE0086576A /* nl2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = nl2.d; sourceTree = "<group>"; };
+ FC7A7BDC149866AE0086576A /* nl2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = nl2.r; sourceTree = "<group>"; };
+ FC7A7BDD149866AE0086576A /* nl2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl2.t; sourceTree = "<group>"; };
+ FC7A7BDE149866AE0086576A /* p.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = p.d; sourceTree = "<group>"; };
+ FC7A7BDF149866AE0086576A /* p.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = p.r; sourceTree = "<group>"; };
+ FC7A7BE0149866AE0086576A /* p.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = p.t; sourceTree = "<group>"; };
+ FC7A7BE1149866AE0086576A /* q.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = q.d; sourceTree = "<group>"; };
+ FC7A7BE2149866AE0086576A /* q.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = q.r; sourceTree = "<group>"; };
+ FC7A7BE3149866AE0086576A /* q.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = q.t; sourceTree = "<group>"; };
+ FC7A7BE4149866AE0086576A /* q1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = q1.err; sourceTree = "<group>"; };
+ FC7A7BE5149866AE0086576A /* r1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r1.d; sourceTree = "<group>"; };
+ FC7A7BE6149866AE0086576A /* r1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = r1.err; sourceTree = "<group>"; };
+ FC7A7BE7149866AE0086576A /* r1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r1.r; sourceTree = "<group>"; };
+ FC7A7BE8149866AE0086576A /* r1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r1.t; sourceTree = "<group>"; };
+ FC7A7BE9149866AE0086576A /* r2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r2.d; sourceTree = "<group>"; };
+ FC7A7BEA149866AE0086576A /* r2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = r2.err; sourceTree = "<group>"; };
+ FC7A7BEB149866AE0086576A /* r2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r2.r; sourceTree = "<group>"; };
+ FC7A7BEC149866AE0086576A /* r2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r2.t; sourceTree = "<group>"; };
+ FC7A7BED149866AE0086576A /* r3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r3.d; sourceTree = "<group>"; };
+ FC7A7BEE149866AE0086576A /* r3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r3.r; sourceTree = "<group>"; };
+ FC7A7BEF149866AE0086576A /* r3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r3.t; sourceTree = "<group>"; };
+ FC7A7BF0149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+ FC7A7BF1149866AE0086576A /* s1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s1.d; sourceTree = "<group>"; };
+ FC7A7BF2149866AE0086576A /* s1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s1.err; sourceTree = "<group>"; };
+ FC7A7BF3149866AE0086576A /* s1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s1.r; sourceTree = "<group>"; };
+ FC7A7BF4149866AE0086576A /* s1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s1.t; sourceTree = "<group>"; };
+ FC7A7BF5149866AE0086576A /* s10.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s10.err; sourceTree = "<group>"; };
+ FC7A7BF6149866AE0086576A /* s2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s2.d; sourceTree = "<group>"; };
+ FC7A7BF7149866AE0086576A /* s2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s2.err; sourceTree = "<group>"; };
+ FC7A7BF8149866AE0086576A /* s2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s2.r; sourceTree = "<group>"; };
+ FC7A7BF9149866AE0086576A /* s2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s2.t; sourceTree = "<group>"; };
+ FC7A7BFA149866AE0086576A /* s3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s3.d; sourceTree = "<group>"; };
+ FC7A7BFB149866AE0086576A /* s3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s3.err; sourceTree = "<group>"; };
+ FC7A7BFC149866AE0086576A /* s3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s3.r; sourceTree = "<group>"; };
+ FC7A7BFD149866AE0086576A /* s3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s3.t; sourceTree = "<group>"; };
+ FC7A7BFE149866AE0086576A /* s4.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s4.err; sourceTree = "<group>"; };
+ FC7A7BFF149866AF0086576A /* s5.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s5.err; sourceTree = "<group>"; };
+ FC7A7C00149866AF0086576A /* s6.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s6.err; sourceTree = "<group>"; };
+ FC7A7C01149866AF0086576A /* s7.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s7.err; sourceTree = "<group>"; };
+ FC7A7C02149866AF0086576A /* s8.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s8.err; sourceTree = "<group>"; };
+ FC7A7C03149866AF0086576A /* s9.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s9.err; sourceTree = "<group>"; };
+ FC7A7C04149866AF0086576A /* t.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t.d; sourceTree = "<group>"; };
+ FC7A7C05149866AF0086576A /* t.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t.r; sourceTree = "<group>"; };
+ FC7A7C06149866AF0086576A /* t1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t1.d; sourceTree = "<group>"; };
+ FC7A7C07149866AF0086576A /* t1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = t1.err; sourceTree = "<group>"; };
+ FC7A7C08149866AF0086576A /* t1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t1.r; sourceTree = "<group>"; };
+ FC7A7C09149866AF0086576A /* t1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = t1.t; sourceTree = "<group>"; };
+ FC7A7C0A149866AF0086576A /* t2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t2.d; sourceTree = "<group>"; };
+ FC7A7C0B149866AF0086576A /* t2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = t2.err; sourceTree = "<group>"; };
+ FC7A7C0C149866AF0086576A /* t2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t2.r; sourceTree = "<group>"; };
+ FC7A7C0D149866AF0086576A /* t2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = t2.t; sourceTree = "<group>"; };
+ FC7A7C0E149866AF0086576A /* TODO */ = {isa = PBXFileReference; lastKnownFileType = text; path = TODO; sourceTree = "<group>"; };
+ FC7A7C0F149866AF0086576A /* u.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = u.d; sourceTree = "<group>"; };
+ FC7A7C10149866AF0086576A /* u.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = u.err; sourceTree = "<group>"; };
+ FC7A7C11149866AF0086576A /* u.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = u.r; sourceTree = "<group>"; };
+ FC7A7C12149866AF0086576A /* u.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = u.t; sourceTree = "<group>"; };
+ FC7A7C13149866AF0086576A /* v.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = v.d; sourceTree = "<group>"; };
+ FC7A7C14149866AF0086576A /* v.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = v.r; sourceTree = "<group>"; };
+ FC7A7C15149866AF0086576A /* v.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = v.t; sourceTree = "<group>"; };
+ FC7A7C16149866AF0086576A /* w.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = w.d; sourceTree = "<group>"; };
+ FC7A7C17149866AF0086576A /* w.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = w.r; sourceTree = "<group>"; };
+ FC7A7C18149866AF0086576A /* w.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = w.t; sourceTree = "<group>"; };
+ FC7A7C19149866AF0086576A /* w1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w1.err; sourceTree = "<group>"; };
+ FC7A7C1A149866AF0086576A /* w2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w2.err; sourceTree = "<group>"; };
+ FC7A7C1B149866AF0086576A /* w3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w3.err; sourceTree = "<group>"; };
+ FC7A7C1C149866AF0086576A /* x.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = x.err; sourceTree = "<group>"; };
+ FC7A7C1D149866AF0086576A /* z.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = z.err; sourceTree = "<group>"; };
+ FC7A7C1E149866AF0086576A /* undo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = undo.c; sourceTree = "<group>"; };
+ FC7A7C20149866AF0086576A /* expand.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = expand.1; sourceTree = "<group>"; };
+ FC7A7C21149866AF0086576A /* expand.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = expand.c; sourceTree = "<group>"; };
+ FC7A7C24149866AF0086576A /* fmt.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fmt.1; sourceTree = "<group>"; };
+ FC7A7C25149866AF0086576A /* fmt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fmt.c; sourceTree = "<group>"; };
+ FC7A7C28149866AF0086576A /* fold.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fold.1; sourceTree = "<group>"; };
+ FC7A7C29149866AF0086576A /* fold.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fold.c; sourceTree = "<group>"; };
+ FC7A7C2C149866AF0086576A /* head.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = head.1; sourceTree = "<group>"; };
+ FC7A7C2D149866AF0086576A /* head.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = head.c; sourceTree = "<group>"; };
+ FC7A7C30149866AF0086576A /* join.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = join.1; sourceTree = "<group>"; };
+ FC7A7C31149866AF0086576A /* join.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = join.c; sourceTree = "<group>"; };
+ FC7A7C34149866AF0086576A /* lam.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = lam.1; sourceTree = "<group>"; };
+ FC7A7C35149866AF0086576A /* lam.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = lam.c; sourceTree = "<group>"; };
+ FC7A7C38149866AF0086576A /* look.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = look.1; sourceTree = "<group>"; };
+ FC7A7C39149866AF0086576A /* look.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = look.c; sourceTree = "<group>"; };
+ FC7A7C3B149866AF0086576A /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; };
+ FC7A7C3D149866AF0086576A /* commoncrypto.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = "<group>"; };
+ FC7A7C3E149866AF0086576A /* commoncrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = "<group>"; };
+ FC7A7C40149866AF0086576A /* md5.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = md5.1; sourceTree = "<group>"; };
+ FC7A7C41149866AF0086576A /* md5.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = md5.c; sourceTree = "<group>"; };
+ FC7A7C44149866AF0086576A /* nl.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = nl.1; sourceTree = "<group>"; };
+ FC7A7C45149866AF0086576A /* nl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nl.c; sourceTree = "<group>"; };
+ FC7A7C48149866AF0086576A /* paste.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = paste.1; sourceTree = "<group>"; };
+ FC7A7C49149866AF0086576A /* paste.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = paste.c; sourceTree = "<group>"; };
+ FC7A7C4B149866AF0086576A /* egetopt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = egetopt.c; sourceTree = "<group>"; };
+ FC7A7C4C149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FC7A7C4E149866AF0086576A /* pr.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pr.1; sourceTree = "<group>"; };
+ FC7A7C4F149866AF0086576A /* pr.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pr.c; sourceTree = "<group>"; };
+ FC7A7C50149866AF0086576A /* pr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pr.h; sourceTree = "<group>"; };
+ FC7A7C53149866AF0086576A /* rev.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rev.1; sourceTree = "<group>"; };
+ FC7A7C54149866AF0086576A /* rev.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rev.c; sourceTree = "<group>"; };
+ FC7A7C57149866AF0086576A /* rs.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rs.1; sourceTree = "<group>"; };
+ FC7A7C58149866AF0086576A /* rs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rs.c; sourceTree = "<group>"; };
+ FC7A7C5A149866AF0086576A /* compile.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compile.c; sourceTree = "<group>"; };
+ FC7A7C5B149866AF0086576A /* defs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = defs.h; sourceTree = "<group>"; };
+ FC7A7C5C149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FC7A7C5D149866AF0086576A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
+ FC7A7C5F149866AF0086576A /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; };
+ FC7A7C60149866AF0086576A /* POSIX */ = {isa = PBXFileReference; lastKnownFileType = text; path = POSIX; sourceTree = "<group>"; };
+ FC7A7C61149866AF0086576A /* process.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = process.c; sourceTree = "<group>"; };
+ FC7A7C62149866AF0086576A /* sed.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = sed.1; sourceTree = "<group>"; };
+ FC7A7C64149866AF0086576A /* hanoi.sed */ = {isa = PBXFileReference; lastKnownFileType = text; path = hanoi.sed; sourceTree = "<group>"; };
+ FC7A7C65149866AF0086576A /* math.sed */ = {isa = PBXFileReference; lastKnownFileType = text; path = math.sed; sourceTree = "<group>"; };
+ FC7A7C66149866AF0086576A /* sed.test */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sed.test; sourceTree = "<group>"; };
+ FC7A7C93149866AF0086576A /* sort.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sort.c; sourceTree = "<group>"; };
+ FC7A7CB4149866AF0086576A /* split.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = split.1; sourceTree = "<group>"; };
+ FC7A7CB5149866AF0086576A /* split.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = split.c; sourceTree = "<group>"; };
+ FC7A7CB7149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FC7A7CB8149866AF0086576A /* forward.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = forward.c; sourceTree = "<group>"; };
+ FC7A7CBA149866AF0086576A /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; };
+ FC7A7CBB149866AF0086576A /* read.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = read.c; sourceTree = "<group>"; };
+ FC7A7CBC149866AF0086576A /* reverse.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = reverse.c; sourceTree = "<group>"; };
+ FC7A7CBD149866AF0086576A /* tail.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tail.1; sourceTree = "<group>"; };
+ FC7A7CBE149866AF0086576A /* tail.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tail.c; sourceTree = "<group>"; };
+ FC7A7CBF149866AF0086576A /* text_cmds.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = text_cmds.plist; sourceTree = "<group>"; };
+ FC7A7CC1149866AF0086576A /* cmap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmap.c; sourceTree = "<group>"; };
+ FC7A7CC2149866AF0086576A /* cmap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cmap.h; sourceTree = "<group>"; };
+ FC7A7CC3149866AF0086576A /* cset.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cset.c; sourceTree = "<group>"; };
+ FC7A7CC4149866AF0086576A /* cset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cset.h; sourceTree = "<group>"; };
+ FC7A7CC5149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FC7A7CC7149866AF0086576A /* str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = str.c; sourceTree = "<group>"; };
+ FC7A7CC8149866AF0086576A /* tr.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tr.1; sourceTree = "<group>"; };
+ FC7A7CC9149866AF0086576A /* tr.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tr.c; sourceTree = "<group>"; };
+ FC7A7CCC149866AF0086576A /* ul.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ul.1; sourceTree = "<group>"; };
+ FC7A7CCD149866AF0086576A /* ul.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ul.c; sourceTree = "<group>"; };
+ FC7A7CD0149866AF0086576A /* unexpand.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unexpand.c; sourceTree = "<group>"; };
+ FC7A7CD3149866AF0086576A /* uniq.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uniq.1; sourceTree = "<group>"; };
+ FC7A7CD4149866AF0086576A /* uniq.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = uniq.c; sourceTree = "<group>"; };
+ FC7A7CD7149866AF0086576A /* unvis.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = unvis.1; sourceTree = "<group>"; };
+ FC7A7CD8149866AF0086576A /* unvis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unvis.c; sourceTree = "<group>"; };
+ FC7A7CDA149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FC7A7CDB149866AF0086576A /* foldit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = foldit.c; sourceTree = "<group>"; };
+ FC7A7CDD149866AF0086576A /* vis.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = vis.1; sourceTree = "<group>"; };
+ FC7A7CDE149866AF0086576A /* vis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vis.c; sourceTree = "<group>"; };
+ FC7A7CE1149866AF0086576A /* wc.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = wc.1; sourceTree = "<group>"; };
+ FC7A7CE2149866AF0086576A /* wc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wc.c; sourceTree = "<group>"; };
+ FC7A7CEA149867ED0086576A /* cat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cat; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7CF2149869E90086576A /* col */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = col; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7CFA14986A160086576A /* colrm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = colrm; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D0214986A190086576A /* column */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = column; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D0A14986A1B0086576A /* comm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = comm; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D1214986A1E0086576A /* csplit */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = csplit; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D1A14986A210086576A /* cut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cut; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D2214986A230086576A /* ed */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ed; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D2A14986A240086576A /* expand */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = expand; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D3214986A280086576A /* fmt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fmt; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D3A14986A2B0086576A /* fold */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fold; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D4214986A2F0086576A /* head */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = head; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D4A14986A310086576A /* join */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = join; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D5214986A340086576A /* lam */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lam; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D5A14986A360086576A /* look */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = look; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D6214986A390086576A /* md5 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = md5; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D6A14986A3C0086576A /* nl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nl; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D7214986A3D0086576A /* paste */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = paste; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D7A14986A400086576A /* pr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pr; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D8214986A410086576A /* rev */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rev; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D8A14986A430086576A /* rs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rs; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D9214986A450086576A /* sed */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sed; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7D9A14986A470086576A /* sort */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sort; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DA214986A480086576A /* split */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = split; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DAA14986A4A0086576A /* tail */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tail; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DB214986A500086576A /* tr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tr; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DBA14986A530086576A /* ul */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ul; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DC214986A540086576A /* unexpand */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unexpand; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DCA14986A560086576A /* uniq */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = uniq; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DD214986A580086576A /* unvis */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unvis; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DDA14986A5A0086576A /* vis */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = vis; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC7A7DE214986A5C0086576A /* wc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wc; sourceTree = BUILT_PRODUCTS_DIR; };
+ FD2BDC161B7188240053EE6B /* ee.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ee.xcconfig; sourceTree = "<group>"; };
+ FD3561F21B827EE0008A70F6 /* grep */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = grep; sourceTree = BUILT_PRODUCTS_DIR; };
+ FD3561F81B827F9F008A70F6 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
+ FD3561F91B827F9F008A70F6 /* grep.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = grep.1; sourceTree = "<group>"; };
+ FD3561FA1B827F9F008A70F6 /* grep.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = grep.c; sourceTree = "<group>"; };
+ FD3561FB1B827F9F008A70F6 /* grep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = grep.h; sourceTree = "<group>"; };
+ FD3561FC1B827F9F008A70F6 /* queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = queue.c; sourceTree = "<group>"; };
+ FD3561FD1B827F9F008A70F6 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = "<group>"; };
+ FD3562031B82803F008A70F6 /* grep.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = grep.xcconfig; sourceTree = "<group>"; };
+ FDA49B8E1B7186CE003B4F3C /* ee */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ee; sourceTree = BUILT_PRODUCTS_DIR; };
+ FDA49B941B718738003B4F3C /* ee.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ee.c; sourceTree = "<group>"; };
+ FDA49B961B718762003B4F3C /* ee.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = ee.1; sourceTree = "<group>"; };
+ FDF283571DAED0C000CF8C36 /* bwstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwstring.c; sourceTree = "<group>"; };
+ FDF283581DAED0C000CF8C36 /* bwstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bwstring.h; sourceTree = "<group>"; };
+ FDF283591DAED0C000CF8C36 /* coll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coll.c; sourceTree = "<group>"; };
+ FDF2835A1DAED0C000CF8C36 /* coll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coll.h; sourceTree = "<group>"; };
+ FDF2835B1DAED0C000CF8C36 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
+ FDF2835C1DAED0C000CF8C36 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
+ FDF2835D1DAED0C000CF8C36 /* mem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mem.c; sourceTree = "<group>"; };
+ FDF2835E1DAED0C000CF8C36 /* mem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem.h; sourceTree = "<group>"; };
+ FDF283601DAED0C000CF8C36 /* C.msg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = C.msg; sourceTree = "<group>"; };
+ FDF283611DAED0C000CF8C36 /* hu_HU.ISO8859-2.msg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "hu_HU.ISO8859-2.msg"; sourceTree = "<group>"; };
+ FDF283621DAED0C000CF8C36 /* radixsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = radixsort.c; sourceTree = "<group>"; };
+ FDF283631DAED0C000CF8C36 /* radixsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = radixsort.h; sourceTree = "<group>"; };
+ FDF283641DAED0C000CF8C36 /* sort.1.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = sort.1.in; sourceTree = "<group>"; };
+ FDF283651DAED0C000CF8C36 /* sort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sort.h; sourceTree = "<group>"; };
+ FDF283661DAED0C000CF8C36 /* vsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsort.c; sourceTree = "<group>"; };
+ FDF283671DAED0C000CF8C36 /* vsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vsort.h; sourceTree = "<group>"; };
+ FDF2836E1DAED24800CF8C36 /* sort.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = sort.xcconfig; sourceTree = "<group>"; };
+ FDF2836F1DAED7C700CF8C36 /* commoncrypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = "<group>"; };
+ FDF283701DAED7C700CF8C36 /* commoncrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ FC7A7B36149865C80086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CE6149867ED0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CEE149869E90086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CF614986A160086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CFE14986A190086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D0614986A1B0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D0E14986A1E0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D1614986A210086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D1E14986A230086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D2614986A240086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D2E14986A280086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D3614986A2B0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D3E14986A2F0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D4614986A310086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D4E14986A340086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D5614986A360086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D5E14986A390086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D6614986A3C0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D6E14986A3D0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D7614986A400086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D7E14986A410086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D8614986A430086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D8E14986A450086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D9614986A470086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D9E14986A480086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DA614986A4A0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DAE14986A500086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DB614986A530086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC6C98FC149A94EB00DDCC47 /* libcurses.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DBE14986A540086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DC614986A560086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DCE14986A580086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DD614986A5A0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DDE14986A5C0086576A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FD3561EF1B827EE0008A70F6 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FDA49B8B1B7186CE003B4F3C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ FC7A7B2E149865C70086576A = {
+ isa = PBXGroup;
+ children = (
+ 3EF506E11F7ABD7500421EB3 /* tests */,
+ FC7A7B4A149866AE0086576A /* banner */,
+ FC7A7B4E149866AE0086576A /* cat */,
+ FC7A7B52149866AE0086576A /* col */,
+ FC7A7B57149866AE0086576A /* colrm */,
+ FC7A7B5B149866AE0086576A /* column */,
+ FC7A7B5F149866AE0086576A /* comm */,
+ FC7A7B63149866AE0086576A /* csplit */,
+ FC7A7B67149866AE0086576A /* cut */,
+ FC7A7B6B149866AE0086576A /* ed */,
+ FDA49B8F1B7186CE003B4F3C /* ee */,
+ FC7A7C1F149866AF0086576A /* expand */,
+ FC7A7C23149866AF0086576A /* fmt */,
+ FC7A7C27149866AF0086576A /* fold */,
+ FD3561F31B827EE0008A70F6 /* grep */,
+ FC7A7C2B149866AF0086576A /* head */,
+ FC7A7C2F149866AF0086576A /* join */,
+ FC7A7C33149866AF0086576A /* lam */,
+ FC7A7C37149866AF0086576A /* look */,
+ FC7A7C3C149866AF0086576A /* md5 */,
+ FC7A7C42149866AF0086576A /* nl */,
+ FC7A7C46149866AF0086576A /* paste */,
+ FC7A7C4A149866AF0086576A /* pr */,
+ FC7A7C51149866AF0086576A /* rev */,
+ FC7A7C55149866AF0086576A /* rs */,
+ FC7A7C59149866AF0086576A /* sed */,
+ FC7A7C67149866AF0086576A /* sort */,
+ FC7A7CB2149866AF0086576A /* split */,
+ FC7A7CB6149866AF0086576A /* tail */,
+ FC7A7CBF149866AF0086576A /* text_cmds.plist */,
+ FC7A7CC0149866AF0086576A /* tr */,
+ FC7A7CCA149866AF0086576A /* ul */,
+ FC7A7CCE149866AF0086576A /* unexpand */,
+ FC7A7CD1149866AF0086576A /* uniq */,
+ FC7A7CD5149866AF0086576A /* unvis */,
+ FC7A7CD9149866AF0086576A /* vis */,
+ FC7A7CDF149866AF0086576A /* wc */,
+ FD2BDC151B7188240053EE6B /* xcconfigs */,
+ FC7A7B3A149865C80086576A /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ FC7A7B3A149865C80086576A /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B39149865C80086576A /* banner */,
+ FC7A7CEA149867ED0086576A /* cat */,
+ FC7A7CF2149869E90086576A /* col */,
+ FC7A7CFA14986A160086576A /* colrm */,
+ FC7A7D0214986A190086576A /* column */,
+ FC7A7D0A14986A1B0086576A /* comm */,
+ FC7A7D1214986A1E0086576A /* csplit */,
+ FC7A7D1A14986A210086576A /* cut */,
+ FC7A7D2214986A230086576A /* ed */,
+ FDA49B8E1B7186CE003B4F3C /* ee */,
+ FC7A7D2A14986A240086576A /* expand */,
+ FC7A7D3214986A280086576A /* fmt */,
+ FC7A7D3A14986A2B0086576A /* fold */,
+ FD3561F21B827EE0008A70F6 /* grep */,
+ FC7A7D4214986A2F0086576A /* head */,
+ FC7A7D4A14986A310086576A /* join */,
+ FC7A7D5214986A340086576A /* lam */,
+ FC7A7D5A14986A360086576A /* look */,
+ FC7A7D6214986A390086576A /* md5 */,
+ FC7A7D6A14986A3C0086576A /* nl */,
+ FC7A7D7214986A3D0086576A /* paste */,
+ FC7A7D7A14986A400086576A /* pr */,
+ FC7A7D8214986A410086576A /* rev */,
+ FC7A7D8A14986A430086576A /* rs */,
+ FC7A7D9214986A450086576A /* sed */,
+ FC7A7D9A14986A470086576A /* sort */,
+ FC7A7DA214986A480086576A /* split */,
+ FC7A7DAA14986A4A0086576A /* tail */,
+ FC7A7DB214986A500086576A /* tr */,
+ FC7A7DBA14986A530086576A /* ul */,
+ FC7A7DC214986A540086576A /* unexpand */,
+ FC7A7DCA14986A560086576A /* uniq */,
+ FC7A7DD214986A580086576A /* unvis */,
+ FC7A7DDA14986A5A0086576A /* vis */,
+ FC7A7DE214986A5C0086576A /* wc */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ FC7A7B4A149866AE0086576A /* banner */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B4B149866AE0086576A /* banner.6 */,
+ FC7A7B4C149866AE0086576A /* banner.c */,
+ );
+ path = banner;
+ sourceTree = "<group>";
+ };
+ FC7A7B4E149866AE0086576A /* cat */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B4F149866AE0086576A /* cat.1 */,
+ FC7A7B50149866AE0086576A /* cat.c */,
+ );
+ path = cat;
+ sourceTree = "<group>";
+ };
+ FC7A7B52149866AE0086576A /* col */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B53149866AE0086576A /* col.1 */,
+ FC7A7B54149866AE0086576A /* col.c */,
+ FC7A7B56149866AE0086576A /* README */,
+ );
+ path = col;
+ sourceTree = "<group>";
+ };
+ FC7A7B57149866AE0086576A /* colrm */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B58149866AE0086576A /* colrm.1 */,
+ FC7A7B59149866AE0086576A /* colrm.c */,
+ );
+ path = colrm;
+ sourceTree = "<group>";
+ };
+ FC7A7B5B149866AE0086576A /* column */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B5C149866AE0086576A /* column.1 */,
+ FC7A7B5D149866AE0086576A /* column.c */,
+ );
+ path = column;
+ sourceTree = "<group>";
+ };
+ FC7A7B5F149866AE0086576A /* comm */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B60149866AE0086576A /* comm.1 */,
+ FC7A7B61149866AE0086576A /* comm.c */,
+ );
+ path = comm;
+ sourceTree = "<group>";
+ };
+ FC7A7B63149866AE0086576A /* csplit */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B64149866AE0086576A /* csplit.1 */,
+ FC7A7B65149866AE0086576A /* csplit.c */,
+ );
+ path = csplit;
+ sourceTree = "<group>";
+ };
+ FC7A7B67149866AE0086576A /* cut */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B68149866AE0086576A /* cut.1 */,
+ FC7A7B69149866AE0086576A /* cut.c */,
+ );
+ path = cut;
+ sourceTree = "<group>";
+ };
+ FC7A7B6B149866AE0086576A /* ed */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B6C149866AE0086576A /* buf.c */,
+ FC7A7B6D149866AE0086576A /* cbc.c */,
+ FC7A7B6E149866AE0086576A /* ed.1 */,
+ FC7A7B6F149866AE0086576A /* ed.h */,
+ FC7A7B70149866AE0086576A /* glbl.c */,
+ FC7A7B71149866AE0086576A /* io.c */,
+ FC7A7B72149866AE0086576A /* main.c */,
+ FC7A7B74149866AE0086576A /* POSIX */,
+ FC7A7B75149866AE0086576A /* re.c */,
+ FC7A7B76149866AE0086576A /* README */,
+ FC7A7B77149866AE0086576A /* red.1 */,
+ FC7A7B78149866AE0086576A /* sub.c */,
+ FC7A7B79149866AE0086576A /* test */,
+ FC7A7C1E149866AF0086576A /* undo.c */,
+ );
+ path = ed;
+ sourceTree = "<group>";
+ };
+ FC7A7B79149866AE0086576A /* test */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7B7A149866AE0086576A /* =.err */,
+ FC7A7B7B149866AE0086576A /* a.d */,
+ FC7A7B7C149866AE0086576A /* a.r */,
+ FC7A7B7D149866AE0086576A /* a.t */,
+ FC7A7B7E149866AE0086576A /* a1.err */,
+ FC7A7B7F149866AE0086576A /* a2.err */,
+ FC7A7B80149866AE0086576A /* addr.d */,
+ FC7A7B81149866AE0086576A /* addr.r */,
+ FC7A7B82149866AE0086576A /* addr.t */,
+ FC7A7B83149866AE0086576A /* addr1.err */,
+ FC7A7B84149866AE0086576A /* addr2.err */,
+ FC7A7B85149866AE0086576A /* ascii.d.uu */,
+ FC7A7B86149866AE0086576A /* ascii.r.uu */,
+ FC7A7B87149866AE0086576A /* ascii.t */,
+ FC7A7B88149866AE0086576A /* bang1.d */,
+ FC7A7B89149866AE0086576A /* bang1.err */,
+ FC7A7B8A149866AE0086576A /* bang1.r */,
+ FC7A7B8B149866AE0086576A /* bang1.t */,
+ FC7A7B8C149866AE0086576A /* bang2.err */,
+ FC7A7B8D149866AE0086576A /* c.d */,
+ FC7A7B8E149866AE0086576A /* c.r */,
+ FC7A7B8F149866AE0086576A /* c.t */,
+ FC7A7B90149866AE0086576A /* c1.err */,
+ FC7A7B91149866AE0086576A /* c2.err */,
+ FC7A7B92149866AE0086576A /* ckscripts.sh */,
+ FC7A7B93149866AE0086576A /* d.d */,
+ FC7A7B94149866AE0086576A /* d.err */,
+ FC7A7B95149866AE0086576A /* d.r */,
+ FC7A7B96149866AE0086576A /* d.t */,
+ FC7A7B97149866AE0086576A /* e1.d */,
+ FC7A7B98149866AE0086576A /* e1.err */,
+ FC7A7B99149866AE0086576A /* e1.r */,
+ FC7A7B9A149866AE0086576A /* e1.t */,
+ FC7A7B9B149866AE0086576A /* e2.d */,
+ FC7A7B9C149866AE0086576A /* e2.err */,
+ FC7A7B9D149866AE0086576A /* e2.r */,
+ FC7A7B9E149866AE0086576A /* e2.t */,
+ FC7A7B9F149866AE0086576A /* e3.d */,
+ FC7A7BA0149866AE0086576A /* e3.err */,
+ FC7A7BA1149866AE0086576A /* e3.r */,
+ FC7A7BA2149866AE0086576A /* e3.t */,
+ FC7A7BA3149866AE0086576A /* e4.d */,
+ FC7A7BA4149866AE0086576A /* e4.r */,
+ FC7A7BA5149866AE0086576A /* e4.t */,
+ FC7A7BA6149866AE0086576A /* f1.err */,
+ FC7A7BA7149866AE0086576A /* f2.err */,
+ FC7A7BA8149866AE0086576A /* g1.d */,
+ FC7A7BA9149866AE0086576A /* g1.err */,
+ FC7A7BAA149866AE0086576A /* g1.r */,
+ FC7A7BAB149866AE0086576A /* g1.t */,
+ FC7A7BAC149866AE0086576A /* g2.d */,
+ FC7A7BAD149866AE0086576A /* g2.err */,
+ FC7A7BAE149866AE0086576A /* g2.r */,
+ FC7A7BAF149866AE0086576A /* g2.t */,
+ FC7A7BB0149866AE0086576A /* g3.d */,
+ FC7A7BB1149866AE0086576A /* g3.err */,
+ FC7A7BB2149866AE0086576A /* g3.r */,
+ FC7A7BB3149866AE0086576A /* g3.t */,
+ FC7A7BB4149866AE0086576A /* g4.d */,
+ FC7A7BB5149866AE0086576A /* g4.r */,
+ FC7A7BB6149866AE0086576A /* g4.t */,
+ FC7A7BB7149866AE0086576A /* g5.d */,
+ FC7A7BB8149866AE0086576A /* g5.r */,
+ FC7A7BB9149866AE0086576A /* g5.t */,
+ FC7A7BBA149866AE0086576A /* h.err */,
+ FC7A7BBB149866AE0086576A /* i.d */,
+ FC7A7BBC149866AE0086576A /* i.r */,
+ FC7A7BBD149866AE0086576A /* i.t */,
+ FC7A7BBE149866AE0086576A /* i1.err */,
+ FC7A7BBF149866AE0086576A /* i2.err */,
+ FC7A7BC0149866AE0086576A /* i3.err */,
+ FC7A7BC1149866AE0086576A /* j.d */,
+ FC7A7BC2149866AE0086576A /* j.r */,
+ FC7A7BC3149866AE0086576A /* j.t */,
+ FC7A7BC4149866AE0086576A /* k.d */,
+ FC7A7BC5149866AE0086576A /* k.r */,
+ FC7A7BC6149866AE0086576A /* k.t */,
+ FC7A7BC7149866AE0086576A /* k1.err */,
+ FC7A7BC8149866AE0086576A /* k2.err */,
+ FC7A7BC9149866AE0086576A /* k3.err */,
+ FC7A7BCA149866AE0086576A /* k4.err */,
+ FC7A7BCB149866AE0086576A /* l.d */,
+ FC7A7BCC149866AE0086576A /* l.r */,
+ FC7A7BCD149866AE0086576A /* l.t */,
+ FC7A7BCE149866AE0086576A /* m.d */,
+ FC7A7BCF149866AE0086576A /* m.err */,
+ FC7A7BD0149866AE0086576A /* m.r */,
+ FC7A7BD1149866AE0086576A /* m.t */,
+ FC7A7BD2149866AE0086576A /* Makefile */,
+ FC7A7BD3149866AE0086576A /* mkscripts.sh */,
+ FC7A7BD4149866AE0086576A /* n.d */,
+ FC7A7BD5149866AE0086576A /* n.r */,
+ FC7A7BD6149866AE0086576A /* n.t */,
+ FC7A7BD7149866AE0086576A /* nl.err */,
+ FC7A7BD8149866AE0086576A /* nl1.d */,
+ FC7A7BD9149866AE0086576A /* nl1.r */,
+ FC7A7BDA149866AE0086576A /* nl1.t */,
+ FC7A7BDB149866AE0086576A /* nl2.d */,
+ FC7A7BDC149866AE0086576A /* nl2.r */,
+ FC7A7BDD149866AE0086576A /* nl2.t */,
+ FC7A7BDE149866AE0086576A /* p.d */,
+ FC7A7BDF149866AE0086576A /* p.r */,
+ FC7A7BE0149866AE0086576A /* p.t */,
+ FC7A7BE1149866AE0086576A /* q.d */,
+ FC7A7BE2149866AE0086576A /* q.r */,
+ FC7A7BE3149866AE0086576A /* q.t */,
+ FC7A7BE4149866AE0086576A /* q1.err */,
+ FC7A7BE5149866AE0086576A /* r1.d */,
+ FC7A7BE6149866AE0086576A /* r1.err */,
+ FC7A7BE7149866AE0086576A /* r1.r */,
+ FC7A7BE8149866AE0086576A /* r1.t */,
+ FC7A7BE9149866AE0086576A /* r2.d */,
+ FC7A7BEA149866AE0086576A /* r2.err */,
+ FC7A7BEB149866AE0086576A /* r2.r */,
+ FC7A7BEC149866AE0086576A /* r2.t */,
+ FC7A7BED149866AE0086576A /* r3.d */,
+ FC7A7BEE149866AE0086576A /* r3.r */,
+ FC7A7BEF149866AE0086576A /* r3.t */,
+ FC7A7BF0149866AE0086576A /* README */,
+ FC7A7BF1149866AE0086576A /* s1.d */,
+ FC7A7BF2149866AE0086576A /* s1.err */,
+ FC7A7BF3149866AE0086576A /* s1.r */,
+ FC7A7BF4149866AE0086576A /* s1.t */,
+ FC7A7BF5149866AE0086576A /* s10.err */,
+ FC7A7BF6149866AE0086576A /* s2.d */,
+ FC7A7BF7149866AE0086576A /* s2.err */,
+ FC7A7BF8149866AE0086576A /* s2.r */,
+ FC7A7BF9149866AE0086576A /* s2.t */,
+ FC7A7BFA149866AE0086576A /* s3.d */,
+ FC7A7BFB149866AE0086576A /* s3.err */,
+ FC7A7BFC149866AE0086576A /* s3.r */,
+ FC7A7BFD149866AE0086576A /* s3.t */,
+ FC7A7BFE149866AE0086576A /* s4.err */,
+ FC7A7BFF149866AF0086576A /* s5.err */,
+ FC7A7C00149866AF0086576A /* s6.err */,
+ FC7A7C01149866AF0086576A /* s7.err */,
+ FC7A7C02149866AF0086576A /* s8.err */,
+ FC7A7C03149866AF0086576A /* s9.err */,
+ FC7A7C04149866AF0086576A /* t.d */,
+ FC7A7C05149866AF0086576A /* t.r */,
+ FC7A7C06149866AF0086576A /* t1.d */,
+ FC7A7C07149866AF0086576A /* t1.err */,
+ FC7A7C08149866AF0086576A /* t1.r */,
+ FC7A7C09149866AF0086576A /* t1.t */,
+ FC7A7C0A149866AF0086576A /* t2.d */,
+ FC7A7C0B149866AF0086576A /* t2.err */,
+ FC7A7C0C149866AF0086576A /* t2.r */,
+ FC7A7C0D149866AF0086576A /* t2.t */,
+ FC7A7C0E149866AF0086576A /* TODO */,
+ FC7A7C0F149866AF0086576A /* u.d */,
+ FC7A7C10149866AF0086576A /* u.err */,
+ FC7A7C11149866AF0086576A /* u.r */,
+ FC7A7C12149866AF0086576A /* u.t */,
+ FC7A7C13149866AF0086576A /* v.d */,
+ FC7A7C14149866AF0086576A /* v.r */,
+ FC7A7C15149866AF0086576A /* v.t */,
+ FC7A7C16149866AF0086576A /* w.d */,
+ FC7A7C17149866AF0086576A /* w.r */,
+ FC7A7C18149866AF0086576A /* w.t */,
+ FC7A7C19149866AF0086576A /* w1.err */,
+ FC7A7C1A149866AF0086576A /* w2.err */,
+ FC7A7C1B149866AF0086576A /* w3.err */,
+ FC7A7C1C149866AF0086576A /* x.err */,
+ FC7A7C1D149866AF0086576A /* z.err */,
+ );
+ path = test;
+ sourceTree = "<group>";
+ };
+ FC7A7C1F149866AF0086576A /* expand */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C20149866AF0086576A /* expand.1 */,
+ FC7A7C21149866AF0086576A /* expand.c */,
+ );
+ path = expand;
+ sourceTree = "<group>";
+ };
+ FC7A7C23149866AF0086576A /* fmt */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C24149866AF0086576A /* fmt.1 */,
+ FC7A7C25149866AF0086576A /* fmt.c */,
+ );
+ path = fmt;
+ sourceTree = "<group>";
+ };
+ FC7A7C27149866AF0086576A /* fold */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C28149866AF0086576A /* fold.1 */,
+ FC7A7C29149866AF0086576A /* fold.c */,
+ );
+ path = fold;
+ sourceTree = "<group>";
+ };
+ FC7A7C2B149866AF0086576A /* head */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C2C149866AF0086576A /* head.1 */,
+ FC7A7C2D149866AF0086576A /* head.c */,
+ );
+ path = head;
+ sourceTree = "<group>";
+ };
+ FC7A7C2F149866AF0086576A /* join */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C30149866AF0086576A /* join.1 */,
+ FC7A7C31149866AF0086576A /* join.c */,
+ );
+ path = join;
+ sourceTree = "<group>";
+ };
+ FC7A7C33149866AF0086576A /* lam */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C34149866AF0086576A /* lam.1 */,
+ FC7A7C35149866AF0086576A /* lam.c */,
+ );
+ path = lam;
+ sourceTree = "<group>";
+ };
+ FC7A7C37149866AF0086576A /* look */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C38149866AF0086576A /* look.1 */,
+ FC7A7C39149866AF0086576A /* look.c */,
+ FC7A7C3B149866AF0086576A /* pathnames.h */,
+ );
+ path = look;
+ sourceTree = "<group>";
+ };
+ FC7A7C3C149866AF0086576A /* md5 */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C3D149866AF0086576A /* commoncrypto.c */,
+ FC7A7C3E149866AF0086576A /* commoncrypto.h */,
+ FC7A7C40149866AF0086576A /* md5.1 */,
+ FC7A7C41149866AF0086576A /* md5.c */,
+ );
+ path = md5;
+ sourceTree = "<group>";
+ };
+ FC7A7C42149866AF0086576A /* nl */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C44149866AF0086576A /* nl.1 */,
+ FC7A7C45149866AF0086576A /* nl.c */,
+ );
+ path = nl;
+ sourceTree = "<group>";
+ };
+ FC7A7C46149866AF0086576A /* paste */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C48149866AF0086576A /* paste.1 */,
+ FC7A7C49149866AF0086576A /* paste.c */,
+ );
+ path = paste;
+ sourceTree = "<group>";
+ };
+ FC7A7C4A149866AF0086576A /* pr */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C4B149866AF0086576A /* egetopt.c */,
+ FC7A7C4C149866AF0086576A /* extern.h */,
+ FC7A7C4E149866AF0086576A /* pr.1 */,
+ FC7A7C4F149866AF0086576A /* pr.c */,
+ FC7A7C50149866AF0086576A /* pr.h */,
+ );
+ path = pr;
+ sourceTree = "<group>";
+ };
+ FC7A7C51149866AF0086576A /* rev */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C53149866AF0086576A /* rev.1 */,
+ FC7A7C54149866AF0086576A /* rev.c */,
+ );
+ path = rev;
+ sourceTree = "<group>";
+ };
+ FC7A7C55149866AF0086576A /* rs */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C57149866AF0086576A /* rs.1 */,
+ FC7A7C58149866AF0086576A /* rs.c */,
+ );
+ path = rs;
+ sourceTree = "<group>";
+ };
+ FC7A7C59149866AF0086576A /* sed */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C5A149866AF0086576A /* compile.c */,
+ FC7A7C5B149866AF0086576A /* defs.h */,
+ FC7A7C5C149866AF0086576A /* extern.h */,
+ FC7A7C5D149866AF0086576A /* main.c */,
+ FC7A7C5F149866AF0086576A /* misc.c */,
+ FC7A7C60149866AF0086576A /* POSIX */,
+ FC7A7C61149866AF0086576A /* process.c */,
+ FC7A7C62149866AF0086576A /* sed.1 */,
+ FC7A7C63149866AF0086576A /* TEST */,
+ );
+ path = sed;
+ sourceTree = "<group>";
+ };
+ FC7A7C63149866AF0086576A /* TEST */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7C64149866AF0086576A /* hanoi.sed */,
+ FC7A7C65149866AF0086576A /* math.sed */,
+ FC7A7C66149866AF0086576A /* sed.test */,
+ );
+ path = TEST;
+ sourceTree = "<group>";
+ };
+ FC7A7C67149866AF0086576A /* sort */ = {
+ isa = PBXGroup;
+ children = (
+ FDF283571DAED0C000CF8C36 /* bwstring.c */,
+ FDF283581DAED0C000CF8C36 /* bwstring.h */,
+ FDF283591DAED0C000CF8C36 /* coll.c */,
+ FDF2835A1DAED0C000CF8C36 /* coll.h */,
+ FDF2836F1DAED7C700CF8C36 /* commoncrypto.c */,
+ FDF283701DAED7C700CF8C36 /* commoncrypto.h */,
+ FDF2835B1DAED0C000CF8C36 /* file.c */,
+ FDF2835C1DAED0C000CF8C36 /* file.h */,
+ FDF2835D1DAED0C000CF8C36 /* mem.c */,
+ FDF2835E1DAED0C000CF8C36 /* mem.h */,
+ FDF2835F1DAED0C000CF8C36 /* nls */,
+ FDF283621DAED0C000CF8C36 /* radixsort.c */,
+ FDF283631DAED0C000CF8C36 /* radixsort.h */,
+ FDF283641DAED0C000CF8C36 /* sort.1.in */,
+ FC7A7C93149866AF0086576A /* sort.c */,
+ FDF283651DAED0C000CF8C36 /* sort.h */,
+ FDF283661DAED0C000CF8C36 /* vsort.c */,
+ FDF283671DAED0C000CF8C36 /* vsort.h */,
+ );
+ path = sort;
+ sourceTree = "<group>";
+ };
+ FC7A7CB2149866AF0086576A /* split */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CB4149866AF0086576A /* split.1 */,
+ FC7A7CB5149866AF0086576A /* split.c */,
+ );
+ path = split;
+ sourceTree = "<group>";
+ };
+ FC7A7CB6149866AF0086576A /* tail */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CB7149866AF0086576A /* extern.h */,
+ FC7A7CB8149866AF0086576A /* forward.c */,
+ FC7A7CBA149866AF0086576A /* misc.c */,
+ FC7A7CBB149866AF0086576A /* read.c */,
+ FC7A7CBC149866AF0086576A /* reverse.c */,
+ FC7A7CBD149866AF0086576A /* tail.1 */,
+ FC7A7CBE149866AF0086576A /* tail.c */,
+ );
+ path = tail;
+ sourceTree = "<group>";
+ };
+ FC7A7CC0149866AF0086576A /* tr */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CC1149866AF0086576A /* cmap.c */,
+ FC7A7CC2149866AF0086576A /* cmap.h */,
+ FC7A7CC3149866AF0086576A /* cset.c */,
+ FC7A7CC4149866AF0086576A /* cset.h */,
+ FC7A7CC5149866AF0086576A /* extern.h */,
+ FC7A7CC7149866AF0086576A /* str.c */,
+ FC7A7CC8149866AF0086576A /* tr.1 */,
+ FC7A7CC9149866AF0086576A /* tr.c */,
+ );
+ path = tr;
+ sourceTree = "<group>";
+ };
+ FC7A7CCA149866AF0086576A /* ul */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CCC149866AF0086576A /* ul.1 */,
+ FC7A7CCD149866AF0086576A /* ul.c */,
+ FC6C98FB149A94EB00DDCC47 /* libcurses.dylib */,
+ );
+ path = ul;
+ sourceTree = "<group>";
+ };
+ FC7A7CCE149866AF0086576A /* unexpand */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CD0149866AF0086576A /* unexpand.c */,
+ );
+ path = unexpand;
+ sourceTree = "<group>";
+ };
+ FC7A7CD1149866AF0086576A /* uniq */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CD3149866AF0086576A /* uniq.1 */,
+ FC7A7CD4149866AF0086576A /* uniq.c */,
+ );
+ path = uniq;
+ sourceTree = "<group>";
+ };
+ FC7A7CD5149866AF0086576A /* unvis */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CD7149866AF0086576A /* unvis.1 */,
+ FC7A7CD8149866AF0086576A /* unvis.c */,
+ );
+ path = unvis;
+ sourceTree = "<group>";
+ };
+ FC7A7CD9149866AF0086576A /* vis */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CDA149866AF0086576A /* extern.h */,
+ FC7A7CDB149866AF0086576A /* foldit.c */,
+ FC7A7CDD149866AF0086576A /* vis.1 */,
+ FC7A7CDE149866AF0086576A /* vis.c */,
+ );
+ path = vis;
+ sourceTree = "<group>";
+ };
+ FC7A7CDF149866AF0086576A /* wc */ = {
+ isa = PBXGroup;
+ children = (
+ FC7A7CE1149866AF0086576A /* wc.1 */,
+ FC7A7CE2149866AF0086576A /* wc.c */,
+ );
+ path = wc;
+ sourceTree = "<group>";
+ };
+ FD2BDC151B7188240053EE6B /* xcconfigs */ = {
+ isa = PBXGroup;
+ children = (
+ FD2BDC161B7188240053EE6B /* ee.xcconfig */,
+ FD3562031B82803F008A70F6 /* grep.xcconfig */,
+ FDF2836E1DAED24800CF8C36 /* sort.xcconfig */,
+ );
+ path = xcconfigs;
+ sourceTree = "<group>";
+ };
+ FD3561F31B827EE0008A70F6 /* grep */ = {
+ isa = PBXGroup;
+ children = (
+ FD3561F81B827F9F008A70F6 /* file.c */,
+ FD3561F91B827F9F008A70F6 /* grep.1 */,
+ FD3561FA1B827F9F008A70F6 /* grep.c */,
+ FD3561FB1B827F9F008A70F6 /* grep.h */,
+ FD3561FC1B827F9F008A70F6 /* queue.c */,
+ FD3561FD1B827F9F008A70F6 /* util.c */,
+ );
+ path = grep;
+ sourceTree = "<group>";
+ };
+ FDA49B8F1B7186CE003B4F3C /* ee */ = {
+ isa = PBXGroup;
+ children = (
+ FDA49B961B718762003B4F3C /* ee.1 */,
+ FDA49B941B718738003B4F3C /* ee.c */,
+ );
+ path = ee;
+ sourceTree = "<group>";
+ };
+ FDF2835F1DAED0C000CF8C36 /* nls */ = {
+ isa = PBXGroup;
+ children = (
+ FDF283601DAED0C000CF8C36 /* C.msg */,
+ FDF283611DAED0C000CF8C36 /* hu_HU.ISO8859-2.msg */,
+ );
+ path = nls;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ 3EF506E21F7ABE4D00421EB3 /* tests */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(ACTION)";
+ buildConfigurationList = 3EF506E31F7ABE4D00421EB3 /* Build configuration list for PBXLegacyTarget "tests" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = tests;
+ dependencies = (
+ );
+ name = tests;
+ passBuildSettingsInEnvironment = 1;
+ productName = tests;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ FC7A7B38149865C80086576A /* banner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7B43149865C80086576A /* Build configuration list for PBXNativeTarget "banner" */;
+ buildPhases = (
+ FC7A7B35149865C80086576A /* Sources */,
+ FC7A7B36149865C80086576A /* Frameworks */,
+ FC7A7B37149865C80086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = banner;
+ productName = text_cmds;
+ productReference = FC7A7B39149865C80086576A /* banner */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7CE4149867ED0086576A /* cat */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7CE8149867ED0086576A /* Build configuration list for PBXNativeTarget "cat" */;
+ buildPhases = (
+ FC7A7CE5149867ED0086576A /* Sources */,
+ FC7A7CE6149867ED0086576A /* Frameworks */,
+ FC7A7CE7149867ED0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = cat;
+ productName = text_cmds;
+ productReference = FC7A7CEA149867ED0086576A /* cat */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7CEC149869E90086576A /* col */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7CF0149869E90086576A /* Build configuration list for PBXNativeTarget "col" */;
+ buildPhases = (
+ FC7A7CED149869E90086576A /* Sources */,
+ FC7A7CEE149869E90086576A /* Frameworks */,
+ FC7A7CEF149869E90086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = col;
+ productName = text_cmds;
+ productReference = FC7A7CF2149869E90086576A /* col */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7CF414986A160086576A /* colrm */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7CF814986A160086576A /* Build configuration list for PBXNativeTarget "colrm" */;
+ buildPhases = (
+ FC7A7CF514986A160086576A /* Sources */,
+ FC7A7CF614986A160086576A /* Frameworks */,
+ FC7A7CF714986A160086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = colrm;
+ productName = text_cmds;
+ productReference = FC7A7CFA14986A160086576A /* colrm */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7CFC14986A190086576A /* column */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D0014986A190086576A /* Build configuration list for PBXNativeTarget "column" */;
+ buildPhases = (
+ FC7A7CFD14986A190086576A /* Sources */,
+ FC7A7CFE14986A190086576A /* Frameworks */,
+ FC7A7CFF14986A190086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = column;
+ productName = text_cmds;
+ productReference = FC7A7D0214986A190086576A /* column */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D0414986A1B0086576A /* comm */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D0814986A1B0086576A /* Build configuration list for PBXNativeTarget "comm" */;
+ buildPhases = (
+ FC7A7D0514986A1B0086576A /* Sources */,
+ FC7A7D0614986A1B0086576A /* Frameworks */,
+ FC7A7D0714986A1B0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = comm;
+ productName = text_cmds;
+ productReference = FC7A7D0A14986A1B0086576A /* comm */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D0C14986A1E0086576A /* csplit */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D1014986A1E0086576A /* Build configuration list for PBXNativeTarget "csplit" */;
+ buildPhases = (
+ FC7A7D0D14986A1E0086576A /* Sources */,
+ FC7A7D0E14986A1E0086576A /* Frameworks */,
+ FC7A7D0F14986A1E0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = csplit;
+ productName = text_cmds;
+ productReference = FC7A7D1214986A1E0086576A /* csplit */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D1414986A210086576A /* cut */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D1814986A210086576A /* Build configuration list for PBXNativeTarget "cut" */;
+ buildPhases = (
+ FC7A7D1514986A210086576A /* Sources */,
+ FC7A7D1614986A210086576A /* Frameworks */,
+ FC7A7D1714986A210086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = cut;
+ productName = text_cmds;
+ productReference = FC7A7D1A14986A210086576A /* cut */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D1C14986A230086576A /* ed */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D2014986A230086576A /* Build configuration list for PBXNativeTarget "ed" */;
+ buildPhases = (
+ FC7A7D1D14986A230086576A /* Sources */,
+ FC7A7D1E14986A230086576A /* Frameworks */,
+ FC7A7D1F14986A230086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ed;
+ productName = text_cmds;
+ productReference = FC7A7D2214986A230086576A /* ed */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D2414986A240086576A /* expand */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D2814986A240086576A /* Build configuration list for PBXNativeTarget "expand" */;
+ buildPhases = (
+ FC7A7D2514986A240086576A /* Sources */,
+ FC7A7D2614986A240086576A /* Frameworks */,
+ FC7A7D2714986A240086576A /* CopyFiles */,
+ FC6C98FE149A99C400DDCC47 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = expand;
+ productName = text_cmds;
+ productReference = FC7A7D2A14986A240086576A /* expand */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D2C14986A280086576A /* fmt */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D3014986A280086576A /* Build configuration list for PBXNativeTarget "fmt" */;
+ buildPhases = (
+ FC7A7D2D14986A280086576A /* Sources */,
+ FC7A7D2E14986A280086576A /* Frameworks */,
+ FC7A7D2F14986A280086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = fmt;
+ productName = text_cmds;
+ productReference = FC7A7D3214986A280086576A /* fmt */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D3414986A2B0086576A /* fold */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D3814986A2B0086576A /* Build configuration list for PBXNativeTarget "fold" */;
+ buildPhases = (
+ FC7A7D3514986A2B0086576A /* Sources */,
+ FC7A7D3614986A2B0086576A /* Frameworks */,
+ FC7A7D3714986A2B0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = fold;
+ productName = text_cmds;
+ productReference = FC7A7D3A14986A2B0086576A /* fold */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D3C14986A2F0086576A /* head */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D4014986A2F0086576A /* Build configuration list for PBXNativeTarget "head" */;
+ buildPhases = (
+ FC7A7D3D14986A2F0086576A /* Sources */,
+ FC7A7D3E14986A2F0086576A /* Frameworks */,
+ FC7A7D3F14986A2F0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = head;
+ productName = text_cmds;
+ productReference = FC7A7D4214986A2F0086576A /* head */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D4414986A310086576A /* join */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D4814986A310086576A /* Build configuration list for PBXNativeTarget "join" */;
+ buildPhases = (
+ FC7A7D4514986A310086576A /* Sources */,
+ FC7A7D4614986A310086576A /* Frameworks */,
+ FC7A7D4714986A310086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = join;
+ productName = text_cmds;
+ productReference = FC7A7D4A14986A310086576A /* join */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D4C14986A340086576A /* lam */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D5014986A340086576A /* Build configuration list for PBXNativeTarget "lam" */;
+ buildPhases = (
+ FC7A7D4D14986A340086576A /* Sources */,
+ FC7A7D4E14986A340086576A /* Frameworks */,
+ FC7A7D4F14986A340086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = lam;
+ productName = text_cmds;
+ productReference = FC7A7D5214986A340086576A /* lam */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D5414986A360086576A /* look */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D5814986A360086576A /* Build configuration list for PBXNativeTarget "look" */;
+ buildPhases = (
+ FC7A7D5514986A360086576A /* Sources */,
+ FC7A7D5614986A360086576A /* Frameworks */,
+ FC7A7D5714986A360086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = look;
+ productName = text_cmds;
+ productReference = FC7A7D5A14986A360086576A /* look */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D5C14986A390086576A /* md5 */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D6014986A390086576A /* Build configuration list for PBXNativeTarget "md5" */;
+ buildPhases = (
+ FC7A7D5D14986A390086576A /* Sources */,
+ FC7A7D5E14986A390086576A /* Frameworks */,
+ FC7A7D5F14986A390086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = md5;
+ productName = text_cmds;
+ productReference = FC7A7D6214986A390086576A /* md5 */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D6414986A3C0086576A /* nl */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D6814986A3C0086576A /* Build configuration list for PBXNativeTarget "nl" */;
+ buildPhases = (
+ FC7A7D6514986A3C0086576A /* Sources */,
+ FC7A7D6614986A3C0086576A /* Frameworks */,
+ FC7A7D6714986A3C0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = nl;
+ productName = text_cmds;
+ productReference = FC7A7D6A14986A3C0086576A /* nl */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D6C14986A3D0086576A /* paste */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D7014986A3D0086576A /* Build configuration list for PBXNativeTarget "paste" */;
+ buildPhases = (
+ FC7A7D6D14986A3D0086576A /* Sources */,
+ FC7A7D6E14986A3D0086576A /* Frameworks */,
+ FC7A7D6F14986A3D0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = paste;
+ productName = text_cmds;
+ productReference = FC7A7D7214986A3D0086576A /* paste */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D7414986A400086576A /* pr */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D7814986A400086576A /* Build configuration list for PBXNativeTarget "pr" */;
+ buildPhases = (
+ FC7A7D7514986A400086576A /* Sources */,
+ FC7A7D7614986A400086576A /* Frameworks */,
+ FC7A7D7714986A400086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = pr;
+ productName = text_cmds;
+ productReference = FC7A7D7A14986A400086576A /* pr */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D7C14986A410086576A /* rev */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D8014986A410086576A /* Build configuration list for PBXNativeTarget "rev" */;
+ buildPhases = (
+ FC7A7D7D14986A410086576A /* Sources */,
+ FC7A7D7E14986A410086576A /* Frameworks */,
+ FC7A7D7F14986A410086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = rev;
+ productName = text_cmds;
+ productReference = FC7A7D8214986A410086576A /* rev */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D8414986A430086576A /* rs */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D8814986A430086576A /* Build configuration list for PBXNativeTarget "rs" */;
+ buildPhases = (
+ FC7A7D8514986A430086576A /* Sources */,
+ FC7A7D8614986A430086576A /* Frameworks */,
+ FC7A7D8714986A430086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = rs;
+ productName = text_cmds;
+ productReference = FC7A7D8A14986A430086576A /* rs */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D8C14986A450086576A /* sed */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D9014986A450086576A /* Build configuration list for PBXNativeTarget "sed" */;
+ buildPhases = (
+ FC7A7D8D14986A450086576A /* Sources */,
+ FC7A7D8E14986A450086576A /* Frameworks */,
+ FC7A7D8F14986A450086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = sed;
+ productName = text_cmds;
+ productReference = FC7A7D9214986A450086576A /* sed */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D9414986A470086576A /* sort */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7D9814986A470086576A /* Build configuration list for PBXNativeTarget "sort" */;
+ buildPhases = (
+ FC7A7D9514986A470086576A /* Sources */,
+ FC7A7D9614986A470086576A /* Frameworks */,
+ FDF283721DAEE22200CF8C36 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = sort;
+ productName = text_cmds;
+ productReference = FC7A7D9A14986A470086576A /* sort */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7D9C14986A480086576A /* split */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DA014986A480086576A /* Build configuration list for PBXNativeTarget "split" */;
+ buildPhases = (
+ FC7A7D9D14986A480086576A /* Sources */,
+ FC7A7D9E14986A480086576A /* Frameworks */,
+ FC7A7D9F14986A480086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = split;
+ productName = text_cmds;
+ productReference = FC7A7DA214986A480086576A /* split */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DA414986A4A0086576A /* tail */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DA814986A4A0086576A /* Build configuration list for PBXNativeTarget "tail" */;
+ buildPhases = (
+ FC7A7DA514986A4A0086576A /* Sources */,
+ FC7A7DA614986A4A0086576A /* Frameworks */,
+ FC7A7DA714986A4A0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = tail;
+ productName = text_cmds;
+ productReference = FC7A7DAA14986A4A0086576A /* tail */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DAC14986A500086576A /* tr */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DB014986A500086576A /* Build configuration list for PBXNativeTarget "tr" */;
+ buildPhases = (
+ FC7A7DAD14986A500086576A /* Sources */,
+ FC7A7DAE14986A500086576A /* Frameworks */,
+ FC7A7DAF14986A500086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = tr;
+ productName = text_cmds;
+ productReference = FC7A7DB214986A500086576A /* tr */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DB414986A530086576A /* ul */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DB814986A530086576A /* Build configuration list for PBXNativeTarget "ul" */;
+ buildPhases = (
+ FC7A7DB514986A530086576A /* Sources */,
+ FC7A7DB614986A530086576A /* Frameworks */,
+ FC7A7DB714986A530086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ul;
+ productName = text_cmds;
+ productReference = FC7A7DBA14986A530086576A /* ul */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DBC14986A540086576A /* unexpand */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DC014986A540086576A /* Build configuration list for PBXNativeTarget "unexpand" */;
+ buildPhases = (
+ FC7A7DBD14986A540086576A /* Sources */,
+ FC7A7DBE14986A540086576A /* Frameworks */,
+ FC7A7DBF14986A540086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = unexpand;
+ productName = text_cmds;
+ productReference = FC7A7DC214986A540086576A /* unexpand */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DC414986A560086576A /* uniq */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DC814986A560086576A /* Build configuration list for PBXNativeTarget "uniq" */;
+ buildPhases = (
+ FC7A7DC514986A560086576A /* Sources */,
+ FC7A7DC614986A560086576A /* Frameworks */,
+ FC7A7DC714986A560086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = uniq;
+ productName = text_cmds;
+ productReference = FC7A7DCA14986A560086576A /* uniq */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DCC14986A580086576A /* unvis */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DD014986A580086576A /* Build configuration list for PBXNativeTarget "unvis" */;
+ buildPhases = (
+ FC7A7DCD14986A580086576A /* Sources */,
+ FC7A7DCE14986A580086576A /* Frameworks */,
+ FC7A7DCF14986A580086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = unvis;
+ productName = text_cmds;
+ productReference = FC7A7DD214986A580086576A /* unvis */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DD414986A5A0086576A /* vis */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DD814986A5A0086576A /* Build configuration list for PBXNativeTarget "vis" */;
+ buildPhases = (
+ FC7A7DD514986A5A0086576A /* Sources */,
+ FC7A7DD614986A5A0086576A /* Frameworks */,
+ FC7A7DD714986A5A0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = vis;
+ productName = text_cmds;
+ productReference = FC7A7DDA14986A5A0086576A /* vis */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC7A7DDC14986A5C0086576A /* wc */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC7A7DE014986A5C0086576A /* Build configuration list for PBXNativeTarget "wc" */;
+ buildPhases = (
+ FC7A7DDD14986A5C0086576A /* Sources */,
+ FC7A7DDE14986A5C0086576A /* Frameworks */,
+ FC7A7DDF14986A5C0086576A /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = wc;
+ productName = text_cmds;
+ productReference = FC7A7DE214986A5C0086576A /* wc */;
+ productType = "com.apple.product-type.tool";
+ };
+ FD3561F11B827EE0008A70F6 /* grep */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FD3561F71B827EE0008A70F6 /* Build configuration list for PBXNativeTarget "grep" */;
+ buildPhases = (
+ FD3561EE1B827EE0008A70F6 /* Sources */,
+ FD3561EF1B827EE0008A70F6 /* Frameworks */,
+ FD3561F01B827EE0008A70F6 /* Install Man Page */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = grep;
+ productName = grep;
+ productReference = FD3561F21B827EE0008A70F6 /* grep */;
+ productType = "com.apple.product-type.tool";
+ };
+ FDA49B8D1B7186CE003B4F3C /* ee */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FDA49B921B7186CE003B4F3C /* Build configuration list for PBXNativeTarget "ee" */;
+ buildPhases = (
+ FDA49B8A1B7186CE003B4F3C /* Sources */,
+ FDA49B8B1B7186CE003B4F3C /* Frameworks */,
+ FDA49B8C1B7186CE003B4F3C /* Copy Files */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ee;
+ productName = ee;
+ productReference = FDA49B8E1B7186CE003B4F3C /* ee */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ FC7A7B30149865C80086576A /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0420;
+ ORGANIZATIONNAME = "Apple Inc.";
+ TargetAttributes = {
+ 3EF506E21F7ABE4D00421EB3 = {
+ CreatedOnToolsVersion = 9.0;
+ ProvisioningStyle = Automatic;
+ };
+ FD3561F11B827EE0008A70F6 = {
+ CreatedOnToolsVersion = 7.0;
+ };
+ FDA49B8D1B7186CE003B4F3C = {
+ CreatedOnToolsVersion = 7.0;
+ };
+ };
+ };
+ buildConfigurationList = FC7A7B33149865C80086576A /* Build configuration list for PBXProject "text_cmds" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = FC7A7B2E149865C70086576A;
+ productRefGroup = FC7A7B3A149865C80086576A /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ FC7A7E91149875C30086576A /* executables */,
+ FC7A7B38149865C80086576A /* banner */,
+ FC7A7CE4149867ED0086576A /* cat */,
+ FC7A7CEC149869E90086576A /* col */,
+ FC7A7CF414986A160086576A /* colrm */,
+ FC7A7CFC14986A190086576A /* column */,
+ FC7A7D0414986A1B0086576A /* comm */,
+ FC7A7D0C14986A1E0086576A /* csplit */,
+ FC7A7D1414986A210086576A /* cut */,
+ FC7A7D1C14986A230086576A /* ed */,
+ FDA49B8D1B7186CE003B4F3C /* ee */,
+ FC7A7D2414986A240086576A /* expand */,
+ FC7A7D2C14986A280086576A /* fmt */,
+ FC7A7D3414986A2B0086576A /* fold */,
+ FD3561F11B827EE0008A70F6 /* grep */,
+ FC7A7D3C14986A2F0086576A /* head */,
+ FC7A7D4414986A310086576A /* join */,
+ FC7A7D4C14986A340086576A /* lam */,
+ FC7A7D5414986A360086576A /* look */,
+ FC7A7D5C14986A390086576A /* md5 */,
+ FC7A7D6414986A3C0086576A /* nl */,
+ FC7A7D6C14986A3D0086576A /* paste */,
+ FC7A7D7414986A400086576A /* pr */,
+ FC7A7D7C14986A410086576A /* rev */,
+ FC7A7D8414986A430086576A /* rs */,
+ FC7A7D8C14986A450086576A /* sed */,
+ FC7A7D9414986A470086576A /* sort */,
+ FC7A7D9C14986A480086576A /* split */,
+ FC7A7DA414986A4A0086576A /* tail */,
+ FC7A7DAC14986A500086576A /* tr */,
+ FC7A7DB414986A530086576A /* ul */,
+ FC7A7DBC14986A540086576A /* unexpand */,
+ FC7A7DC414986A560086576A /* uniq */,
+ FC7A7DCC14986A580086576A /* unvis */,
+ FC7A7DD414986A5A0086576A /* vis */,
+ FC7A7DDC14986A5C0086576A /* wc */,
+ 3EF506E21F7ABE4D00421EB3 /* tests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ FC6C98FD149A989300DDCC47 /* Install open source info */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Install open source info";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-opensource.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC6C98FE149A99C400DDCC47 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/expand/xcodescripts/link-man-pages.sh";
+ showEnvVarsInLog = 0;
+ };
+ FD3562041B828154008A70F6 /* Install grep variant links */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Install grep variant links";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/grep_variant_links.sh";
+ showEnvVarsInLog = 0;
+ };
+ FDF283721DAEE22200CF8C36 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". ${SRCROOT}/xcodescripts/install-sort-man.sh";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ FC7A7B35149865C80086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DE514986CD60086576A /* banner.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CE5149867ED0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DE614986CDB0086576A /* cat.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CED149869E90086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DE714986CE00086576A /* col.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CF514986A160086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DE814986CE40086576A /* colrm.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7CFD14986A190086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DE914986CE90086576A /* column.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D0514986A1B0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DEA14986CEE0086576A /* comm.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D0D14986A1E0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DEB14986D0C0086576A /* csplit.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D1514986A210086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DEC14986D120086576A /* cut.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D1D14986A230086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DED14986D690086576A /* buf.c in Sources */,
+ FC7A7DEE14986D690086576A /* cbc.c in Sources */,
+ FC7A7DF014986D690086576A /* glbl.c in Sources */,
+ FC7A7DF114986D690086576A /* io.c in Sources */,
+ FC7A7DF214986D690086576A /* main.c in Sources */,
+ FC7A7DF314986D690086576A /* re.c in Sources */,
+ FC7A7DF414986D690086576A /* sub.c in Sources */,
+ FC7A7DF514986D690086576A /* undo.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D2514986A240086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DF614986D760086576A /* expand.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D2D14986A280086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DF714986D800086576A /* fmt.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D3514986A2B0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DF814986D8B0086576A /* fold.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D3D14986A2F0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DF914986D920086576A /* head.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D4514986A310086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DFA14986DA10086576A /* join.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D4D14986A340086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DFB14986DAB0086576A /* lam.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D5514986A360086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DFC14986DBF0086576A /* look.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D5D14986A390086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7DFE14986DC80086576A /* commoncrypto.c in Sources */,
+ FC7A7E0014986DC80086576A /* md5.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D6514986A3C0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0114986DD90086576A /* nl.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D6D14986A3D0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0214986DE40086576A /* paste.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D7514986A400086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0314986DF60086576A /* egetopt.c in Sources */,
+ FC7A7E0514986DF60086576A /* pr.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D7D14986A410086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0714986E040086576A /* rev.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D8514986A430086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0814986E0E0086576A /* rs.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D8D14986A450086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E0914986E1B0086576A /* compile.c in Sources */,
+ FC7A7E0C14986E1B0086576A /* main.c in Sources */,
+ FC7A7E0D14986E1B0086576A /* misc.c in Sources */,
+ FC7A7E0E14986E1B0086576A /* process.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D9514986A470086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FDF283681DAED0C000CF8C36 /* bwstring.c in Sources */,
+ FDF283691DAED0C000CF8C36 /* coll.c in Sources */,
+ FDF283711DAED7C700CF8C36 /* commoncrypto.c in Sources */,
+ FDF2836A1DAED0C000CF8C36 /* file.c in Sources */,
+ FDF2836B1DAED0C000CF8C36 /* mem.c in Sources */,
+ FDF2836C1DAED0C000CF8C36 /* radixsort.c in Sources */,
+ FC7A7E3714986E580086576A /* sort.c in Sources */,
+ FDF2836D1DAED0C000CF8C36 /* vsort.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7D9D14986A480086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E5614986E630086576A /* split.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DA514986A4A0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E5814986E750086576A /* forward.c in Sources */,
+ FC7A7E5914986E750086576A /* misc.c in Sources */,
+ FC7A7E5A14986E750086576A /* read.c in Sources */,
+ FC7A7E5B14986E750086576A /* reverse.c in Sources */,
+ FC7A7E5C14986E750086576A /* tail.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DAD14986A500086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E5D14986E880086576A /* cmap.c in Sources */,
+ FC7A7E5F14986E880086576A /* cset.c in Sources */,
+ FC7A7E6214986E880086576A /* str.c in Sources */,
+ FC7A7E6314986E880086576A /* tr.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DB514986A530086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6414986E920086576A /* ul.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DBD14986A540086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6514986E9B0086576A /* unexpand.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DC514986A560086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6614986EA40086576A /* uniq.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DCD14986A580086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6714986EAC0086576A /* unvis.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DD514986A5A0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6914986EB90086576A /* foldit.c in Sources */,
+ FC7A7E6A14986EB90086576A /* vis.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC7A7DDD14986A5C0086576A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC7A7E6B14986EC10086576A /* wc.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FD3561EE1B827EE0008A70F6 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FD3561FE1B827F9F008A70F6 /* file.c in Sources */,
+ FD3561FF1B827F9F008A70F6 /* grep.c in Sources */,
+ FD3562001B827F9F008A70F6 /* queue.c in Sources */,
+ FD3562011B827F9F008A70F6 /* util.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FDA49B8A1B7186CE003B4F3C /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FDA49B951B718738003B4F3C /* ee.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ FC7A7E95149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7B38149865C80086576A /* banner */;
+ targetProxy = FC7A7E94149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7E97149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7CE4149867ED0086576A /* cat */;
+ targetProxy = FC7A7E96149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7E99149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7CEC149869E90086576A /* col */;
+ targetProxy = FC7A7E98149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7E9B149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7CF414986A160086576A /* colrm */;
+ targetProxy = FC7A7E9A149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7E9D149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7CFC14986A190086576A /* column */;
+ targetProxy = FC7A7E9C149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7E9F149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D0414986A1B0086576A /* comm */;
+ targetProxy = FC7A7E9E149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EA1149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D0C14986A1E0086576A /* csplit */;
+ targetProxy = FC7A7EA0149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EA3149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D1414986A210086576A /* cut */;
+ targetProxy = FC7A7EA2149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EA5149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D1C14986A230086576A /* ed */;
+ targetProxy = FC7A7EA4149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EA7149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D2414986A240086576A /* expand */;
+ targetProxy = FC7A7EA6149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EA9149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D2C14986A280086576A /* fmt */;
+ targetProxy = FC7A7EA8149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EAB149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D3414986A2B0086576A /* fold */;
+ targetProxy = FC7A7EAA149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EAD149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D3C14986A2F0086576A /* head */;
+ targetProxy = FC7A7EAC149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EAF149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D4414986A310086576A /* join */;
+ targetProxy = FC7A7EAE149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EB1149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D4C14986A340086576A /* lam */;
+ targetProxy = FC7A7EB0149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EB3149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D5414986A360086576A /* look */;
+ targetProxy = FC7A7EB2149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EB5149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D5C14986A390086576A /* md5 */;
+ targetProxy = FC7A7EB4149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EB7149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D6414986A3C0086576A /* nl */;
+ targetProxy = FC7A7EB6149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EB9149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D6C14986A3D0086576A /* paste */;
+ targetProxy = FC7A7EB8149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EBB149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D7414986A400086576A /* pr */;
+ targetProxy = FC7A7EBA149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EBD149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D7C14986A410086576A /* rev */;
+ targetProxy = FC7A7EBC149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EBF149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D8414986A430086576A /* rs */;
+ targetProxy = FC7A7EBE149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EC1149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D8C14986A450086576A /* sed */;
+ targetProxy = FC7A7EC0149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EC3149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D9414986A470086576A /* sort */;
+ targetProxy = FC7A7EC2149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EC5149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7D9C14986A480086576A /* split */;
+ targetProxy = FC7A7EC4149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EC7149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DA414986A4A0086576A /* tail */;
+ targetProxy = FC7A7EC6149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7EC9149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DAC14986A500086576A /* tr */;
+ targetProxy = FC7A7EC8149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ECB149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DB414986A530086576A /* ul */;
+ targetProxy = FC7A7ECA149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ECD149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DBC14986A540086576A /* unexpand */;
+ targetProxy = FC7A7ECC149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ECF149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DC414986A560086576A /* uniq */;
+ targetProxy = FC7A7ECE149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ED1149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DCC14986A580086576A /* unvis */;
+ targetProxy = FC7A7ED0149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ED3149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DD414986A5A0086576A /* vis */;
+ targetProxy = FC7A7ED2149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FC7A7ED5149875E00086576A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC7A7DDC14986A5C0086576A /* wc */;
+ targetProxy = FC7A7ED4149875E00086576A /* PBXContainerItemProxy */;
+ };
+ FD2BDC181B718D310053EE6B /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FDA49B8D1B7186CE003B4F3C /* ee */;
+ targetProxy = FD2BDC171B718D310053EE6B /* PBXContainerItemProxy */;
+ };
+ FD3562061B8281A6008A70F6 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FD3561F11B827EE0008A70F6 /* grep */;
+ targetProxy = FD3562051B8281A6008A70F6 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 3EF506E41F7ABE4D00421EB3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_STYLE = Automatic;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7B42149865C80086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = "__FBSDID=__RCSID";
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = /usr/bin;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = __;
+ };
+ name = Release;
+ };
+ FC7A7B45149865C80086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7CE9149867ED0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7CF1149869E90086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7CF914986A160086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D0114986A190086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D0914986A1B0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D1114986A1E0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D1914986A210086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D2114986A230086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ INSTALL_PATH = /bin;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D2914986A240086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D3114986A280086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D3914986A2B0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D4114986A2F0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D4914986A310086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D5114986A340086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D5914986A360086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D6114986A390086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /sbin;
+ "OTHER_LDFLAGS[sdk=macosx*]" = "-lCrashReporterClient";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Release;
+ };
+ FC7A7D6914986A3C0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D7114986A3D0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D7914986A400086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D8114986A410086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D8914986A430086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D9114986A450086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7D9914986A470086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FDF2836E1DAED24800CF8C36 /* sort.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC7A7DA114986A480086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DA914986A4A0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DB114986A500086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DB914986A530086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DC114986A540086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DC914986A560086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DD114986A580086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DD914986A5A0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7DE114986A5C0086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC7A7E93149875C30086576A /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FD3561F61B827EE0008A70F6 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FD3562031B82803F008A70F6 /* grep.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FDA49B931B7186CE003B4F3C /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FD2BDC161B7188240053EE6B /* ee.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 3EF506E31F7ABE4D00421EB3 /* Build configuration list for PBXLegacyTarget "tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3EF506E41F7ABE4D00421EB3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7B33149865C80086576A /* Build configuration list for PBXProject "text_cmds" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7B42149865C80086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7B43149865C80086576A /* Build configuration list for PBXNativeTarget "banner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7B45149865C80086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7CE8149867ED0086576A /* Build configuration list for PBXNativeTarget "cat" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7CE9149867ED0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7CF0149869E90086576A /* Build configuration list for PBXNativeTarget "col" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7CF1149869E90086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7CF814986A160086576A /* Build configuration list for PBXNativeTarget "colrm" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7CF914986A160086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D0014986A190086576A /* Build configuration list for PBXNativeTarget "column" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D0114986A190086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D0814986A1B0086576A /* Build configuration list for PBXNativeTarget "comm" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D0914986A1B0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D1014986A1E0086576A /* Build configuration list for PBXNativeTarget "csplit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D1114986A1E0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D1814986A210086576A /* Build configuration list for PBXNativeTarget "cut" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D1914986A210086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D2014986A230086576A /* Build configuration list for PBXNativeTarget "ed" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D2114986A230086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D2814986A240086576A /* Build configuration list for PBXNativeTarget "expand" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D2914986A240086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D3014986A280086576A /* Build configuration list for PBXNativeTarget "fmt" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D3114986A280086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D3814986A2B0086576A /* Build configuration list for PBXNativeTarget "fold" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D3914986A2B0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D4014986A2F0086576A /* Build configuration list for PBXNativeTarget "head" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D4114986A2F0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D4814986A310086576A /* Build configuration list for PBXNativeTarget "join" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D4914986A310086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D5014986A340086576A /* Build configuration list for PBXNativeTarget "lam" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D5114986A340086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D5814986A360086576A /* Build configuration list for PBXNativeTarget "look" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D5914986A360086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D6014986A390086576A /* Build configuration list for PBXNativeTarget "md5" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D6114986A390086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D6814986A3C0086576A /* Build configuration list for PBXNativeTarget "nl" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D6914986A3C0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D7014986A3D0086576A /* Build configuration list for PBXNativeTarget "paste" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D7114986A3D0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D7814986A400086576A /* Build configuration list for PBXNativeTarget "pr" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D7914986A400086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D8014986A410086576A /* Build configuration list for PBXNativeTarget "rev" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D8114986A410086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D8814986A430086576A /* Build configuration list for PBXNativeTarget "rs" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D8914986A430086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D9014986A450086576A /* Build configuration list for PBXNativeTarget "sed" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D9114986A450086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7D9814986A470086576A /* Build configuration list for PBXNativeTarget "sort" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7D9914986A470086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DA014986A480086576A /* Build configuration list for PBXNativeTarget "split" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DA114986A480086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DA814986A4A0086576A /* Build configuration list for PBXNativeTarget "tail" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DA914986A4A0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DB014986A500086576A /* Build configuration list for PBXNativeTarget "tr" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DB114986A500086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DB814986A530086576A /* Build configuration list for PBXNativeTarget "ul" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DB914986A530086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DC014986A540086576A /* Build configuration list for PBXNativeTarget "unexpand" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DC114986A540086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DC814986A560086576A /* Build configuration list for PBXNativeTarget "uniq" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DC914986A560086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DD014986A580086576A /* Build configuration list for PBXNativeTarget "unvis" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DD114986A580086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DD814986A5A0086576A /* Build configuration list for PBXNativeTarget "vis" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DD914986A5A0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7DE014986A5C0086576A /* Build configuration list for PBXNativeTarget "wc" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7DE114986A5C0086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC7A7E92149875C30086576A /* Build configuration list for PBXAggregateTarget "executables" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC7A7E93149875C30086576A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FD3561F71B827EE0008A70F6 /* Build configuration list for PBXNativeTarget "grep" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FD3561F61B827EE0008A70F6 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FDA49B921B7186CE003B4F3C /* Build configuration list for PBXNativeTarget "ee" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FDA49B931B7186CE003B4F3C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = FC7A7B30149865C80086576A /* Project object */;
+}
diff --git a/text_cmds/text_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/text_cmds/text_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/text_cmds/text_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/text_cmds/tr/cmap.c b/text_cmds/tr/cmap.c
new file mode 100644
index 0000000..1edd9d2
--- /dev/null
+++ b/text_cmds/tr/cmap.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * "Character map" ADT. Stores mappings between pairs of characters in a
+ * splay tree, with a lookup table cache to simplify looking up the first
+ * bunch of characters (which are presumably more common than others).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/tr/cmap.c,v 1.2 2004/07/14 08:36:09 tjr Exp $");
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include "cmap.h"
+
+static struct cmapnode *cmap_splay(struct cmapnode *, wint_t);
+
+/*
+ * cmap_alloc --
+ * Allocate a character map.
+ */
+struct cmap *
+cmap_alloc(void)
+{
+ struct cmap *cm;
+
+ cm = malloc(sizeof(*cm));
+ if (cm == NULL)
+ return (NULL);
+ cm->cm_root = NULL;
+ cm->cm_def = CM_DEF_SELF;
+ cm->cm_havecache = false;
+ cm->cm_min = cm->cm_max = 0;
+ return (cm);
+}
+
+/*
+ * cmap_add --
+ * Add a mapping from "from" to "to" to the map.
+ */
+bool
+cmap_add(struct cmap *cm, wint_t from, wint_t to)
+{
+ struct cmapnode *cmn, *ncmn;
+
+ cm->cm_havecache = false;
+
+ if (cm->cm_root == NULL) {
+ cmn = malloc(sizeof(*cmn));
+ if (cmn == NULL)
+ return (false);
+ cmn->cmn_from = from;
+ cmn->cmn_to = to;
+ cmn->cmn_left = cmn->cmn_right = NULL;
+ cm->cm_root = cmn;
+ cm->cm_min = cm->cm_max = from;
+ return (true);
+ }
+
+ cmn = cm->cm_root = cmap_splay(cm->cm_root, from);
+
+ if (cmn->cmn_from == from) {
+ cmn->cmn_to = to;
+ return (true);
+ }
+
+ ncmn = malloc(sizeof(*ncmn));
+ if (ncmn == NULL)
+ return (false);
+ ncmn->cmn_from = from;
+ ncmn->cmn_to = to;
+ if (from < cmn->cmn_from) {
+ ncmn->cmn_left = cmn->cmn_left;
+ ncmn->cmn_right = cmn;
+ cmn->cmn_left = NULL;
+ } else {
+ ncmn->cmn_right = cmn->cmn_right;
+ ncmn->cmn_left = cmn;
+ cmn->cmn_right = NULL;
+ }
+ if (from < cm->cm_min)
+ cm->cm_min = from;
+ if (from > cm->cm_max)
+ cm->cm_max = from;
+ cm->cm_root = ncmn;
+
+ return (true);
+}
+
+/*
+ * cmap_lookup_hard --
+ * Look up the mapping for a character without using the cache.
+ */
+wint_t
+cmap_lookup_hard(struct cmap *cm, wint_t ch)
+{
+
+ if (cm->cm_root != NULL) {
+ cm->cm_root = cmap_splay(cm->cm_root, ch);
+ if (cm->cm_root->cmn_from == ch)
+ return (cm->cm_root->cmn_to);
+ }
+ return (cm->cm_def == CM_DEF_SELF ? ch : cm->cm_def);
+}
+
+/*
+ * cmap_cache --
+ * Update the cache.
+ */
+void
+cmap_cache(struct cmap *cm)
+{
+ wint_t ch;
+
+ for (ch = 0; ch < CM_CACHE_SIZE; ch++)
+ cm->cm_cache[ch] = cmap_lookup_hard(cm, ch);
+
+ cm->cm_havecache = true;
+}
+
+/*
+ * cmap_default --
+ * Change the value that characters without mappings map to, and
+ * return the old value. The special character value CM_MAP_SELF
+ * means characters map to themselves.
+ */
+wint_t
+cmap_default(struct cmap *cm, wint_t def)
+{
+ wint_t old;
+
+ old = cm->cm_def;
+ cm->cm_def = def;
+ cm->cm_havecache = false;
+ return (old);
+}
+
+static struct cmapnode *
+cmap_splay(struct cmapnode *t, wint_t ch)
+{
+ struct cmapnode N, *l, *r, *y;
+
+ /*
+ * Based on public domain code from Sleator.
+ */
+
+ assert(t != NULL);
+
+ N.cmn_left = N.cmn_right = NULL;
+ l = r = &N;
+ for (;;) {
+ if (ch < t->cmn_from) {
+ if (t->cmn_left != NULL &&
+ ch < t->cmn_left->cmn_from) {
+ y = t->cmn_left;
+ t->cmn_left = y->cmn_right;
+ y->cmn_right = t;
+ t = y;
+ }
+ if (t->cmn_left == NULL)
+ break;
+ r->cmn_left = t;
+ r = t;
+ t = t->cmn_left;
+ } else if (ch > t->cmn_from) {
+ if (t->cmn_right != NULL &&
+ ch > t->cmn_right->cmn_from) {
+ y = t->cmn_right;
+ t->cmn_right = y->cmn_left;
+ y->cmn_left = t;
+ t = y;
+ }
+ if (t->cmn_right == NULL)
+ break;
+ l->cmn_right = t;
+ l = t;
+ t = t->cmn_right;
+ } else
+ break;
+ }
+ l->cmn_right = t->cmn_left;
+ r->cmn_left = t->cmn_right;
+ t->cmn_left = N.cmn_right;
+ t->cmn_right = N.cmn_left;
+ return (t);
+}
diff --git a/text_cmds/tr/cmap.h b/text_cmds/tr/cmap.h
new file mode 100644
index 0000000..38e3d0e
--- /dev/null
+++ b/text_cmds/tr/cmap.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.bin/tr/cmap.h,v 1.1 2004/07/09 02:08:07 tjr Exp $
+ */
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <wchar.h>
+
+struct cmapnode {
+ wint_t cmn_from;
+ wint_t cmn_to;
+ struct cmapnode *cmn_left;
+ struct cmapnode *cmn_right;
+};
+
+struct cmap {
+#define CM_CACHE_SIZE 128
+ wint_t cm_cache[CM_CACHE_SIZE];
+ bool cm_havecache;
+ struct cmapnode *cm_root;
+#define CM_DEF_SELF -2
+ wint_t cm_def;
+ wint_t cm_min;
+ wint_t cm_max;
+};
+
+struct cmap * cmap_alloc(void);
+bool cmap_add(struct cmap *, wint_t, wint_t);
+wint_t cmap_lookup_hard(struct cmap *, wint_t);
+void cmap_cache(struct cmap *);
+wint_t cmap_default(struct cmap *, wint_t);
+
+static __inline wint_t
+cmap_lookup(struct cmap *cm, wint_t from)
+{
+
+ if (from < CM_CACHE_SIZE && cm->cm_havecache)
+ return (cm->cm_cache[from]);
+ return (cmap_lookup_hard(cm, from));
+}
+
+static __inline wint_t
+cmap_min(struct cmap *cm)
+{
+
+ return (cm->cm_min);
+}
+
+static __inline wint_t
+cmap_max(struct cmap *cm)
+{
+
+ return (cm->cm_max);
+}
+
+#endif
diff --git a/text_cmds/tr/cset.c b/text_cmds/tr/cset.c
new file mode 100644
index 0000000..6e7c217
--- /dev/null
+++ b/text_cmds/tr/cset.c
@@ -0,0 +1,290 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * "Set of characters" ADT implemented as a splay tree of extents, with
+ * a lookup table cache to simplify looking up the first bunch of
+ * characters (which are presumably more common than others).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/tr/cset.c,v 1.3 2004/07/14 08:33:14 tjr Exp $");
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+#include "cset.h"
+
+static struct csnode * cset_delete(struct csnode *, wchar_t);
+static __inline int cset_rangecmp(struct csnode *, wchar_t);
+static struct csnode * cset_splay(struct csnode *, wchar_t);
+
+/*
+ * cset_alloc --
+ * Allocate a set of characters.
+ */
+struct cset *
+cset_alloc(void)
+{
+ struct cset *cs;
+
+ if ((cs = malloc(sizeof(*cs))) == NULL)
+ return (NULL);
+ cs->cs_root = NULL;
+ cs->cs_classes = NULL;
+ cs->cs_havecache = false;
+ cs->cs_invert = false;
+ return (cs);
+}
+
+/*
+ * cset_add --
+ * Add a character to the set.
+ */
+bool
+cset_add(struct cset *cs, wchar_t ch)
+{
+ struct csnode *csn, *ncsn;
+ wchar_t oval;
+
+ cs->cs_havecache = false;
+
+ /*
+ * Inserting into empty tree; new item becomes the root.
+ */
+ if (cs->cs_root == NULL) {
+ csn = malloc(sizeof(*cs->cs_root));
+ if (csn == NULL)
+ return (false);
+ csn->csn_left = csn->csn_right = NULL;
+ csn->csn_min = csn->csn_max = ch;
+ cs->cs_root = csn;
+ return (true);
+ }
+
+ /*
+ * Splay to check whether the item already exists, and otherwise,
+ * where we should put it.
+ */
+ csn = cs->cs_root = cset_splay(cs->cs_root, ch);
+
+ /*
+ * Avoid adding duplicate nodes.
+ */
+ if (cset_rangecmp(csn, ch) == 0)
+ return (true);
+
+ /*
+ * Allocate a new node and make it the new root.
+ */
+ ncsn = malloc(sizeof(*ncsn));
+ if (ncsn == NULL)
+ return (false);
+ ncsn->csn_min = ncsn->csn_max = ch;
+ if (cset_rangecmp(csn, ch) < 0) {
+ ncsn->csn_left = csn->csn_left;
+ ncsn->csn_right = csn;
+ csn->csn_left = NULL;
+ } else {
+ ncsn->csn_right = csn->csn_right;
+ ncsn->csn_left = csn;
+ csn->csn_right = NULL;
+ }
+ cs->cs_root = ncsn;
+
+ /*
+ * Coalesce with left and right neighbours if possible.
+ */
+ if (ncsn->csn_left != NULL) {
+ ncsn->csn_left = cset_splay(ncsn->csn_left, ncsn->csn_min - 1);
+ if (ncsn->csn_left->csn_max == ncsn->csn_min - 1) {
+ oval = ncsn->csn_left->csn_min;
+ ncsn->csn_left = cset_delete(ncsn->csn_left,
+ ncsn->csn_left->csn_min);
+ ncsn->csn_min = oval;
+ }
+ }
+ if (ncsn->csn_right != NULL) {
+ ncsn->csn_right = cset_splay(ncsn->csn_right,
+ ncsn->csn_max + 1);
+ if (ncsn->csn_right->csn_min == ncsn->csn_max + 1) {
+ oval = ncsn->csn_right->csn_max;
+ ncsn->csn_right = cset_delete(ncsn->csn_right,
+ ncsn->csn_right->csn_min);
+ ncsn->csn_max = oval;
+ }
+ }
+
+ return (true);
+}
+
+/*
+ * cset_in_hard --
+ * Determine whether a character is in the set without using
+ * the cache.
+ */
+bool
+cset_in_hard(struct cset *cs, wchar_t ch)
+{
+ struct csclass *csc;
+
+ for (csc = cs->cs_classes; csc != NULL; csc = csc->csc_next)
+ if (csc->csc_invert ^ iswctype(ch, csc->csc_type) != 0)
+ return (cs->cs_invert ^ true);
+ if (cs->cs_root != NULL) {
+ cs->cs_root = cset_splay(cs->cs_root, ch);
+ return (cs->cs_invert ^ cset_rangecmp(cs->cs_root, ch) == 0);
+ }
+ return (cs->cs_invert ^ false);
+}
+
+/*
+ * cset_cache --
+ * Update the cache.
+ */
+void
+cset_cache(struct cset *cs)
+{
+ wchar_t i;
+
+ for (i = 0; i < CS_CACHE_SIZE; i++)
+ cs->cs_cache[i] = cset_in_hard(cs, i);
+
+ cs->cs_havecache = true;
+}
+
+/*
+ * cset_invert --
+ * Invert the character set.
+ */
+void
+cset_invert(struct cset *cs)
+{
+
+ cs->cs_invert ^= true;
+ cs->cs_havecache = false;
+}
+
+/*
+ * cset_addclass --
+ * Add a wctype()-style character class to the set, optionally
+ * inverting it.
+ */
+bool
+cset_addclass(struct cset *cs, wctype_t type, bool invert)
+{
+ struct csclass *csc;
+
+ csc = malloc(sizeof(*csc));
+ if (csc == NULL)
+ return (false);
+ csc->csc_type = type;
+ csc->csc_invert = invert;
+ csc->csc_next = cs->cs_classes;
+ cs->cs_classes = csc;
+ cs->cs_havecache = false;
+ return (true);
+}
+
+static __inline int
+cset_rangecmp(struct csnode *t, wchar_t ch)
+{
+
+ if (ch < t->csn_min)
+ return (-1);
+ if (ch > t->csn_max)
+ return (1);
+ return (0);
+}
+
+static struct csnode *
+cset_splay(struct csnode *t, wchar_t ch)
+{
+ struct csnode N, *l, *r, *y;
+
+ /*
+ * Based on public domain code from Sleator.
+ */
+
+ assert(t != NULL);
+
+ N.csn_left = N.csn_right = NULL;
+ l = r = &N;
+ for (;;) {
+ if (cset_rangecmp(t, ch) < 0) {
+ if (t->csn_left != NULL &&
+ cset_rangecmp(t->csn_left, ch) < 0) {
+ y = t->csn_left;
+ t->csn_left = y->csn_right;
+ y->csn_right = t;
+ t = y;
+ }
+ if (t->csn_left == NULL)
+ break;
+ r->csn_left = t;
+ r = t;
+ t = t->csn_left;
+ } else if (cset_rangecmp(t, ch) > 0) {
+ if (t->csn_right != NULL &&
+ cset_rangecmp(t->csn_right, ch) > 0) {
+ y = t->csn_right;
+ t->csn_right = y->csn_left;
+ y->csn_left = t;
+ t = y;
+ }
+ if (t->csn_right == NULL)
+ break;
+ l->csn_right = t;
+ l = t;
+ t = t->csn_right;
+ } else
+ break;
+ }
+ l->csn_right = t->csn_left;
+ r->csn_left = t->csn_right;
+ t->csn_left = N.csn_right;
+ t->csn_right = N.csn_left;
+ return (t);
+}
+
+static struct csnode *
+cset_delete(struct csnode *t, wchar_t ch)
+{
+ struct csnode *x;
+
+ assert(t != NULL);
+ t = cset_splay(t, ch);
+ assert(cset_rangecmp(t, ch) == 0);
+ if (t->csn_left == NULL)
+ x = t->csn_right;
+ else {
+ x = cset_splay(t->csn_left, ch);
+ x->csn_right = t->csn_right;
+ }
+ free(t);
+ return x;
+}
diff --git a/text_cmds/tr/cset.h b/text_cmds/tr/cset.h
new file mode 100644
index 0000000..ae62d1d
--- /dev/null
+++ b/text_cmds/tr/cset.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2004 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.bin/tr/cset.h,v 1.2 2004/07/14 08:35:11 tjr Exp $
+ */
+
+#ifndef CSET_H
+#define CSET_H
+
+#include <stdbool.h>
+#include <wchar.h>
+#include <wctype.h>
+
+struct csnode {
+ wchar_t csn_min;
+ wchar_t csn_max;
+ struct csnode *csn_left;
+ struct csnode *csn_right;
+};
+
+struct csclass {
+ wctype_t csc_type;
+ bool csc_invert;
+ struct csclass *csc_next;
+};
+
+struct cset {
+#define CS_CACHE_SIZE 256
+ bool cs_cache[CS_CACHE_SIZE];
+ bool cs_havecache;
+ struct csclass *cs_classes;
+ struct csnode *cs_root;
+ bool cs_invert;
+};
+
+bool cset_addclass(struct cset *, wctype_t, bool);
+struct cset * cset_alloc(void);
+bool cset_add(struct cset *, wchar_t);
+void cset_invert(struct cset *);
+bool cset_in_hard(struct cset *, wchar_t);
+void cset_cache(struct cset *);
+
+static __inline bool
+cset_in(struct cset *cs, wchar_t ch)
+{
+
+ if (ch < CS_CACHE_SIZE && cs->cs_havecache)
+ return (cs->cs_cache[ch]);
+ return (cset_in_hard(cs, ch));
+}
+
+#endif /* CSET_H */
diff --git a/text_cmds/tr/extern.h b/text_cmds/tr/extern.h
new file mode 100644
index 0000000..adc9ec3
--- /dev/null
+++ b/text_cmds/tr/extern.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.bin/tr/extern.h,v 1.9 2004/07/09 02:08:07 tjr Exp $
+ */
+
+#include <limits.h>
+
+#define NCHARS_SB (UCHAR_MAX + 1) /* Number of single-byte characters. */
+#define OOBCH -1 /* Out of band character value. */
+
+typedef struct {
+ enum { STRING1, STRING2 } which;
+ enum { EOS, INFINITE, NORMAL, RANGE, SEQUENCE,
+ CCLASS, CCLASS_UPPER, CCLASS_LOWER, SET } state;
+ int cnt; /* character count */
+ wint_t lastch; /* last character */
+ wctype_t cclass; /* character class from wctype() */
+ wint_t equiv[NCHARS_SB]; /* equivalence set */
+ wint_t *set; /* set of characters */
+ char *str; /* user's string */
+} STR;
+
+wint_t next(STR *);
+int charcoll(const void *, const void *);
diff --git a/text_cmds/tr/str.c b/text_cmds/tr/str.c
new file mode 100644
index 0000000..e7e6757
--- /dev/null
+++ b/text_cmds/tr/str.c
@@ -0,0 +1,450 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/tr/str.c,v 1.24 2004/11/14 05:15:25 jkh Exp $");
+
+#ifndef lint
+static const char sccsid[] = "@(#)str.c 8.2 (Berkeley) 4/28/95";
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <xlocale.h>
+
+#include "extern.h"
+
+static int backslash(STR *, int *);
+static int bracket(STR *);
+static void genclass(STR *);
+static void genequiv(STR *);
+static int genrange(STR *, int);
+static void genseq(STR *);
+
+/*
+ * Using libc internal function __collate_lookup_l for character
+ * equivalence
+ */
+void __collate_lookup_l(const __darwin_wchar_t *, int *, int *, int *,
+locale_t);
+/*
+ * Cache for primary collation weight of each single byte character
+ * used in static void genequiv(s)
+ */
+int collation_weight_cache[NCHARS_SB];
+int is_weight_cached = 0;
+
+wint_t
+next(s)
+ STR *s;
+{
+ int is_octal;
+ wint_t ch;
+ wchar_t wch;
+ size_t clen;
+
+ switch (s->state) {
+ case EOS:
+ return (0);
+ case INFINITE:
+#ifdef __APPLE__
+ switch (ch = (u_char)*s->str) {
+ case '\0':
+ /*
+ * force at least one postive return so setup() will
+ * process lastch of a sequence like [a*]; but change
+ * state so it won't get stuck in a while(next(s)) loop
+ */
+ s->state = NORMAL;
+ }
+#endif /* __APPLE__ */
+ return (1);
+ case NORMAL:
+ switch (*s->str) {
+ case '\0':
+ s->state = EOS;
+ return (0);
+ case '\\':
+ s->lastch = backslash(s, &is_octal);
+ break;
+ case '[':
+ if (bracket(s))
+ return (next(s));
+ /* FALLTHROUGH */
+ default:
+ clen = mbrtowc(&wch, s->str, MB_LEN_MAX, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2 ||
+ clen == 0)
+ errc(1, EILSEQ, NULL);
+ is_octal = 0;
+ s->lastch = wch;
+ s->str += clen;
+ break;
+ }
+
+ /* We can start a range at any time. */
+ if (s->str[0] == '-' && genrange(s, is_octal))
+ return (next(s));
+ return (1);
+ case RANGE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ ++s->lastch;
+ return (1);
+ case SEQUENCE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ return (1);
+ case CCLASS:
+ case CCLASS_UPPER:
+ case CCLASS_LOWER:
+ s->cnt++;
+ ch = nextwctype(s->lastch, s->cclass);
+ if (ch == -1) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ s->lastch = ch;
+ return (1);
+ case SET:
+ if ((ch = s->set[s->cnt++]) == OOBCH) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ s->lastch = ch;
+ return (1);
+ default:
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+static int
+bracket(s)
+ STR *s;
+{
+ char *p;
+
+ switch (s->str[1]) {
+ case ':': /* "[:class:]" */
+ if ((p = strchr(s->str + 2, ']')) == NULL)
+ return (0);
+ if (*(p - 1) != ':' || p - s->str < 4)
+ goto repeat;
+ *(p - 1) = '\0';
+ s->str += 2;
+ genclass(s);
+ s->str = p + 1;
+ return (1);
+ case '=': /* "[=equiv=]" */
+ if ((p = strchr(s->str + 2, ']')) == NULL)
+ return (0);
+ if (*(p - 1) != '=' || p - s->str < 4)
+ goto repeat;
+ s->str += 2;
+ genequiv(s);
+ return (1);
+ default: /* "[\###*n]" or "[#*n]" */
+ repeat:
+ if ((p = strpbrk(s->str + 2, "*]")) == NULL)
+ return (0);
+ if (p[0] != '*' || index(p, ']') == NULL)
+ return (0);
+ s->str += 1;
+ genseq(s);
+ return (1);
+ }
+ /* NOTREACHED */
+}
+
+static void
+genclass(s)
+ STR *s;
+{
+
+ if ((s->cclass = wctype(s->str)) == 0)
+ errx(1, "unknown class %s", s->str);
+ s->cnt = 0;
+ s->lastch = -1; /* incremented before check in next() */
+ if (strcmp(s->str, "upper") == 0)
+ s->state = CCLASS_UPPER;
+ else if (strcmp(s->str, "lower") == 0)
+ s->state = CCLASS_LOWER;
+ else
+ s->state = CCLASS;
+}
+
+static void
+genequiv(s)
+ STR *s;
+{
+ int i, p;
+ size_t clen;
+ wchar_t wc;
+
+ if (*s->str == '\\') {
+ s->equiv[0] = backslash(s, NULL);
+ if (*s->str != '=')
+ errx(1, "misplaced equivalence equals sign");
+ s->str += 2;
+ } else {
+ clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2 || clen == 0)
+ errc(1, EILSEQ, NULL);
+ s->equiv[0] = wc;
+ if (s->str[clen] != '=')
+ errx(1, "misplaced equivalence equals sign");
+ s->str += clen + 2;
+ }
+
+ /*
+ * Partially supporting multi-byte locales; only finds equivalent
+ * characters within the first NCHARS_SB entries of the
+ * collation table
+ */
+ int tprim, tsec;
+ int len;
+ __collate_lookup_l(s->equiv, &len, &tprim, &tsec, LC_GLOBAL_LOCALE);
+
+ if (tprim != -1) {
+ for (p = 1, i = 1; i < NCHARS_SB; i++) {
+ int cprim;
+ if (is_weight_cached) {
+ /*
+ * retrieve primary weight from cache
+ */
+ cprim = collation_weight_cache[i];
+ } else {
+ /*
+ * perform lookup of primary weight and fill cache
+ */
+ int csec;
+ __collate_lookup_l((__darwin_wchar_t *)&i, &len, &cprim, &csec, LC_GLOBAL_LOCALE);
+ collation_weight_cache[i] = cprim;
+ }
+
+ /*
+ * If a character does not exist in the collation
+ * table, just skip it
+ */
+ if (cprim == -1) {
+ continue;
+ }
+
+ /*
+ * Only compare primary weights to determine multi-byte
+ * character equivalence
+ */
+ if (cprim == tprim) {
+ s->equiv[p++] = i;
+ }
+ }
+ s->equiv[p] = OOBCH;
+
+ if (!is_weight_cached) {
+ is_weight_cached = 1;
+ }
+ }
+
+ s->cnt = 0;
+ s->state = SET;
+ s->set = s->equiv;
+}
+
+static int
+genrange(STR *s, int was_octal)
+{
+ int stopval, octal;
+ char *savestart;
+ int n, cnt, *p;
+ size_t clen;
+ wchar_t wc;
+
+ octal = 0;
+ savestart = s->str;
+ if (*++s->str == '\\')
+ stopval = backslash(s, &octal);
+ else {
+ clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2)
+ errc(1, EILSEQ, NULL);
+ stopval = wc;
+ s->str += clen;
+ }
+ /*
+ * XXX Characters are not ordered according to collating sequence in
+ * multibyte locales.
+ */
+ if (octal || was_octal || MB_CUR_MAX > 1) {
+ if (stopval < s->lastch) {
+ s->str = savestart;
+ return (0);
+ }
+ s->cnt = stopval - s->lastch + 1;
+ s->state = RANGE;
+ --s->lastch;
+ return (1);
+ }
+ if (charcoll((const void *)&stopval, (const void *)&(s->lastch)) < 0) {
+ s->str = savestart;
+ return (0);
+ }
+ if ((s->set = p = malloc((NCHARS_SB + 1) * sizeof(int))) == NULL)
+ err(1, "genrange() malloc");
+ for (cnt = 0; cnt < NCHARS_SB; cnt++)
+ if (charcoll((const void *)&cnt, (const void *)&(s->lastch)) >= 0 &&
+ charcoll((const void *)&cnt, (const void *)&stopval) <= 0)
+ *p++ = cnt;
+ *p = OOBCH;
+ n = p - s->set;
+
+ s->cnt = 0;
+ s->state = SET;
+ if (n > 1)
+ mergesort(s->set, n, sizeof(*(s->set)), charcoll);
+ return (1);
+}
+
+static void
+genseq(s)
+ STR *s;
+{
+ char *ep;
+ wchar_t wc;
+ size_t clen;
+
+#ifndef __APPLE__
+ if (s->which == STRING1)
+ errx(1, "sequences only valid in string2");
+#endif /* !__APPLE__ */
+
+ if (*s->str == '\\')
+ s->lastch = backslash(s, NULL);
+ else {
+ clen = mbrtowc(&wc, s->str, MB_LEN_MAX, NULL);
+ if (clen == (size_t)-1 || clen == (size_t)-2)
+ errc(1, EILSEQ, NULL);
+ s->lastch = wc;
+ s->str += clen;
+ }
+ if (*s->str != '*')
+ errx(1, "misplaced sequence asterisk");
+
+ switch (*++s->str) {
+ case '\\':
+ s->cnt = backslash(s, NULL);
+ break;
+ case ']':
+ s->cnt = 0;
+ ++s->str;
+ break;
+ default:
+ if (isdigit((u_char)*s->str)) {
+ s->cnt = strtol(s->str, &ep, 0);
+ if (*ep == ']') {
+ s->str = ep + 1;
+ break;
+ }
+ }
+ errx(1, "illegal sequence count");
+ /* NOTREACHED */
+ }
+
+ s->state = s->cnt ? SEQUENCE : INFINITE;
+}
+
+/*
+ * Translate \??? into a character. Up to 3 octal digits, if no digits either
+ * an escape code or a literal character.
+ */
+static int
+backslash(STR *s, int *is_octal)
+{
+ int ch, cnt, val;
+
+ if (is_octal != NULL)
+ *is_octal = 0;
+ for (cnt = val = 0;;) {
+ ch = (u_char)*++s->str;
+ if (!isdigit(ch) || ch > '7')
+ break;
+ val = val * 8 + ch - '0';
+ if (++cnt == 3) {
+ ++s->str;
+ break;
+ }
+ }
+ if (cnt) {
+ if (is_octal != NULL)
+ *is_octal = 1;
+ return (val);
+ }
+ if (ch != '\0')
+ ++s->str;
+ switch (ch) {
+ case 'a': /* escape characters */
+ return ('\7');
+ case 'b':
+ return ('\b');
+ case 'f':
+ return ('\f');
+ case 'n':
+ return ('\n');
+ case 'r':
+ return ('\r');
+ case 't':
+ return ('\t');
+ case 'v':
+ return ('\13');
+ case '\0': /* \" -> \ */
+ s->state = EOS;
+ return ('\\');
+ default: /* \x" -> x */
+ return (ch);
+ }
+}
diff --git a/text_cmds/tr/tr.1 b/text_cmds/tr/tr.1
new file mode 100644
index 0000000..6513674
--- /dev/null
+++ b/text_cmds/tr/tr.1
@@ -0,0 +1,420 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tr.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/tr/tr.1,v 1.33 2005/02/13 23:45:51 ru Exp $
+.\"
+.Dd July 23, 2004
+.Dt TR 1
+.Os
+.Sh NAME
+.Nm tr
+.Nd translate characters
+.Sh SYNOPSIS
+.Nm
+.Op Fl Ccsu
+.Ar string1 string2
+.Nm
+.Op Fl Ccu
+.Fl d
+.Ar string1
+.Nm
+.Op Fl Ccu
+.Fl s
+.Ar string1
+.Nm
+.Op Fl Ccu
+.Fl ds
+.Ar string1 string2
+.Sh DESCRIPTION
+The
+.Nm
+utility copies the standard input to the standard output with substitution
+or deletion of selected characters.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl C
+Complement the set of characters in
+.Ar string1 ,
+that is
+.Dq Fl C Li ab
+includes every character except for
+.Ql a
+and
+.Ql b .
+.It Fl c
+Same as
+.Fl C
+but complement the set of values in
+.Ar string1 .
+.It Fl d
+Delete characters in
+.Ar string1
+from the input.
+.It Fl s
+Squeeze multiple occurrences of the characters listed in the last
+operand (either
+.Ar string1
+or
+.Ar string2 )
+in the input into a single instance of the character.
+This occurs after all deletion and translation is completed.
+.It Fl u
+Guarantee that any output is unbuffered.
+.El
+.Pp
+In the first synopsis form, the characters in
+.Ar string1
+are translated into the characters in
+.Ar string2
+where the first character in
+.Ar string1
+is translated into the first character in
+.Ar string2
+and so on.
+If
+.Ar string1
+is longer than
+.Ar string2 ,
+the last character found in
+.Ar string2
+is duplicated until
+.Ar string1
+is exhausted.
+.Pp
+In the second synopsis form, the characters in
+.Ar string1
+are deleted from the input.
+.Pp
+In the third synopsis form, the characters in
+.Ar string1
+are compressed as described for the
+.Fl s
+option.
+.Pp
+In the fourth synopsis form, the characters in
+.Ar string1
+are deleted from the input, and the characters in
+.Ar string2
+are compressed as described for the
+.Fl s
+option.
+.Pp
+The following conventions can be used in
+.Ar string1
+and
+.Ar string2
+to specify sets of characters:
+.Bl -tag -width [:equiv:]
+.It character
+Any character not described by one of the following conventions
+represents itself.
+.It \eoctal
+A backslash followed by 1, 2 or 3 octal digits represents a character
+with that encoded value.
+To follow an octal sequence with a digit as a character, left zero-pad
+the octal sequence to the full 3 octal digits.
+.It \echaracter
+A backslash followed by certain special characters maps to special
+values.
+.Pp
+.Bl -column "\ea"
+.It "\ea <alert character>
+.It "\eb <backspace>
+.It "\ef <form-feed>
+.It "\en <newline>
+.It "\er <carriage return>
+.It "\et <tab>
+.It "\ev <vertical tab>
+.El
+.Pp
+A backslash followed by any other character maps to that character.
+.It c-c
+For non-octal range endpoints
+represents the range of characters between the range endpoints, inclusive,
+in ascending order,
+as defined by the collation sequence.
+If either or both of the range endpoints are octal sequences, it
+represents the range of specific coded values between the
+range endpoints, inclusive.
+.Pp
+.Bf Em
+See the
+.Sx COMPATIBILITY
+section below for an important note regarding
+differences in the way the current
+implementation interprets range expressions differently from
+previous implementations.
+.Ef
+.It [:class:]
+Represents all characters belonging to the defined character class.
+Class names are:
+.Pp
+.Bl -column "phonogram"
+.It "alnum <alphanumeric characters>
+.It "alpha <alphabetic characters>
+.It "blank <whitespace characters>
+.It "cntrl <control characters>
+.It "digit <numeric characters>
+.It "graph <graphic characters>
+.It "ideogram <ideographic characters>
+.It "lower <lower-case alphabetic characters>
+.It "phonogram <phonographic characters>
+.It "print <printable characters>
+.It "punct <punctuation characters>
+.It "rune <valid characters>
+.It "space <space characters>
+.It "special <special characters>
+.It "upper <upper-case characters>
+.It "xdigit <hexadecimal characters>
+.El
+.Pp
+.\" All classes may be used in
+.\" .Ar string1 ,
+.\" and in
+.\" .Ar string2
+.\" when both the
+.\" .Fl d
+.\" and
+.\" .Fl s
+.\" options are specified.
+.\" Otherwise, only the classes ``upper'' and ``lower'' may be used in
+.\" .Ar string2
+.\" and then only when the corresponding class (``upper'' for ``lower''
+.\" and vice-versa) is specified in the same relative position in
+.\" .Ar string1 .
+.\" .Pp
+When
+.Dq Li [:lower:]
+appears in
+.Ar string1
+and
+.Dq Li [:upper:]
+appears in the same relative position in
+.Ar string2 ,
+it represents the characters pairs from the
+.Dv toupper
+mapping in the
+.Ev LC_CTYPE
+category of the current locale.
+When
+.Dq Li [:upper:]
+appears in
+.Ar string1
+and
+.Dq Li [:lower:]
+appears in the same relative position in
+.Ar string2 ,
+it represents the characters pairs from the
+.Dv tolower
+mapping in the
+.Ev LC_CTYPE
+category of the current locale.
+.Pp
+With the exception of case conversion,
+characters in the classes are in unspecified order.
+.Pp
+For specific information as to which
+.Tn ASCII
+characters are included
+in these classes, see
+.Xr ctype 3
+and related manual pages.
+.It [=equiv=]
+Represents all characters belonging to the same equivalence class as
+.Ar equiv ,
+ordered by their encoded values.
+.It [#*n]
+Represents
+.Ar n
+repeated occurrences of the character represented by
+.Ar # .
+This
+expression is only valid when it occurs in
+.Ar string2 .
+If
+.Ar n
+is omitted or is zero, it is be interpreted as large enough to extend
+.Ar string2
+sequence to the length of
+.Ar string1 .
+If
+.Ar n
+has a leading zero, it is interpreted as an octal value, otherwise,
+it is interpreted as a decimal value.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_CTYPE
+and
+.Ev LC_COLLATE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The following examples are shown as given to the shell:
+.Pp
+Create a list of the words in file1, one per line, where a word is taken to
+be a maximal string of letters.
+.Pp
+.D1 Li "tr -cs \*q[:alpha:]\*q \*q\en\*q < file1"
+.Pp
+Translate the contents of file1 to upper-case.
+.Pp
+.D1 Li "tr \*q[:lower:]\*q \*q[:upper:]\*q < file1"
+.Pp
+(This should be preferred over the traditional
+.Ux
+idiom of
+.Dq Li "tr a-z A-Z" ,
+since it works correctly in all locales.)
+.Pp
+Strip out non-printable characters from file1.
+.Pp
+.D1 Li "tr -cd \*q[:print:]\*q < file1"
+.Pp
+Remove diacritical marks from all accented variants of the letter
+.Ql e :
+.Pp
+.Dl "tr \*q[=e=]\*q \*qe\*q"
+.Sh COMPATIBILITY
+Previous
+.Fx
+implementations of
+.Nm
+did not order characters in range expressions according to the current
+locale's collation order, making it possible to convert unaccented Latin
+characters (esp.\& as found in English text) from upper to lower case using
+the traditional
+.Ux
+idiom of
+.Dq Li "tr A-Z a-z" .
+Since
+.Nm
+now obeys the locale's collation order, this idiom may not produce
+correct results when there is not a 1:1 mapping between lower and
+upper case, or when the order of characters within the two cases differs.
+As noted in the
+.Sx EXAMPLES
+section above, the character class expressions
+.Dq Li [:lower:]
+and
+.Dq Li [:upper:]
+should be used instead of explicit character ranges like
+.Dq Li a-z
+and
+.Dq Li A-Z .
+.Pp
+System V has historically implemented character ranges using the syntax
+.Dq Li [c-c]
+instead of the
+.Dq Li c-c
+used by historic
+.Bx
+implementations and
+standardized by POSIX.
+System V shell scripts should work under this implementation as long as
+the range is intended to map in another range, i.e., the command
+.Dq Li "tr [a-z] [A-Z]"
+will work as it will map the
+.Ql \&[
+character in
+.Ar string1
+to the
+.Ql \&[
+character in
+.Ar string2 .
+However, if the shell script is deleting or squeezing characters as in
+the command
+.Dq Li "tr -d [a-z]" ,
+the characters
+.Ql \&[
+and
+.Ql \&]
+will be
+included in the deletion or compression list which would not have happened
+under a historic System V implementation.
+Additionally, any scripts that depended on the sequence
+.Dq Li a-z
+to
+represent the three characters
+.Ql a ,
+.Ql \-
+and
+.Ql z
+will have to be
+rewritten as
+.Dq Li a\e-z .
+.Pp
+The
+.Nm
+utility has historically not permitted the manipulation of NUL bytes in
+its input and, additionally, stripped NUL's from its input stream.
+This implementation has removed this behavior as a bug.
+.Pp
+The
+.Nm
+utility has historically been extremely forgiving of syntax errors,
+for example, the
+.Fl c
+and
+.Fl s
+options were ignored unless two strings were specified.
+This implementation will not permit illegal syntax.
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Pp
+It should be noted that the feature wherein the last character of
+.Ar string2
+is duplicated if
+.Ar string2
+has less characters than
+.Ar string1
+is permitted by POSIX but is not required.
+Shell scripts attempting to be portable to other POSIX systems should use
+the
+.Dq Li [#*]
+convention instead of relying on this behavior.
+The
+.Fl u
+option is an extension to the
+.St -p1003.1-2001
+standard.
diff --git a/text_cmds/tr/tr.c b/text_cmds/tr/tr.c
new file mode 100644
index 0000000..7bd2ca7
--- /dev/null
+++ b/text_cmds/tr/tr.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/tr/tr.c,v 1.24 2005/04/09 14:31:41 stefanf Exp $");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)tr.c 8.2 (Berkeley) 5/4/95";
+#endif
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "cmap.h"
+#include "cset.h"
+#include "extern.h"
+
+STR s1 = { STRING1, NORMAL, 0, OOBCH, 0, { 0, OOBCH }, NULL, NULL };
+STR s2 = { STRING2, NORMAL, 0, OOBCH, 0, { 0, OOBCH }, NULL, NULL };
+
+static struct cset *setup(char *, STR *, int, int);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ static int carray[NCHARS_SB];
+ struct cmap *map;
+ struct cset *delete, *squeeze;
+ int n, *p;
+ int Cflag, cflag, dflag, sflag, isstring2;
+ wint_t ch, cnt, lastch;
+
+ (void)setlocale(LC_ALL, "");
+
+ Cflag = cflag = dflag = sflag = 0;
+ while ((ch = getopt(argc, argv, "Ccdsu")) != -1)
+ switch((char)ch) {
+ case 'C':
+ Cflag = 1;
+ cflag = 0;
+ break;
+ case 'c':
+ cflag = 1;
+ Cflag = 0;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'u':
+ setbuf(stdout, (char *)NULL);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ default:
+ usage();
+ /* NOTREACHED */
+ case 1:
+ isstring2 = 0;
+ if(!argv[0]) usage();
+ break;
+ case 2:
+ isstring2 = 1;
+ if(!argv[0] || !argv[1]) usage();
+ break;
+ }
+
+ /*
+ * tr -ds [-Cc] string1 string2
+ * Delete all characters (or complemented characters) in string1.
+ * Squeeze all characters in string2.
+ */
+ if (dflag && sflag) {
+ if (!isstring2)
+ usage();
+
+ delete = setup(argv[0], &s1, cflag, Cflag);
+ squeeze = setup(argv[1], &s2, 0, 0);
+
+ for (lastch = OOBCH; (ch = getwchar()) != WEOF;)
+ if (!cset_in(delete, ch) &&
+ (lastch != ch || !cset_in(squeeze, ch))) {
+ lastch = ch;
+ (void)putwchar(ch);
+ }
+ if (ferror(stdin))
+ err(1, NULL);
+ exit(0);
+ }
+
+ /*
+ * tr -d [-Cc] string1
+ * Delete all characters (or complemented characters) in string1.
+ */
+ if (dflag) {
+ if (isstring2)
+ usage();
+
+ delete = setup(argv[0], &s1, cflag, Cflag);
+
+ while ((ch = getwchar()) != WEOF)
+ if (!cset_in(delete, ch))
+ (void)putwchar(ch);
+ if (ferror(stdin))
+ err(1, NULL);
+ exit(0);
+ }
+
+ /*
+ * tr -s [-Cc] string1
+ * Squeeze all characters (or complemented characters) in string1.
+ */
+ if (sflag && !isstring2) {
+ squeeze = setup(argv[0], &s1, cflag, Cflag);
+
+ for (lastch = OOBCH; (ch = getwchar()) != WEOF;)
+ if (lastch != ch || !cset_in(squeeze, ch)) {
+ lastch = ch;
+ (void)putwchar(ch);
+ }
+ if (ferror(stdin))
+ err(1, NULL);
+ exit(0);
+ }
+
+ /*
+ * tr [-Ccs] string1 string2
+ * Replace all characters (or complemented characters) in string1 with
+ * the character in the same position in string2. If the -s option is
+ * specified, squeeze all the characters in string2.
+ */
+ if (!isstring2)
+ usage();
+
+ map = cmap_alloc();
+ if (map == NULL)
+ err(1, NULL);
+ squeeze = cset_alloc();
+ if (squeeze == NULL)
+ err(1, NULL);
+
+ s1.str = argv[0];
+
+ if (Cflag || cflag) {
+ cmap_default(map, OOBCH);
+ if ((s2.str = strdup(argv[1])) == NULL)
+ errx(1, "strdup(argv[1])");
+ } else
+ s2.str = argv[1];
+
+ if (!next(&s2))
+ errx(1, "empty string2");
+
+ /*
+ * For -s result will contain only those characters defined
+ * as the second characters in each of the toupper or tolower
+ * pairs.
+ */
+
+ /* If string2 runs out of characters, use the last one specified. */
+ while (next(&s1)) {
+ again:
+ if (s1.state == CCLASS_LOWER &&
+ s2.state == CCLASS_UPPER &&
+ s1.cnt == 1 && s2.cnt == 1) {
+ do {
+ ch = towupper(s1.lastch);
+ cmap_add(map, s1.lastch, ch);
+ if (sflag && iswupper(ch))
+ cset_add(squeeze, ch);
+ if (!next(&s1))
+ goto endloop;
+ } while (s1.state == CCLASS_LOWER && s1.cnt > 1);
+ /* skip upper set */
+ do {
+ if (!next(&s2))
+ break;
+ } while (s2.state == CCLASS_UPPER && s2.cnt > 1);
+ goto again;
+ } else if (s1.state == CCLASS_UPPER &&
+ s2.state == CCLASS_LOWER &&
+ s1.cnt == 1 && s2.cnt == 1) {
+ do {
+ ch = towlower(s1.lastch);
+ cmap_add(map, s1.lastch, ch);
+ if (sflag && iswlower(ch))
+ cset_add(squeeze, ch);
+ if (!next(&s1))
+ goto endloop;
+ } while (s1.state == CCLASS_UPPER && s1.cnt > 1);
+ /* skip lower set */
+ do {
+ if (!next(&s2))
+ break;
+ } while (s2.state == CCLASS_LOWER && s2.cnt > 1);
+ goto again;
+ } else {
+ cmap_add(map, s1.lastch, s2.lastch);
+ if (sflag)
+ cset_add(squeeze, s2.lastch);
+ }
+ (void)next(&s2);
+ }
+endloop:
+ if (cflag || (Cflag && MB_CUR_MAX > 1)) {
+ /*
+ * This is somewhat tricky: since the character set is
+ * potentially huge, we need to avoid allocating a map
+ * entry for every character. Our strategy is to set the
+ * default mapping to the last character of string #2
+ * (= the one that gets automatically repeated), then to
+ * add back identity mappings for characters that should
+ * remain unchanged. We don't waste space on identity mappings
+ * for non-characters with the -C option; those are simulated
+ * in the I/O loop.
+ */
+ s2.str = argv[1];
+ s2.state = NORMAL;
+ for (cnt = 0; cnt < WCHAR_MAX; cnt++) {
+ if (Cflag && !iswrune(cnt))
+ continue;
+ if (cmap_lookup(map, cnt) == OOBCH) {
+ if (next(&s2))
+ cmap_add(map, cnt, s2.lastch);
+ if (sflag)
+ cset_add(squeeze, s2.lastch);
+ } else
+ cmap_add(map, cnt, cnt);
+ if ((s2.state == EOS || s2.state == INFINITE) &&
+ cnt >= cmap_max(map))
+ break;
+ }
+ cmap_default(map, s2.lastch);
+ } else if (Cflag) {
+ for (p = carray, cnt = 0; cnt < NCHARS_SB; cnt++) {
+ if (cmap_lookup(map, cnt) == OOBCH && iswrune(cnt))
+ *p++ = cnt;
+ else
+ cmap_add(map, cnt, cnt);
+ }
+ n = p - carray;
+ if (Cflag && n > 1)
+ (void)mergesort(carray, n, sizeof(*carray), charcoll);
+
+ s2.str = argv[1];
+ s2.state = NORMAL;
+ for (cnt = 0; cnt < n; cnt++) {
+ (void)next(&s2);
+ cmap_add(map, carray[cnt], s2.lastch);
+ /*
+ * Chars taken from s2 can be different this time
+ * due to lack of complex upper/lower processing,
+ * so fill string2 again to not miss some.
+ */
+ if (sflag)
+ cset_add(squeeze, s2.lastch);
+ }
+ }
+
+ cset_cache(squeeze);
+ cmap_cache(map);
+
+ if (sflag)
+ for (lastch = OOBCH; (ch = getwchar()) != WEOF;) {
+ if (!Cflag || iswrune(ch))
+ ch = cmap_lookup(map, ch);
+ if (lastch != ch || !cset_in(squeeze, ch)) {
+ lastch = ch;
+ (void)putwchar(ch);
+ }
+ }
+ else
+ while ((ch = getwchar()) != WEOF) {
+ if (!Cflag || iswrune(ch))
+ ch = cmap_lookup(map, ch);
+ (void)putwchar(ch);
+ }
+ if (ferror(stdin))
+ err(1, NULL);
+ exit (0);
+}
+
+static struct cset *
+setup(char *arg, STR *str, int cflag, int Cflag)
+{
+ struct cset *cs;
+
+ cs = cset_alloc();
+ if (cs == NULL)
+ err(1, NULL);
+ str->str = arg;
+ while (next(str))
+ cset_add(cs, str->lastch);
+ if (Cflag)
+ cset_addclass(cs, wctype("rune"), true);
+ if (cflag || Cflag)
+ cset_invert(cs);
+ cset_cache(cs);
+ return (cs);
+}
+
+int
+charcoll(const void *a, const void *b)
+{
+ static char sa[2], sb[2];
+
+ sa[0] = *(const int *)a;
+ sb[0] = *(const int *)b;
+ return (strcoll(sa, sb));
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: tr [-Ccsu] string1 string2",
+ " tr [-Ccu] -d string1",
+ " tr [-Ccu] -s string1",
+ " tr [-Ccu] -ds string1 string2");
+ exit(1);
+}
diff --git a/text_cmds/ul/ul.1 b/text_cmds/ul/ul.1
new file mode 100644
index 0000000..847b958
--- /dev/null
+++ b/text_cmds/ul/ul.1
@@ -0,0 +1,104 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ul.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/ul/ul.1,v 1.18 2005/05/21 09:55:09 ru Exp $
+.\"
+.Dd August 4, 2004
+.Dt UL 1
+.Os
+.Sh NAME
+.Nm ul
+.Nd do underlining
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl t Ar terminal
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the named files (or standard input if none are given)
+and translates occurrences of underscores to the sequence
+which indicates underlining for the terminal in use, as specified
+by the environment variable
+.Ev TERM .
+The file
+.Pa /etc/termcap
+is read to determine the appropriate sequences for underlining.
+If the terminal is incapable of underlining, but is capable of
+a standout mode then that is used instead.
+If the terminal can overstrike,
+or handles underlining automatically,
+.Nm
+degenerates to
+.Xr cat 1 .
+If the terminal cannot underline, underlining is ignored.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl i
+Underlining is indicated by a separate line containing appropriate
+dashes `\-'; this is useful when you want to look at the underlining
+which is present in an
+.Xr nroff 1
+output stream on a crt-terminal.
+.It Fl t Ar terminal
+Overrides the terminal type specified in the environment with
+.Ar terminal .
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL , LC_CTYPE
+and
+.Ev TERM
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr colcrt 1 ,
+.Xr man 1 ,
+.Xr nroff 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+The
+.Xr nroff 1
+command usually outputs a series of backspaces and underlines intermixed
+with the text to indicate underlining.
+No attempt is made to optimize
+the backward motion.
diff --git a/text_cmds/ul/ul.c b/text_cmds/ul/ul.c
new file mode 100644
index 0000000..ab22ea8
--- /dev/null
+++ b/text_cmds/ul/ul.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 1980, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD: src/usr.bin/ul/ul.c,v 1.14 2005/05/21 09:55:09 ru Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termcap.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define IESC '\033'
+#define SO '\016'
+#define SI '\017'
+#define HFWD '9'
+#define HREV '8'
+#define FREV '7'
+#define MAXBUF 512
+
+#define NORMAL 000
+#define ALTSET 001 /* Reverse */
+#define SUPERSC 002 /* Dim */
+#define SUBSC 004 /* Dim | Ul */
+#define UNDERL 010 /* Ul */
+#define BOLD 020 /* Bold */
+
+int must_use_uc, must_overstrike;
+const char
+ *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
+ *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
+ *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
+
+struct CHAR {
+ char c_mode;
+ wchar_t c_char;
+ int c_width; /* width or -1 if multi-column char. filler */
+} ;
+
+struct CHAR obuf[MAXBUF];
+int col, maxcol;
+int mode;
+int halfpos;
+int upln;
+int iflag;
+
+static void usage(void);
+void setnewmode(int);
+void initcap(void);
+void reverse(void);
+int outchar(int);
+void fwd(void);
+void initbuf(void);
+void iattr(void);
+void overstrike(void);
+void flushln(void);
+void filter(FILE *);
+void outc(wint_t, int);
+
+#define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar)
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ const char *termtype;
+ FILE *f;
+ char termcap[1024];
+
+ setlocale(LC_ALL, "");
+
+ termtype = getenv("TERM");
+ if (termtype == NULL || (argv[0] && argv[0][0] == 'c' && !isatty(1)))
+ termtype = "lpr";
+ while ((c=getopt(argc, argv, "it:T:")) != -1)
+ switch(c) {
+
+ case 't':
+ case 'T': /* for nroff compatibility */
+ termtype = optarg;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ default:
+ usage();
+ }
+
+ switch(tgetent(termcap, termtype)) {
+
+ case 1:
+ break;
+
+ default:
+ warnx("trouble reading termcap");
+ /* FALLTHROUGH */
+
+ case 0:
+ /* No such terminal type - assume dumb */
+ (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
+ break;
+ }
+ initcap();
+ if ( (tgetflag("os") && ENTER_BOLD==NULL ) ||
+ (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+ must_overstrike = 1;
+ initbuf();
+ if (optind == argc)
+ filter(stdin);
+ else for (; optind<argc && argv[optind]; optind++) {
+ f = fopen(argv[optind],"r");
+ if (f == NULL)
+ err(1, "%s", argv[optind]);
+ else
+ filter(f);
+ }
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: ul [-i] [-t terminal] [file ...]\n");
+ exit(1);
+}
+
+void
+filter(FILE *f)
+{
+ wint_t c;
+ int i, w;
+
+ while ((c = getwc(f)) != WEOF && col < MAXBUF) switch(c) {
+
+ case '\b':
+ if (col > 0)
+ col--;
+ continue;
+
+ case '\t':
+ col = (col+8) & ~07;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+
+ case '\r':
+ col = 0;
+ continue;
+
+ case SO:
+ mode |= ALTSET;
+ continue;
+
+ case SI:
+ mode &= ~ALTSET;
+ continue;
+
+ case IESC:
+ switch (c = getwc(f)) {
+
+ case HREV:
+ if (halfpos == 0) {
+ mode |= SUPERSC;
+ halfpos--;
+ } else if (halfpos > 0) {
+ mode &= ~SUBSC;
+ halfpos--;
+ } else {
+ halfpos = 0;
+ reverse();
+ }
+ continue;
+
+ case HFWD:
+ if (halfpos == 0) {
+ mode |= SUBSC;
+ halfpos++;
+ } else if (halfpos < 0) {
+ mode &= ~SUPERSC;
+ halfpos++;
+ } else {
+ halfpos = 0;
+ fwd();
+ }
+ continue;
+
+ case FREV:
+ reverse();
+ continue;
+
+ default:
+ errx(1, "unknown escape sequence in input: %o, %o", IESC, c);
+ }
+ continue;
+
+ case '_':
+ if (obuf[col].c_char || obuf[col].c_width < 0) {
+ while (col > 0 && obuf[col].c_width < 0)
+ col--;
+ w = obuf[col].c_width;
+ for (i = 0; i < w; i++)
+ obuf[col++].c_mode |= UNDERL | mode;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+ }
+ obuf[col].c_char = '_';
+ obuf[col].c_width = 1;
+ /* FALLTHROUGH */
+ case ' ':
+ col++;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+
+ case '\n':
+ flushln();
+ continue;
+
+ case '\f':
+ flushln();
+ putwchar('\f');
+ continue;
+
+ default:
+ if ((w = wcwidth(c)) <= 0) /* non printing */
+ continue;
+ if (obuf[col].c_char == '\0') {
+ obuf[col].c_char = c;
+ for (i = 0; i < w; i++)
+ obuf[col + i].c_mode = mode;
+ obuf[col].c_width = w;
+ for (i = 1; i < w; i++)
+ obuf[col + i].c_width = -1;
+ } else if (obuf[col].c_char == '_') {
+ obuf[col].c_char = c;
+ for (i = 0; i < w; i++)
+ obuf[col + i].c_mode |= UNDERL|mode;
+ obuf[col].c_width = w;
+ for (i = 1; i < w; i++)
+ obuf[col + i].c_width = -1;
+ } else if (obuf[col].c_char == c) {
+ for (i = 0; i < w; i++)
+ obuf[col + i].c_mode |= BOLD|mode;
+ } else {
+ w = obuf[col].c_width;
+ for (i = 0; i < w; i++)
+ obuf[col + i].c_mode = mode;
+ }
+ col += w;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+ }
+ if (ferror(f))
+ err(1, NULL);
+ if (maxcol)
+ flushln();
+}
+
+void
+flushln(void)
+{
+ int lastmode;
+ int i;
+ int hadmodes = 0;
+
+ lastmode = NORMAL;
+ for (i=0; i<maxcol; i++) {
+ if (obuf[i].c_mode != lastmode) {
+ hadmodes++;
+ setnewmode(obuf[i].c_mode);
+ lastmode = obuf[i].c_mode;
+ }
+ if (obuf[i].c_char == '\0') {
+ if (upln)
+ PRINT(CURS_RIGHT);
+ else
+ outc(' ', 1);
+ } else
+ outc(obuf[i].c_char, obuf[i].c_width);
+ if (obuf[i].c_width > 1)
+ i += obuf[i].c_width - 1;
+ }
+ if (lastmode != NORMAL) {
+ setnewmode(0);
+ }
+ if (must_overstrike && hadmodes)
+ overstrike();
+ putwchar('\n');
+ if (iflag && hadmodes)
+ iattr();
+ (void)fflush(stdout);
+ if (upln)
+ upln--;
+ initbuf();
+}
+
+/*
+ * For terminals that can overstrike, overstrike underlines and bolds.
+ * We don't do anything with halfline ups and downs, or Greek.
+ */
+void
+overstrike(void)
+{
+ int i;
+ wchar_t lbuf[256];
+ wchar_t *cp = lbuf;
+ int hadbold=0;
+
+ /* Set up overstrike buffer */
+ for (i=0; i<maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL:
+ default:
+ *cp++ = ' ';
+ break;
+ case UNDERL:
+ *cp++ = '_';
+ break;
+ case BOLD:
+ *cp++ = obuf[i].c_char;
+ if (obuf[i].c_width > 1)
+ i += obuf[i].c_width - 1;
+ hadbold=1;
+ break;
+ }
+ putwchar('\r');
+ for (*cp=' '; *cp==' '; cp--)
+ *cp = 0;
+ for (cp=lbuf; *cp; cp++)
+ putwchar(*cp);
+ if (hadbold) {
+ putwchar('\r');
+ for (cp=lbuf; *cp; cp++)
+ putwchar(*cp=='_' ? ' ' : *cp);
+ putwchar('\r');
+ for (cp=lbuf; *cp; cp++)
+ putwchar(*cp=='_' ? ' ' : *cp);
+ }
+}
+
+void
+iattr(void)
+{
+ int i;
+ wchar_t lbuf[256];
+ wchar_t *cp = lbuf;
+
+ for (i=0; i<maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL: *cp++ = ' '; break;
+ case ALTSET: *cp++ = 'g'; break;
+ case SUPERSC: *cp++ = '^'; break;
+ case SUBSC: *cp++ = 'v'; break;
+ case UNDERL: *cp++ = '_'; break;
+ case BOLD: *cp++ = '!'; break;
+ default: *cp++ = 'X'; break;
+ }
+ for (*cp=' '; *cp==' '; cp--)
+ *cp = 0;
+ for (cp=lbuf; *cp; cp++)
+ putwchar(*cp);
+ putwchar('\n');
+}
+
+void
+initbuf(void)
+{
+
+ bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
+ col = 0;
+ maxcol = 0;
+ mode &= ALTSET;
+}
+
+void
+fwd(void)
+{
+ int oldcol, oldmax;
+
+ oldcol = col;
+ oldmax = maxcol;
+ flushln();
+ col = oldcol;
+ maxcol = oldmax;
+}
+
+void
+reverse(void)
+{
+ upln++;
+ fwd();
+ PRINT(CURS_UP);
+ PRINT(CURS_UP);
+ upln++;
+}
+
+void
+initcap(void)
+{
+ static char tcapbuf[512];
+ char *bp = tcapbuf;
+
+ /* This nonsense attempts to work with both old and new termcap */
+ CURS_UP = tgetstr("up", &bp);
+ CURS_RIGHT = tgetstr("ri", &bp);
+ if (CURS_RIGHT == NULL)
+ CURS_RIGHT = tgetstr("nd", &bp);
+ CURS_LEFT = tgetstr("le", &bp);
+ if (CURS_LEFT == NULL)
+ CURS_LEFT = tgetstr("bc", &bp);
+ if (CURS_LEFT == NULL && tgetflag("bs"))
+ CURS_LEFT = "\b";
+
+ ENTER_STANDOUT = tgetstr("so", &bp);
+ EXIT_STANDOUT = tgetstr("se", &bp);
+ ENTER_UNDERLINE = tgetstr("us", &bp);
+ EXIT_UNDERLINE = tgetstr("ue", &bp);
+ ENTER_DIM = tgetstr("mh", &bp);
+ ENTER_BOLD = tgetstr("md", &bp);
+ ENTER_REVERSE = tgetstr("mr", &bp);
+ EXIT_ATTRIBUTES = tgetstr("me", &bp);
+
+ if (!ENTER_BOLD && ENTER_REVERSE)
+ ENTER_BOLD = ENTER_REVERSE;
+ if (!ENTER_BOLD && ENTER_STANDOUT)
+ ENTER_BOLD = ENTER_STANDOUT;
+ if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
+ ENTER_UNDERLINE = ENTER_STANDOUT;
+ EXIT_UNDERLINE = EXIT_STANDOUT;
+ }
+ if (!ENTER_DIM && ENTER_STANDOUT)
+ ENTER_DIM = ENTER_STANDOUT;
+ if (!ENTER_REVERSE && ENTER_STANDOUT)
+ ENTER_REVERSE = ENTER_STANDOUT;
+ if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
+ EXIT_ATTRIBUTES = EXIT_STANDOUT;
+
+ /*
+ * Note that we use REVERSE for the alternate character set,
+ * not the as/ae capabilities. This is because we are modelling
+ * the model 37 teletype (since that's what nroff outputs) and
+ * the typical as/ae is more of a graphics set, not the greek
+ * letters the 37 has.
+ */
+
+ UNDER_CHAR = tgetstr("uc", &bp);
+ must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
+}
+
+int
+outchar(int c)
+{
+ return (putwchar(c) != WEOF ? c : EOF);
+}
+
+static int curmode = 0;
+
+void
+outc(wint_t c, int width)
+{
+ int i;
+
+ putwchar(c);
+ if (must_use_uc && (curmode&UNDERL)) {
+ for (i = 0; i < width; i++)
+ PRINT(CURS_LEFT);
+ for (i = 0; i < width; i++)
+ PRINT(UNDER_CHAR);
+ }
+}
+
+void
+setnewmode(int newmode)
+{
+ if (!iflag) {
+ if (curmode != NORMAL && newmode != NORMAL)
+ setnewmode(NORMAL);
+ switch (newmode) {
+ case NORMAL:
+ switch(curmode) {
+ case NORMAL:
+ break;
+ case UNDERL:
+ PRINT(EXIT_UNDERLINE);
+ break;
+ default:
+ /* This includes standout */
+ PRINT(EXIT_ATTRIBUTES);
+ break;
+ }
+ break;
+ case ALTSET:
+ PRINT(ENTER_REVERSE);
+ break;
+ case SUPERSC:
+ /*
+ * This only works on a few terminals.
+ * It should be fixed.
+ */
+ PRINT(ENTER_UNDERLINE);
+ PRINT(ENTER_DIM);
+ break;
+ case SUBSC:
+ PRINT(ENTER_DIM);
+ break;
+ case UNDERL:
+ PRINT(ENTER_UNDERLINE);
+ break;
+ case BOLD:
+ PRINT(ENTER_BOLD);
+ break;
+ default:
+ /*
+ * We should have some provision here for multiple modes
+ * on at once. This will have to come later.
+ */
+ PRINT(ENTER_STANDOUT);
+ break;
+ }
+ }
+ curmode = newmode;
+}
diff --git a/text_cmds/unexpand/unexpand.c b/text_cmds/unexpand/unexpand.c
new file mode 100644
index 0000000..7120925
--- /dev/null
+++ b/text_cmds/unexpand/unexpand.c
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 1980, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/unexpand/unexpand.c,v 1.15 2006/10/13 16:22:25 ru Exp $");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)unexpand.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+/*
+ * unexpand - put tabs into a file replacing blanks
+ */
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+int all;
+int nstops;
+int tabstops[100];
+
+static void getstops(const char *);
+static void usage(void);
+static int tabify(const char *);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, failed;
+ char *filename;
+
+ setlocale(LC_CTYPE, "");
+
+ nstops = 1;
+ tabstops[0] = 8;
+ while ((ch = getopt(argc, argv, "at:")) != -1) {
+ switch (ch) {
+ case 'a': /* Un-expand all spaces, not just leading. */
+ all = 1;
+ break;
+ case 't': /* Specify tab list, implies -a. */
+ getstops(optarg);
+ all = 1;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ failed = 0;
+ if (argc == 0)
+ failed |= tabify("stdin");
+ else {
+ while ((filename = *argv++) != NULL) {
+ if (freopen(filename, "r", stdin) == NULL) {
+ warn("%s", filename);
+ failed = 1;
+ } else
+ failed |= tabify(filename);
+ }
+ }
+ exit(failed != 0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: unexpand [-a | -t tablist] [file ...]\n");
+ exit(1);
+}
+
+static int
+tabify(const char *curfile)
+{
+ int dcol, doneline, limit, n, ocol, width;
+ wint_t ch;
+
+ limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
+
+ doneline = ocol = dcol = 0;
+ while ((ch = getwchar()) != WEOF) {
+ if (ch == ' ' && !doneline) {
+ if (++dcol >= limit)
+ doneline = 1;
+ continue;
+ } else if (ch == '\t') {
+ if (nstops == 1) {
+ dcol = (1 + dcol / tabstops[0]) *
+ tabstops[0];
+ continue;
+ } else {
+ for (n = 0; tabstops[n] - 1 < dcol &&
+ n < nstops; n++)
+ ;
+ if (n < nstops - 1 && tabstops[n] - 1 < limit) {
+ dcol = tabstops[n];
+ continue;
+ }
+ doneline = 1;
+ }
+ }
+
+ /* Output maximal number of tabs. */
+ if (nstops == 1) {
+ while (((ocol + tabstops[0]) / tabstops[0])
+ <= (dcol / tabstops[0])) {
+ if (dcol - ocol < 2)
+ break;
+ putwchar('\t');
+ ocol = (1 + ocol / tabstops[0]) *
+ tabstops[0];
+ }
+ } else {
+ for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
+ ;
+ while (ocol < dcol && n < nstops && ocol < limit) {
+ putwchar('\t');
+ ocol = tabstops[n++];
+ }
+ }
+
+ /* Then spaces. */
+ while (ocol < dcol && ocol < limit) {
+ putwchar(' ');
+ ocol++;
+ }
+
+ if (ch == '\b') {
+ putwchar('\b');
+ if (ocol > 0)
+ ocol--, dcol--;
+ } else if (ch == '\n') {
+ putwchar('\n');
+ doneline = ocol = dcol = 0;
+ continue;
+ } else if (ch != ' ' || dcol > limit) {
+ putwchar(ch);
+ if ((width = wcwidth(ch)) > 0)
+ ocol += width, dcol += width;
+ }
+
+ /*
+ * Only processing leading blanks or we've gone past the
+ * last tab stop. Emit remainder of this line unchanged.
+ */
+ if (!all || dcol >= limit) {
+ while ((ch = getwchar()) != '\n' && ch != WEOF)
+ putwchar(ch);
+ if (ch == '\n')
+ putwchar('\n');
+ doneline = ocol = dcol = 0;
+ }
+ }
+ if (ferror(stdin)) {
+ warn("%s", curfile);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+getstops(const char *cp)
+{
+ int i;
+
+ nstops = 0;
+ for (;;) {
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + *cp++ - '0';
+ if (i <= 0)
+ errx(1, "bad tab stop spec");
+ if (nstops > 0 && i <= tabstops[nstops-1])
+ errx(1, "bad tab stop spec");
+ if (nstops == sizeof(tabstops) / sizeof(*tabstops))
+ errx(1, "too many tab stops");
+ tabstops[nstops++] = i;
+ if (*cp == 0)
+ break;
+ if (*cp != ',' && !isblank((unsigned char)*cp))
+ errx(1, "bad tab stop spec");
+ cp++;
+ }
+}
diff --git a/text_cmds/uniq/uniq.1 b/text_cmds/uniq/uniq.1
new file mode 100644
index 0000000..2baaee1
--- /dev/null
+++ b/text_cmds/uniq/uniq.1
@@ -0,0 +1,151 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)uniq.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: head/usr.bin/uniq/uniq.1 216370 2010-12-11 08:32:16Z joel $
+.\"
+.Dd December 17, 2009
+.Dt UNIQ 1
+.Os
+.Sh NAME
+.Nm uniq
+.Nd report or filter out repeated lines in a file
+.Sh SYNOPSIS
+.Nm
+.Op Fl c | Fl d | Fl u
+.Op Fl i
+.Op Fl f Ar num
+.Op Fl s Ar chars
+.Oo
+.Ar input_file
+.Op Ar output_file
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the specified
+.Ar input_file
+comparing adjacent lines, and writes a copy of each unique input line to
+the
+.Ar output_file .
+If
+.Ar input_file
+is a single dash
+.Pq Sq Fl
+or absent, the standard input is read.
+If
+.Ar output_file
+is absent, standard output is used for output.
+The second and succeeding copies of identical adjacent input lines are
+not written.
+Repeated lines in the input will not be detected if they are not adjacent,
+so it may be necessary to sort the files first.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Precede each output line with the count of the number of times the line
+occurred in the input, followed by a single space.
+.It Fl d
+Only output lines that are repeated in the input.
+.It Fl f Ar num
+Ignore the first
+.Ar num
+fields in each input line when doing comparisons.
+A field is a string of non-blank characters separated from adjacent fields
+by blanks.
+Field numbers are one based, i.e., the first field is field one.
+.It Fl s Ar chars
+Ignore the first
+.Ar chars
+characters in each input line when doing comparisons.
+If specified in conjunction with the
+.Fl f
+option, the first
+.Ar chars
+characters after the first
+.Ar num
+fields will be ignored.
+Character numbers are one based, i.e., the first character is character one.
+.It Fl u
+Only output lines that are not repeated in the input.
+.It Fl i
+Case insensitive comparison of lines.
+.\".It Fl Ns Ar n
+.\"(Deprecated; replaced by
+.\".Fl f ) .
+.\"Ignore the first n
+.\"fields on each input line when doing comparisons,
+.\"where n is a number.
+.\"A field is a string of non-blank
+.\"characters separated from adjacent fields
+.\"by blanks.
+.\".It Cm \&\(pl Ns Ar n
+.\"(Deprecated; replaced by
+.\".Fl s ) .
+.\"Ignore the first
+.\".Ar m
+.\"characters when doing comparisons, where
+.\".Ar m
+.\"is a
+.\"number.
+.El
+.Sh ENVIRONMENT
+The
+.Ev LANG ,
+.Ev LC_ALL ,
+.Ev LC_COLLATE
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+The historic
+.Cm \&\(pl Ns Ar number
+and
+.Fl Ns Ar number
+options have been deprecated but are still supported in this implementation.
+.Sh SEE ALSO
+.Xr sort 1
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001
+as amended by Cor.\& 1-2002.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v3 .
diff --git a/text_cmds/uniq/uniq.c b/text_cmds/uniq/uniq.c
new file mode 100644
index 0000000..4258ab9
--- /dev/null
+++ b/text_cmds/uniq/uniq.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD: head/usr.bin/uniq/uniq.c 303526 2016-07-30 01:07:47Z bapt $";
+#endif /* not lint */
+
+#ifndef __APPLE__
+#include <sys/capsicum.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <nl_types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+static int cflag, dflag, uflag, iflag;
+static long numchars, numfields;
+static int repeats;
+
+static FILE *file(const char *, const char *);
+static wchar_t *convert(const char *);
+static int inlcmp(const char *, const char *);
+static void show(FILE *, const char *);
+static wchar_t *skip(wchar_t *);
+static void obsolete(char *[]);
+static void usage(void);
+
+static void
+strerror_init(void)
+{
+
+ /*
+ * Cache NLS data before entering capability mode.
+ * XXXPJD: There should be strerror_init() and strsignal_init() in libc.
+ */
+ (void)catopen("libc", NL_CAT_LOCALE);
+}
+
+int
+main (int argc, char *argv[])
+{
+ wchar_t *tprev, *tthis;
+ FILE *ifp, *ofp;
+ int ch, comp;
+ size_t prevbuflen, thisbuflen, b1;
+ char *prevline, *thisline, *p;
+ const char *ifn;
+#ifndef __APPLE__
+ cap_rights_t rights;
+#endif
+
+ (void) setlocale(LC_ALL, "");
+
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "cdif:s:u")) != -1)
+ switch (ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'f':
+ numfields = strtol(optarg, &p, 10);
+ if (numfields < 0 || *p)
+ errx(1, "illegal field skip value: %s", optarg);
+ break;
+ case 's':
+ numchars = strtol(optarg, &p, 10);
+ if (numchars < 0 || *p)
+ errx(1, "illegal character skip value: %s", optarg);
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* If no flags are set, default is -d -u. */
+ if (cflag) {
+ if (dflag || uflag)
+ usage();
+ } else if (!dflag && !uflag)
+ dflag = uflag = 1;
+
+ if (argc > 2)
+ usage();
+
+ ifp = stdin;
+ ifn = "stdin";
+ ofp = stdout;
+ if (argc > 0 && strcmp(argv[0], "-") != 0)
+ ifp = file(ifn = argv[0], "r");
+#ifndef __APPLE__
+ cap_rights_init(&rights, CAP_FSTAT, CAP_READ);
+ if (cap_rights_limit(fileno(ifp), &rights) < 0 && errno != ENOSYS)
+ err(1, "unable to limit rights for %s", ifn);
+ cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE);
+ if (argc > 1)
+ ofp = file(argv[1], "w");
+ else
+ cap_rights_set(&rights, CAP_IOCTL);
+ if (cap_rights_limit(fileno(ofp), &rights) < 0 && errno != ENOSYS) {
+ err(1, "unable to limit rights for %s",
+ argc > 1 ? argv[1] : "stdout");
+ }
+ if (cap_rights_is_set(&rights, CAP_IOCTL)) {
+ unsigned long cmd;
+
+ cmd = TIOCGETA; /* required by isatty(3) in printf(3) */
+
+ if (cap_ioctls_limit(fileno(ofp), &cmd, 1) < 0 &&
+ errno != ENOSYS) {
+ err(1, "unable to limit ioctls for %s",
+ argc > 1 ? argv[1] : "stdout");
+ }
+ }
+
+ strerror_init();
+ if (cap_enter() < 0 && errno != ENOSYS)
+ err(1, "unable to enter capability mode");
+#else
+ if (argc > 1)
+ ofp = file(argv[1], "w");
+ strerror_init();
+#endif
+
+ prevbuflen = thisbuflen = 0;
+ prevline = thisline = NULL;
+
+ if (getline(&prevline, &prevbuflen, ifp) < 0) {
+ if (ferror(ifp))
+ err(1, "%s", ifn);
+ exit(0);
+ }
+ tprev = convert(prevline);
+
+ if (!cflag && uflag && dflag)
+ show(ofp, prevline);
+
+ tthis = NULL;
+ while (getline(&thisline, &thisbuflen, ifp) >= 0) {
+ if (tthis != NULL)
+ free(tthis);
+ tthis = convert(thisline);
+
+ if (tthis == NULL && tprev == NULL)
+ comp = inlcmp(thisline, prevline);
+ else if (tthis == NULL || tprev == NULL)
+ comp = 1;
+ else
+ comp = wcscoll(tthis, tprev);
+
+ if (comp) {
+ /* If different, print; set previous to new value. */
+ if (cflag || !dflag || !uflag)
+ show(ofp, prevline);
+ p = prevline;
+ b1 = prevbuflen;
+ prevline = thisline;
+ prevbuflen = thisbuflen;
+ if (tprev != NULL)
+ free(tprev);
+ tprev = tthis;
+ if (!cflag && uflag && dflag)
+ show(ofp, prevline);
+ thisline = p;
+ thisbuflen = b1;
+ tthis = NULL;
+ repeats = 0;
+ } else
+ ++repeats;
+ }
+ if (ferror(ifp))
+ err(1, "%s", ifn);
+ if (cflag || !dflag || !uflag)
+ show(ofp, prevline);
+ exit(0);
+}
+
+static wchar_t *
+convert(const char *str)
+{
+ size_t n;
+ wchar_t *buf, *ret, *p;
+
+ if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1)
+ return (NULL);
+ if (SIZE_MAX / sizeof(*buf) < n + 1)
+ errx(1, "conversion buffer length overflow");
+ if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL)
+ err(1, "malloc");
+ if (mbstowcs(buf, str, n + 1) != n)
+ errx(1, "internal mbstowcs() error");
+ /* The last line may not end with \n. */
+ if (n > 0 && buf[n - 1] == L'\n')
+ buf[n - 1] = L'\0';
+
+ /* If requested get the chosen fields + character offsets. */
+ if (numfields || numchars) {
+ if ((ret = wcsdup(skip(buf))) == NULL)
+ err(1, "wcsdup");
+ free(buf);
+ } else
+ ret = buf;
+
+ if (iflag) {
+ for (p = ret; *p != L'\0'; p++)
+ *p = towlower(*p);
+ }
+
+ return (ret);
+}
+
+static int
+inlcmp(const char *s1, const char *s2)
+{
+ int c1, c2;
+
+ while (*s1 == *s2++)
+ if (*s1++ == '\0')
+ return (0);
+ c1 = (unsigned char)*s1;
+ c2 = (unsigned char)*(s2 - 1);
+ /* The last line may not end with \n. */
+ if (c1 == '\n')
+ c1 = '\0';
+ if (c2 == '\n')
+ c2 = '\0';
+ return (c1 - c2);
+}
+
+/*
+ * show --
+ * Output a line depending on the flags and number of repetitions
+ * of the line.
+ */
+static void
+show(FILE *ofp, const char *str)
+{
+
+ if (cflag)
+ (void)fprintf(ofp, "%4d %s", repeats + 1, str);
+ if ((dflag && repeats) || (uflag && !repeats))
+ (void)fprintf(ofp, "%s", str);
+}
+
+static wchar_t *
+skip(wchar_t *str)
+{
+ long nchars, nfields;
+
+ for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) {
+ while (iswblank(*str))
+ str++;
+ while (*str != L'\0' && !iswblank(*str))
+ str++;
+ }
+ for (nchars = numchars; nchars-- && *str != L'\0'; ++str)
+ ;
+ return(str);
+}
+
+static FILE *
+file(const char *name, const char *mode)
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, mode)) == NULL)
+ err(1, "%s", name);
+ return(fp);
+}
+
+static void
+obsolete(char *argv[])
+{
+ size_t len;
+ char *ap, *p, *start;
+
+ while ((ap = *++argv)) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+ if (!isdigit((unsigned char)ap[1]))
+ continue;
+ /*
+ * Digit signifies an old-style option. Malloc space for dash,
+ * new option and argument.
+ */
+ len = strlen(ap);
+ if ((start = p = malloc(len + 3)) == NULL)
+ err(1, "malloc");
+ *p++ = '-';
+ *p++ = ap[0] == '+' ? 's' : 'f';
+ (void)strcpy(p, ap + 1);
+ *argv = start;
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+"usage: uniq [-c | -d | -u] [-i] [-f fields] [-s chars] [input [output]]\n");
+ exit(1);
+}
diff --git a/text_cmds/unvis/unvis.1 b/text_cmds/unvis/unvis.1
new file mode 100644
index 0000000..21e3485
--- /dev/null
+++ b/text_cmds/unvis/unvis.1
@@ -0,0 +1,59 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)unvis.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/unvis/unvis.1,v 1.7 2002/04/20 12:17:45 charnier Exp $
+.\"
+.Dd June 6, 1993
+.Dt UNVIS 1
+.Os
+.Sh NAME
+.Nm unvis
+.Nd "revert a visual representation of data back to original form"
+.Sh SYNOPSIS
+.Nm
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is the inverse function of
+.Xr vis 1 .
+It reverts
+a visual representation of data back to its original form on standard output.
+.Sh SEE ALSO
+.Xr vis 1 ,
+.Xr unvis 3 ,
+.Xr vis 3
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/text_cmds/unvis/unvis.c b/text_cmds/unvis/unvis.c
new file mode 100644
index 0000000..713177b
--- /dev/null
+++ b/text_cmds/unvis/unvis.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1989, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD: src/usr.bin/unvis/unvis.c,v 1.9 2002/09/04 23:29:08 dwmalone Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vis.h>
+#include <sysexits.h>
+
+void process(FILE *, const char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch((char)ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ while (*argv) {
+ if ((fp=fopen(*argv, "r")) != NULL)
+ process(fp, *argv);
+ else
+ warn("%s", *argv);
+ argv++;
+ }
+ else
+ process(stdin, "<stdin>");
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: unvis [file ...]\n");
+ exit(1);
+}
+
+void
+process(FILE *fp, const char *filename)
+{
+ int offset = 0, c, ret;
+ int state = 0;
+ char outc;
+
+ while ((c = getc(fp)) != EOF) {
+ offset++;
+ again:
+ switch(ret = unvis(&outc, (char)c, &state, 0)) {
+ case UNVIS_VALID:
+ putchar(outc);
+ break;
+ case UNVIS_VALIDPUSH:
+ putchar(outc);
+ goto again;
+ case UNVIS_SYNBAD:
+ warnx("%s: offset: %d: can't decode", filename, offset);
+ state = 0;
+ break;
+ case 0:
+ case UNVIS_NOCHAR:
+ break;
+ default:
+ errx(1, "bad return value (%d), can't happen", ret);
+ }
+ }
+ if (unvis(&outc, (char)0, &state, UNVIS_END) == UNVIS_VALID)
+ putchar(outc);
+
+ if (ferror(fp))
+ errx(EX_IOERR, "Error reading %s", fp);
+}
diff --git a/text_cmds/vis/extern.h b/text_cmds/vis/extern.h
new file mode 100644
index 0000000..bc7de9b
--- /dev/null
+++ b/text_cmds/vis/extern.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1989, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.bin/vis/extern.h,v 1.2 2002/03/22 01:42:42 imp Exp $
+ */
+
+extern int foldit(char *, int, int);
diff --git a/text_cmds/vis/foldit.c b/text_cmds/vis/foldit.c
new file mode 100644
index 0000000..70cba4a
--- /dev/null
+++ b/text_cmds/vis/foldit.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/vis/foldit.c,v 1.6 2001/12/11 23:02:44 markm Exp $");
+
+#ifndef lint
+static const char sccsid[] = "@(#)foldit.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <stdio.h>
+
+#include "extern.h"
+
+int
+foldit(chunk, col, max)
+ char *chunk;
+ int col, max;
+{
+ char *cp;
+
+ /*
+ * Keep track of column position. Insert hidden newline
+ * if this chunk puts us over the limit.
+ */
+again:
+ cp = chunk;
+ while (*cp) {
+ switch(*cp) {
+ case '\n':
+ case '\r':
+ col = 0;
+ break;
+ case '\t':
+ col = (col + 8) &~ 07;
+ break;
+ case '\b':
+ col = col ? col - 1 : 0;
+ break;
+ default:
+ col++;
+ }
+ if (col > (max - 2)) {
+ printf("\\\n");
+ col = 0;
+ goto again;
+ }
+ cp++;
+ }
+ return (col);
+}
diff --git a/text_cmds/vis/vis.1 b/text_cmds/vis/vis.1
new file mode 100644
index 0000000..093e382
--- /dev/null
+++ b/text_cmds/vis/vis.1
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1989, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vis.1 8.4 (Berkeley) 4/19/94
+.\" $FreeBSD: src/usr.bin/vis/vis.1,v 1.12 2005/02/13 22:25:25 ru Exp $
+.\"
+.Dd June 25, 2004
+.Dt VIS 1
+.Os
+.Sh NAME
+.Nm vis
+.Nd display non-printable characters in a visual format
+.Sh SYNOPSIS
+.Nm
+.Op Fl cbflnostw
+.Op Fl F Ar foldwidth
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is a filter for converting non-printable characters
+into a visual representation.
+It differs from
+.Ql cat -v
+in that
+the form is unique and invertible.
+By default, all non-graphic
+characters except space, tab, and newline are encoded.
+A detailed description of the
+various visual formats is given in
+.Xr vis 3 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Turns off prepending of backslash before up-arrow control sequences
+and meta characters, and disables the doubling of backslashes.
+This
+produces output which is neither invertible or precise, but does
+represent a minimum of change to the input.
+It is similar to
+.Dq Li cat -v .
+.It Fl c
+Request a format which displays a small subset of the
+non-printable characters using C-style backslash sequences.
+.It Fl F
+Causes
+.Nm
+to fold output lines to foldwidth columns (default 80), like
+.Xr fold 1 ,
+except
+that a hidden newline sequence is used, (which is removed
+when inverting the file back to its original form with
+.Xr unvis 1 ) .
+If the last character in the encoded file does not end in a newline,
+a hidden newline sequence is appended to the output.
+This makes
+the output usable with various editors and other utilities which
+typically do not work with partial lines.
+.It Fl f
+Same as
+.Fl F .
+.It Fl l
+Mark newlines with the visible sequence
+.Ql \e$ ,
+followed by the newline.
+.It Fl n
+Turns off any encoding, except for the fact that backslashes are
+still doubled and hidden newline sequences inserted if
+.Fl f
+or
+.Fl F
+is selected.
+When combined with the
+.Fl f
+flag,
+.Nm
+becomes like
+an invertible version of the
+.Xr fold 1
+utility.
+That is, the output
+can be unfolded by running the output through
+.Xr unvis 1 .
+.It Fl o
+Request a format which displays non-printable characters as
+an octal number, \eddd.
+.It Fl s
+Only characters considered unsafe to send to a terminal are encoded.
+This flag allows backspace, bell, and carriage return in addition
+to the default space, tab and newline.
+.It Fl t
+Tabs are also encoded.
+.It Fl w
+White space (space-tab-newline) is also encoded.
+.El
+.Sh SEE ALSO
+.Xr unvis 1 ,
+.Xr vis 3
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
+.Sh BUGS
+Due to limitations in the underlying
+.Xr vis 3
+function, the
+.Nm
+utility
+does not recognize multibyte characters, and thus may consider them to be
+non-printable when they are in fact printable (and vice versa).
diff --git a/text_cmds/vis/vis.c b/text_cmds/vis/vis.c
new file mode 100644
index 0000000..703dec5
--- /dev/null
+++ b/text_cmds/vis/vis.c
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 1989, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/vis/vis.c,v 1.10 2002/09/04 23:29:09 dwmalone Exp $");
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char sccsid[] = "@(#)vis.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vis.h>
+#include <sysexits.h>
+
+#include "extern.h"
+
+int eflags, fold, foldwidth=80, none, markeol, debug;
+
+void process(FILE *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int ch;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "nwctsobfF:ld")) != -1)
+ switch((char)ch) {
+ case 'n':
+ none++;
+ break;
+ case 'w':
+ eflags |= VIS_WHITE;
+ break;
+ case 'c':
+ eflags |= VIS_CSTYLE;
+ break;
+ case 't':
+ eflags |= VIS_TAB;
+ break;
+ case 's':
+ eflags |= VIS_SAFE;
+ break;
+ case 'o':
+ eflags |= VIS_OCTAL;
+ break;
+ case 'b':
+ eflags |= VIS_NOSLASH;
+ break;
+ case 'F':
+ if ((foldwidth = atoi(optarg))<5)
+ errx(1, "can't fold lines to less than 5 cols");
+ /*FALLTHROUGH*/
+ case 'f':
+ fold++; /* fold output lines to 80 cols */
+ break; /* using hidden newline */
+ case 'l':
+ markeol++; /* mark end of line with \$ */
+ break;
+#ifdef DEBUG
+ case 'd':
+ debug++;
+ break;
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ while (*argv) {
+ if ((fp=fopen(*argv, "r")) != NULL)
+ process(fp);
+ else
+ warn("%s", *argv);
+ argv++;
+ }
+ else
+ process(stdin);
+ exit(0);
+}
+
+
+static void
+usage(void)
+{
+#ifdef DEBUG
+ fprintf(stderr, "usage: vis [-cbflnostwd] [-F foldwidth] [file ...]\n");
+#else
+ fprintf(stderr, "usage: vis [-cbflnostw] [-F foldwidth] [file ...]\n");
+#endif
+ exit(1);
+}
+
+void
+process(FILE *fp)
+{
+ static int col = 0;
+ static char dummy[] = "\0";
+ char *cp = dummy+1; /* so *(cp-1) starts out != '\n' */
+ int c, rachar;
+ char buff[5];
+
+ c = getc(fp);
+ while (c != EOF) {
+ rachar = getc(fp);
+ if (none) {
+ cp = buff;
+ *cp++ = c;
+ if (c == '\\')
+ *cp++ = '\\';
+ *cp = '\0';
+ } else if (markeol && c == '\n') {
+ cp = buff;
+ if ((eflags & VIS_NOSLASH) == 0)
+ *cp++ = '\\';
+ *cp++ = '$';
+ *cp++ = '\n';
+ *cp = '\0';
+ } else
+ (void) vis(buff, (char)c, eflags, (char)rachar);
+
+ cp = buff;
+ if (fold) {
+#ifdef DEBUG
+ if (debug)
+ printf("<%02d,", col);
+#endif
+ col = foldit(cp, col, foldwidth);
+#ifdef DEBUG
+ if (debug)
+ printf("%02d>", col);
+#endif
+ }
+ do {
+ putchar(*cp);
+ } while (*++cp);
+ c = rachar;
+ }
+ /*
+ * terminate partial line with a hidden newline
+ */
+ if (fold && *(cp-1) != '\n')
+ printf("\\\n");
+
+ if (ferror(fp))
+ errx(EX_IOERR, NULL);
+}
diff --git a/text_cmds/wc/wc.1 b/text_cmds/wc/wc.1
new file mode 100644
index 0000000..d0902e9
--- /dev/null
+++ b/text_cmds/wc/wc.1
@@ -0,0 +1,163 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)wc.1 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD: src/usr.bin/wc/wc.1,v 1.23 2005/02/26 04:14:20 trhodes Exp $
+.\"
+.Dd February 23, 2005
+.Dt WC 1
+.Os
+.Sh NAME
+.Nm wc
+.Nd word, line, character, and byte count
+.Sh SYNOPSIS
+.Nm
+.Op Fl clmw
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the number of lines, words, and bytes contained in each
+input
+.Ar file ,
+or standard input (if no file is specified) to the standard output.
+A line is defined as a string of characters delimited by a
+.Aq newline
+character.
+Characters beyond the final
+.Aq newline
+character will not be included
+in the line count.
+.Pp
+A word is defined as a string of characters delimited by white space
+characters.
+White space characters are the set of characters for which the
+.Xr iswspace 3
+function returns true.
+If more than one input file is specified, a line of cumulative counts
+for all the files is displayed on a separate line after the output for
+the last file.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+The number of bytes in each input file
+is written to the standard output.
+This will cancel out any prior usage of the
+.Fl m
+option.
+.It Fl l
+The number of lines in each input file
+is written to the standard output.
+.It Fl m
+The number of characters in each input file is written to the standard output.
+If the current locale does not support multibyte characters, this
+is equivalent to the
+.Fl c
+option.
+This will cancel out any prior usage of the
+.Fl c
+option.
+.It Fl w
+The number of words in each input file
+is written to the standard output.
+.El
+.Pp
+When an option is specified,
+.Nm
+only reports the information requested by that option.
+The order of output always takes the form of line, word,
+byte, and file name.
+The default action is equivalent to specifying the
+.Fl c , l
+and
+.Fl w
+options.
+.Pp
+If no files are specified, the standard input is used and no
+file name is displayed.
+The prompt will accept input until receiving EOF, or
+.Bq ^D
+in most environments.
+.Sh ENVIRONMENT
+The
+.Ev LANG , LC_ALL
+and
+.Ev LC_CTYPE
+environment variables affect the execution of
+.Nm
+as described in
+.Xr environ 7 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Count the number of characters, words and lines in each of the files
+.Pa report1
+and
+.Pa report2
+as well as the totals for both:
+.Pp
+.Dl "wc -mlw report1 report2"
+.Sh COMPATIBILITY
+Historically, the
+.Nm
+utility was documented to define a word as a ``maximal string of
+characters delimited by <space>, <tab> or <newline> characters''.
+The implementation, however, did not handle non-printing characters
+correctly so that
+.Dq Li " ^D^E "
+counted as 6 spaces, while
+.Dq Li foo^D^Ebar
+counted as 8 characters.
+.Bx 4
+systems after
+.Bx 4.3
+modified the implementation to be consistent
+with the documentation.
+This implementation defines a ``word'' in terms of the
+.Xr iswspace 3
+function, as required by
+.St -p1003.2 .
+.Sh SEE ALSO
+.Xr iswspace 3
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/text_cmds/wc/wc.c b/text_cmds/wc/wc.c
new file mode 100644
index 0000000..cb25729
--- /dev/null
+++ b/text_cmds/wc/wc.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1980, 1987, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/wc/wc.c,v 1.21 2004/12/27 22:27:56 josef Exp $");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+/* We allocte this much memory statically, and use it as a fallback for
+ malloc failure, or statfs failure. So it should be small, but not
+ "too small" */
+#define SMALL_BUF_SIZE (1024 * 8)
+
+uintmax_t tlinect, twordct, tcharct;
+int doline, doword, dochar, domulti;
+
+static int cnt(const char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, errors, total;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "clmw")) != -1)
+ switch((char)ch) {
+ case 'l':
+ doline = 1;
+ break;
+ case 'w':
+ doword = 1;
+ break;
+ case 'c':
+ dochar = 1;
+ domulti = 0;
+ break;
+ case 'm':
+ domulti = 1;
+ dochar = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ /* Wc's flags are on by default. */
+ if (doline + doword + dochar + domulti == 0)
+ doline = doword = dochar = 1;
+
+ errors = 0;
+ total = 0;
+ if (!*argv) {
+ if (cnt((char *)NULL) != 0)
+ ++errors;
+ else
+ (void)printf("\n");
+ }
+ else do {
+ if (cnt(*argv) != 0)
+ ++errors;
+ else
+ (void)printf(" %s\n", *argv);
+ ++total;
+ } while(*++argv);
+
+ if (total > 1) {
+ if (doline)
+ (void)printf(" %7ju", tlinect);
+ if (doword)
+ (void)printf(" %7ju", twordct);
+ if (dochar || domulti)
+ (void)printf(" %7ju", tcharct);
+ (void)printf(" total\n");
+ }
+ exit(errors == 0 ? 0 : 1);
+}
+
+static int
+cnt(const char *file)
+{
+ struct stat sb;
+ struct statfs fsb;
+ uintmax_t linect, wordct, charct;
+ int fd, len, warned;
+ int stat_ret;
+ size_t clen;
+ short gotsp;
+ u_char *p;
+ static u_char small_buf[SMALL_BUF_SIZE];
+ static u_char *buf = small_buf;
+ static off_t buf_size = SMALL_BUF_SIZE;
+ wchar_t wch;
+ mbstate_t mbs;
+
+ linect = wordct = charct = 0;
+ if (file == NULL) {
+ file = "stdin";
+ fd = STDIN_FILENO;
+ } else {
+ if ((fd = open(file, O_RDONLY, 0)) < 0) {
+ warn("%s: open", file);
+ return (1);
+ }
+ }
+
+ if (fstatfs(fd, &fsb)) {
+ fsb.f_iosize = SMALL_BUF_SIZE;
+ }
+ if (fsb.f_iosize != buf_size) {
+ if (buf != small_buf) {
+ free(buf);
+ }
+ if (fsb.f_iosize == SMALL_BUF_SIZE || !(buf = malloc(fsb.f_iosize))) {
+ buf = small_buf;
+ buf_size = SMALL_BUF_SIZE;
+ } else {
+ buf_size = fsb.f_iosize;
+ }
+ }
+
+ if (doword || (domulti && MB_CUR_MAX != 1))
+ goto word;
+ /*
+ * Line counting is split out because it's a lot faster to get
+ * lines than to get words, since the word count requires some
+ * logic.
+ */
+ if (doline) {
+ while ((len = read(fd, buf, buf_size))) {
+ if (len == -1) {
+ warn("%s: read", file);
+ (void)close(fd);
+ return (1);
+ }
+ charct += len;
+ for (p = buf; len--; ++p)
+ if (*p == '\n')
+ ++linect;
+ }
+ tlinect += linect;
+ (void)printf(" %7ju", linect);
+ if (dochar) {
+ tcharct += charct;
+ (void)printf(" %7ju", charct);
+ }
+ (void)close(fd);
+ return (0);
+ }
+ /*
+ * If all we need is the number of characters and it's a
+ * regular file, just stat the puppy.
+ */
+ if (dochar || domulti) {
+ if (fstat(fd, &sb)) {
+ warn("%s: fstat", file);
+ (void)close(fd);
+ return (1);
+ }
+ if (S_ISREG(sb.st_mode)) {
+ (void)printf(" %7lld", (long long)sb.st_size);
+ tcharct += sb.st_size;
+ (void)close(fd);
+ return (0);
+ }
+ }
+
+ /* Do it the hard way... */
+word: gotsp = 1;
+ warned = 0;
+ memset(&mbs, 0, sizeof(mbs));
+ while ((len = read(fd, buf, buf_size)) != 0) {
+ if (len == -1) {
+ warn("%s: read", file);
+ (void)close(fd);
+ return (1);
+ }
+ p = buf;
+ while (len > 0) {
+ if (!domulti || MB_CUR_MAX == 1) {
+ clen = 1;
+ wch = (unsigned char)*p;
+ } else if ((clen = mbrtowc(&wch, p, len, &mbs)) ==
+ (size_t)-1) {
+ if (!warned) {
+ errno = EILSEQ;
+ warn("%s", file);
+ warned = 1;
+ }
+ memset(&mbs, 0, sizeof(mbs));
+ clen = 1;
+ wch = (unsigned char)*p;
+ } else if (clen == (size_t)-2)
+ break;
+ else if (clen == 0)
+ clen = 1;
+ charct++;
+ len -= clen;
+ p += clen;
+ if (wch == L'\n')
+ ++linect;
+ if (iswspace(wch))
+ gotsp = 1;
+ else if (gotsp) {
+ gotsp = 0;
+ ++wordct;
+ }
+ }
+ }
+ if (domulti && MB_CUR_MAX > 1)
+ if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned)
+ warn("%s", file);
+ if (doline) {
+ tlinect += linect;
+ (void)printf(" %7ju", linect);
+ }
+ if (doword) {
+ twordct += wordct;
+ (void)printf(" %7ju", wordct);
+ }
+ if (dochar || domulti) {
+ tcharct += charct;
+ (void)printf(" %7ju", charct);
+ }
+ (void)close(fd);
+ return (0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: wc [-clmw] [file ...]\n");
+ exit(1);
+}
diff --git a/text_cmds/xcconfigs/base.xcconfig b/text_cmds/xcconfigs/base.xcconfig
new file mode 100644
index 0000000..72c716a
--- /dev/null
+++ b/text_cmds/xcconfigs/base.xcconfig
@@ -0,0 +1,59 @@
+// Build Options
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
+
+// Code Signing
+CODE_SIGN_IDENTITY = -
+
+// Deployment
+COPY_PHASE_STRIP = YES
+
+// Packaging
+APPLY_RULES_IN_COPY_FILES = YES
+
+// Search Paths
+ALWAYS_SEARCH_USER_PATHS = NO
+USE_HEADERMAP = NO
+
+// Versioning
+CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
+VERSIONING_SYSTEM = apple-generic
+VERSION_INFO_PREFIX = __
+
+// Warning Policies
+GCC_TREAT_WARNINGS_AS_ERRORS = YES
+
+// Warnings - All languages
+CLANG_WARN_ASSIGN_ENUM = YES
+CLANG_WARN_BOOL_CONVERSION = YES
+CLANG_WARN_CONSTANT_CONVERSION = YES
+CLANG_WARN_DOCUMENTATION_COMMENTS = YES
+CLANG_WARN_EMPTY_BODY = YES
+CLANG_WARN_ENUM_CONVERSION = YES
+CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
+CLANG_WARN_INT_CONVERSION = YES
+CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES
+CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
+GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
+GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES
+GCC_WARN_64_TO_32_BIT_CONVERSION = YES
+GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES
+GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
+GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES
+GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
+GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
+GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES
+GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
+GCC_WARN_MISSING_PARENTHESES = YES
+GCC_WARN_SHADOW = YES
+GCC_WARN_SIGN_COMPARE = YES
+GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES
+GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
+GCC_WARN_UNKNOWN_PRAGMAS = YES
+GCC_WARN_UNUSED_FUNCTION = YES
+GCC_WARN_UNUSED_LABEL = YES
+GCC_WARN_UNUSED_PARAMETER = YES
+GCC_WARN_UNUSED_VALUE = YES
+GCC_WARN_UNUSED_VARIABLE = YES
diff --git a/text_cmds/xcconfigs/ee.xcconfig b/text_cmds/xcconfigs/ee.xcconfig
new file mode 100644
index 0000000..348c96a
--- /dev/null
+++ b/text_cmds/xcconfigs/ee.xcconfig
@@ -0,0 +1,27 @@
+#include "base.xcconfig"
+
+EE_PREFIX = /usr/local
+//EE_PREFIX[sdk=macosx*] = /usr
+
+// Deployment
+INSTALL_PATH = $(EE_PREFIX)/bin
+
+// Linking
+OTHER_LDFLAGS = -lncurses
+
+// Packaging
+PRODUCT_NAME = ee
+
+// Preprocessing
+GCC_PREPROCESSOR_DEFINITIONS = HAS_NCURSES HAS_UNISTD HAS_STDARG HAS_STDLIB HAS_SYS_WAIT NO_CATGETS
+
+// Warnings - All languages
+CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO
+CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO
+GCC_WARN_64_TO_32_BIT_CONVERSION = NO
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO
+GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO
+GCC_WARN_SHADOW = NO
+GCC_WARN_SIGN_COMPARE = NO
+GCC_WARN_UNINITIALIZED_AUTOS = NO
+GCC_WARN_UNUSED_PARAMETER = NO
diff --git a/text_cmds/xcconfigs/grep.xcconfig b/text_cmds/xcconfigs/grep.xcconfig
new file mode 100644
index 0000000..648bd86
--- /dev/null
+++ b/text_cmds/xcconfigs/grep.xcconfig
@@ -0,0 +1,19 @@
+#include "base.xcconfig"
+
+// Architectures
+SDKROOT = macosx.internal
+
+// Linking
+OTHER_LDFLAGS = -lbz2 -llzma -lz
+
+// Packaging
+PRODUCT_NAME = grep
+
+// Preprocessing
+GCC_PREPROCESSOR_DEFINITIONS = WITHOUT_FASTMATCH WITHOUT_NLS
+
+// Warnings - All languages
+CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO
+GCC_WARN_64_TO_32_BIT_CONVERSION = NO
+GCC_WARN_SHADOW = NO
+GCC_WARN_SIGN_COMPARE = NO
diff --git a/text_cmds/xcconfigs/sort.xcconfig b/text_cmds/xcconfigs/sort.xcconfig
new file mode 100644
index 0000000..e36bb00
--- /dev/null
+++ b/text_cmds/xcconfigs/sort.xcconfig
@@ -0,0 +1,15 @@
+#include "base.xcconfig"
+
+// Packaging
+PRODUCT_NAME = sort
+
+// Preprocessing
+GCC_PREPROCESSOR_DEFINITIONS = SORT_VERSION=\"$(RC_ProjectSourceVersion)\" WITHOUT_NLS SORT_THREADS
+
+// Warnings - All languages
+CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO
+CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO
+GCC_WARN_64_TO_32_BIT_CONVERSION = NO
+GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO
+GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO
+GCC_WARN_UNINITIALIZED_AUTOS = YES
diff --git a/text_cmds/xcodescripts/grep_variant_links.sh b/text_cmds/xcodescripts/grep_variant_links.sh
new file mode 100644
index 0000000..018a63a
--- /dev/null
+++ b/text_cmds/xcodescripts/grep_variant_links.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# This script phase cannot be run in the "grep" target itself, because Strip/CodeSign/etc are
+# after all other phases. Running it in the aggregate target guarantees that the grep variants
+# are really linked to the actual stripped/signed grep binary.
+#
+
+set -ex
+
+for variant in e f z ze zf bz bze bzf; do
+ ln ${DSTROOT}/usr/bin/grep ${DSTROOT}/usr/bin/${variant}grep
+ ln ${DSTROOT}/usr/share/man/man1/grep.1 ${DSTROOT}/usr/share/man/man1/${variant}grep.1
+done
diff --git a/text_cmds/xcodescripts/install-opensource.sh b/text_cmds/xcodescripts/install-opensource.sh
new file mode 100644
index 0000000..f8f80a1
--- /dev/null
+++ b/text_cmds/xcodescripts/install-opensource.sh
@@ -0,0 +1,9 @@
+set -e -x
+
+OSV="$DSTROOT"/usr/local/OpenSourceVersions
+OSL="$DSTROOT"/usr/local/OpenSourceLicenses
+
+install -d -o root -g wheel -m 0755 "$OSV"
+install -c -o root -g wheel -m 0644 \
+ "$SRCROOT"/text_cmds.plist \
+ "$OSV"
diff --git a/text_cmds/xcodescripts/install-sort-man.sh b/text_cmds/xcodescripts/install-sort-man.sh
new file mode 100644
index 0000000..2b76e68
--- /dev/null
+++ b/text_cmds/xcodescripts/install-sort-man.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+mkdir -p ${DSTROOT}/usr/share/man/man1
+sed -e 's|^%%THREADS%%||' -e 's|^%%NLS%%|\.\\"|' < ${SRCROOT}/sort/sort.1.in > ${DSTROOT}/usr/share/man/man1/sort.1