From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- text_cmds/.upstream_base_commits | 10 + text_cmds/banner/banner.6 | 82 + text_cmds/banner/banner.c | 1186 +++++ text_cmds/cat/cat.1 | 201 + text_cmds/cat/cat.c | 314 ++ text_cmds/col/README | 48 + text_cmds/col/col.1 | 156 + text_cmds/col/col.c | 552 ++ text_cmds/colrm/colrm.1 | 91 + text_cmds/colrm/colrm.c | 146 + text_cmds/column/column.1 | 101 + text_cmds/column/column.c | 334 ++ text_cmds/comm/comm.1 | 124 + text_cmds/comm/comm.c | 220 + text_cmds/csplit/csplit.1 | 157 + text_cmds/csplit/csplit.c | 467 ++ text_cmds/cut/cut.1 | 166 + text_cmds/cut/cut.c | 468 ++ text_cmds/ed/POSIX | 101 + text_cmds/ed/README | 24 + text_cmds/ed/buf.c | 284 ++ text_cmds/ed/cbc.c | 402 ++ text_cmds/ed/ed.1 | 1004 ++++ text_cmds/ed/ed.h | 282 ++ text_cmds/ed/glbl.c | 218 + text_cmds/ed/io.c | 341 ++ text_cmds/ed/main.c | 1455 ++++++ text_cmds/ed/re.c | 132 + text_cmds/ed/red.1 | 1 + text_cmds/ed/sub.c | 256 + text_cmds/ed/test/=.err | 1 + text_cmds/ed/test/README | 32 + text_cmds/ed/test/TODO | 15 + text_cmds/ed/test/a.d | 5 + text_cmds/ed/test/a.r | 8 + text_cmds/ed/test/a.t | 9 + text_cmds/ed/test/a1.err | 3 + text_cmds/ed/test/a2.err | 3 + text_cmds/ed/test/addr.d | 9 + text_cmds/ed/test/addr.r | 2 + text_cmds/ed/test/addr.t | 5 + text_cmds/ed/test/addr1.err | 1 + text_cmds/ed/test/addr2.err | 1 + text_cmds/ed/test/ascii.d.uu | 9 + text_cmds/ed/test/ascii.r.uu | 9 + text_cmds/ed/test/ascii.t | 0 text_cmds/ed/test/bang1.d | 0 text_cmds/ed/test/bang1.err | 1 + text_cmds/ed/test/bang1.r | 1 + text_cmds/ed/test/bang1.t | 5 + text_cmds/ed/test/bang2.err | 1 + text_cmds/ed/test/c.d | 5 + text_cmds/ed/test/c.r | 4 + text_cmds/ed/test/c.t | 12 + text_cmds/ed/test/c1.err | 3 + text_cmds/ed/test/c2.err | 3 + text_cmds/ed/test/ckscripts.sh | 37 + text_cmds/ed/test/d.d | 5 + text_cmds/ed/test/d.err | 1 + text_cmds/ed/test/d.r | 1 + text_cmds/ed/test/d.t | 3 + text_cmds/ed/test/e1.d | 1 + text_cmds/ed/test/e1.err | 1 + text_cmds/ed/test/e1.r | 1 + text_cmds/ed/test/e1.t | 1 + text_cmds/ed/test/e2.d | 1 + text_cmds/ed/test/e2.err | 1 + text_cmds/ed/test/e2.r | 1 + text_cmds/ed/test/e2.t | 1 + text_cmds/ed/test/e3.d | 1 + text_cmds/ed/test/e3.err | 1 + text_cmds/ed/test/e3.r | 1 + text_cmds/ed/test/e3.t | 1 + text_cmds/ed/test/e4.d | 1 + text_cmds/ed/test/e4.r | 1 + text_cmds/ed/test/e4.t | 1 + text_cmds/ed/test/f1.err | 1 + text_cmds/ed/test/f2.err | 1 + text_cmds/ed/test/g1.d | 5 + text_cmds/ed/test/g1.err | 1 + text_cmds/ed/test/g1.r | 15 + text_cmds/ed/test/g1.t | 6 + text_cmds/ed/test/g2.d | 5 + text_cmds/ed/test/g2.err | 1 + text_cmds/ed/test/g2.r | 1 + text_cmds/ed/test/g2.t | 2 + text_cmds/ed/test/g3.d | 5 + text_cmds/ed/test/g3.err | 1 + text_cmds/ed/test/g3.r | 5 + text_cmds/ed/test/g3.t | 4 + text_cmds/ed/test/g4.d | 5 + text_cmds/ed/test/g4.r | 7 + text_cmds/ed/test/g4.t | 13 + text_cmds/ed/test/g5.d | 3 + text_cmds/ed/test/g5.r | 9 + text_cmds/ed/test/g5.t | 2 + text_cmds/ed/test/h.err | 1 + text_cmds/ed/test/i.d | 5 + text_cmds/ed/test/i.r | 8 + text_cmds/ed/test/i.t | 9 + text_cmds/ed/test/i1.err | 3 + text_cmds/ed/test/i2.err | 3 + text_cmds/ed/test/i3.err | 3 + text_cmds/ed/test/j.d | 5 + text_cmds/ed/test/j.r | 4 + text_cmds/ed/test/j.t | 2 + text_cmds/ed/test/k.d | 5 + text_cmds/ed/test/k.r | 5 + text_cmds/ed/test/k.t | 10 + text_cmds/ed/test/k1.err | 1 + text_cmds/ed/test/k2.err | 1 + text_cmds/ed/test/k3.err | 1 + text_cmds/ed/test/k4.err | 6 + text_cmds/ed/test/l.d | 0 text_cmds/ed/test/l.r | 0 text_cmds/ed/test/l.t | 0 text_cmds/ed/test/m.d | 5 + text_cmds/ed/test/m.err | 4 + text_cmds/ed/test/m.r | 5 + text_cmds/ed/test/m.t | 7 + text_cmds/ed/test/mkscripts.sh | 75 + text_cmds/ed/test/n.d | 0 text_cmds/ed/test/n.r | 0 text_cmds/ed/test/n.t | 0 text_cmds/ed/test/nl.err | 1 + text_cmds/ed/test/nl1.d | 5 + text_cmds/ed/test/nl1.r | 8 + text_cmds/ed/test/nl1.t | 8 + text_cmds/ed/test/nl2.d | 5 + text_cmds/ed/test/nl2.r | 6 + text_cmds/ed/test/nl2.t | 4 + text_cmds/ed/test/p.d | 0 text_cmds/ed/test/p.r | 0 text_cmds/ed/test/p.t | 0 text_cmds/ed/test/q.d | 0 text_cmds/ed/test/q.r | 0 text_cmds/ed/test/q.t | 5 + text_cmds/ed/test/q1.err | 1 + text_cmds/ed/test/r1.d | 5 + text_cmds/ed/test/r1.err | 1 + text_cmds/ed/test/r1.r | 7 + text_cmds/ed/test/r1.t | 3 + text_cmds/ed/test/r2.d | 5 + text_cmds/ed/test/r2.err | 1 + text_cmds/ed/test/r2.r | 10 + text_cmds/ed/test/r2.t | 1 + text_cmds/ed/test/r3.d | 1 + text_cmds/ed/test/r3.r | 2 + text_cmds/ed/test/r3.t | 1 + text_cmds/ed/test/s1.d | 5 + text_cmds/ed/test/s1.err | 1 + text_cmds/ed/test/s1.r | 5 + text_cmds/ed/test/s1.t | 6 + text_cmds/ed/test/s10.err | 4 + text_cmds/ed/test/s2.d | 5 + text_cmds/ed/test/s2.err | 4 + text_cmds/ed/test/s2.r | 5 + text_cmds/ed/test/s2.t | 4 + text_cmds/ed/test/s3.d | 0 text_cmds/ed/test/s3.err | 1 + text_cmds/ed/test/s3.r | 1 + text_cmds/ed/test/s3.t | 6 + text_cmds/ed/test/s4.err | 1 + text_cmds/ed/test/s5.err | 1 + text_cmds/ed/test/s6.err | 1 + text_cmds/ed/test/s7.err | 5 + text_cmds/ed/test/s8.err | 4 + text_cmds/ed/test/s9.err | 4 + text_cmds/ed/test/t.d | 5 + text_cmds/ed/test/t.r | 16 + text_cmds/ed/test/t1.d | 5 + text_cmds/ed/test/t1.err | 1 + text_cmds/ed/test/t1.r | 16 + text_cmds/ed/test/t1.t | 3 + text_cmds/ed/test/t2.d | 5 + text_cmds/ed/test/t2.err | 1 + text_cmds/ed/test/t2.r | 6 + text_cmds/ed/test/t2.t | 1 + text_cmds/ed/test/u.d | 5 + text_cmds/ed/test/u.err | 1 + text_cmds/ed/test/u.r | 9 + text_cmds/ed/test/u.t | 31 + text_cmds/ed/test/v.d | 5 + text_cmds/ed/test/v.r | 11 + text_cmds/ed/test/v.t | 6 + text_cmds/ed/test/w.d | 5 + text_cmds/ed/test/w.r | 10 + text_cmds/ed/test/w.t | 2 + text_cmds/ed/test/w1.err | 1 + text_cmds/ed/test/w2.err | 1 + text_cmds/ed/test/w3.err | 1 + text_cmds/ed/test/x.err | 1 + text_cmds/ed/test/z.err | 2 + text_cmds/ed/undo.c | 150 + text_cmds/ee/Changes | 40 + text_cmds/ee/Makefile | 29 + text_cmds/ee/README.ee | 119 + text_cmds/ee/create.make | 292 ++ text_cmds/ee/ee.1 | 543 ++ text_cmds/ee/ee.c | 5348 ++++++++++++++++++++ text_cmds/ee/ee.i18n.guide | 158 + text_cmds/ee/ee.msg | 186 + text_cmds/ee/ee_version.h | 6 + text_cmds/ee/genstr | 32 + text_cmds/ee/make.default | 57 + text_cmds/ee/new_curse.c | 3819 ++++++++++++++ text_cmds/ee/new_curse.h | 260 + text_cmds/expand/expand.1 | 118 + text_cmds/expand/expand.c | 200 + text_cmds/expand/xcodescripts/link-man-pages.sh | 3 + text_cmds/fmt/fmt.1 | 196 + text_cmds/fmt/fmt.c | 668 +++ text_cmds/fold/fold.1 | 90 + text_cmds/fold/fold.c | 228 + text_cmds/grep/file.c | 347 ++ text_cmds/grep/grep.1 | 500 ++ text_cmds/grep/grep.c | 783 +++ text_cmds/grep/grep.h | 161 + text_cmds/grep/queue.c | 107 + text_cmds/grep/util.c | 599 +++ text_cmds/head/head.1 | 69 + text_cmds/head/head.c | 199 + text_cmds/join/join.1 | 235 + text_cmds/join/join.c | 685 +++ text_cmds/lam/lam.1 | 141 + text_cmds/lam/lam.c | 234 + text_cmds/look/look.1 | 124 + text_cmds/look/look.c | 357 ++ text_cmds/look/pathnames.h | 36 + text_cmds/md5/commoncrypto.c | 108 + text_cmds/md5/commoncrypto.h | 8 + text_cmds/md5/md5.1 | 95 + text_cmds/md5/md5.c | 419 ++ text_cmds/nl/nl.1 | 253 + text_cmds/nl/nl.c | 439 ++ text_cmds/paste/paste.1 | 150 + text_cmds/paste/paste.c | 280 + text_cmds/pr/egetopt.c | 218 + text_cmds/pr/extern.h | 61 + text_cmds/pr/pr.1 | 402 ++ text_cmds/pr/pr.c | 1856 +++++++ text_cmds/pr/pr.h | 74 + text_cmds/rev/rev.1 | 49 + text_cmds/rev/rev.c | 118 + text_cmds/rs/rs.1 | 239 + text_cmds/rs/rs.c | 553 ++ text_cmds/sed/POSIX | 204 + text_cmds/sed/TEST/hanoi.sed | 103 + text_cmds/sed/TEST/math.sed | 439 ++ text_cmds/sed/TEST/sed.test | 556 ++ text_cmds/sed/compile.c | 987 ++++ text_cmds/sed/defs.h | 151 + text_cmds/sed/extern.h | 60 + text_cmds/sed/main.c | 547 ++ text_cmds/sed/misc.c | 73 + text_cmds/sed/process.c | 791 +++ text_cmds/sed/sed.1 | 668 +++ text_cmds/sort/bwstring.c | 1142 +++++ text_cmds/sort/bwstring.h | 142 + text_cmds/sort/coll.c | 1324 +++++ text_cmds/sort/coll.h | 168 + text_cmds/sort/commoncrypto.c | 98 + text_cmds/sort/commoncrypto.h | 8 + text_cmds/sort/file.c | 1684 ++++++ text_cmds/sort/file.h | 126 + text_cmds/sort/mem.c | 81 + text_cmds/sort/mem.h | 45 + text_cmds/sort/nls/C.msg | 16 + text_cmds/sort/nls/hu_HU.ISO8859-2.msg | 16 + text_cmds/sort/radixsort.c | 746 +++ text_cmds/sort/radixsort.h | 38 + text_cmds/sort/sort.1.in | 639 +++ text_cmds/sort/sort.c | 1325 +++++ text_cmds/sort/sort.h | 139 + text_cmds/sort/testsuite/README.txt | 19 + text_cmds/sort/testsuite/bigsample.txt.xz | Bin 0 -> 50904 bytes text_cmds/sort/testsuite/run.sh | 436 ++ text_cmds/sort/testsuite/sample.txt | Bin 0 -> 1672 bytes text_cmds/sort/vsort.c | 265 + text_cmds/sort/vsort.h | 37 + text_cmds/split/split.1 | 142 + text_cmds/split/split.c | 342 ++ text_cmds/tail/extern.h | 76 + text_cmds/tail/forward.c | 520 ++ text_cmds/tail/misc.c | 119 + text_cmds/tail/read.c | 214 + text_cmds/tail/reverse.c | 287 ++ text_cmds/tail/tail.1 | 188 + text_cmds/tail/tail.c | 349 ++ text_cmds/tests/Makefile | 6 + text_cmds/tests/sort_vers.c | 23 + text_cmds/text_cmds.plist | 607 +++ text_cmds/text_cmds.xcodeproj/project.pbxproj | 3890 ++++++++++++++ .../project.xcworkspace/contents.xcworkspacedata | 7 + text_cmds/tr/cmap.c | 212 + text_cmds/tr/cmap.h | 83 + text_cmds/tr/cset.c | 290 ++ text_cmds/tr/cset.h | 74 + text_cmds/tr/extern.h | 55 + text_cmds/tr/str.c | 450 ++ text_cmds/tr/tr.1 | 420 ++ text_cmds/tr/tr.c | 378 ++ text_cmds/ul/ul.1 | 104 + text_cmds/ul/ul.c | 569 +++ text_cmds/unexpand/unexpand.c | 231 + text_cmds/uniq/uniq.1 | 151 + text_cmds/uniq/uniq.c | 367 ++ text_cmds/unvis/unvis.1 | 59 + text_cmds/unvis/unvis.c | 126 + text_cmds/vis/extern.h | 36 + text_cmds/vis/foldit.c | 82 + text_cmds/vis/vis.1 | 140 + text_cmds/vis/vis.c | 194 + text_cmds/wc/wc.1 | 163 + text_cmds/wc/wc.c | 294 ++ text_cmds/xcconfigs/base.xcconfig | 59 + text_cmds/xcconfigs/ee.xcconfig | 27 + text_cmds/xcconfigs/grep.xcconfig | 19 + text_cmds/xcconfigs/sort.xcconfig | 15 + text_cmds/xcodescripts/grep_variant_links.sh | 13 + text_cmds/xcodescripts/install-opensource.sh | 9 + text_cmds/xcodescripts/install-sort-man.sh | 3 + 322 files changed, 56992 insertions(+) create mode 100644 text_cmds/.upstream_base_commits create mode 100644 text_cmds/banner/banner.6 create mode 100644 text_cmds/banner/banner.c create mode 100644 text_cmds/cat/cat.1 create mode 100644 text_cmds/cat/cat.c create mode 100644 text_cmds/col/README create mode 100644 text_cmds/col/col.1 create mode 100644 text_cmds/col/col.c create mode 100644 text_cmds/colrm/colrm.1 create mode 100644 text_cmds/colrm/colrm.c create mode 100644 text_cmds/column/column.1 create mode 100644 text_cmds/column/column.c create mode 100644 text_cmds/comm/comm.1 create mode 100644 text_cmds/comm/comm.c create mode 100644 text_cmds/csplit/csplit.1 create mode 100644 text_cmds/csplit/csplit.c create mode 100644 text_cmds/cut/cut.1 create mode 100644 text_cmds/cut/cut.c create mode 100644 text_cmds/ed/POSIX create mode 100644 text_cmds/ed/README create mode 100644 text_cmds/ed/buf.c create mode 100644 text_cmds/ed/cbc.c create mode 100644 text_cmds/ed/ed.1 create mode 100644 text_cmds/ed/ed.h create mode 100644 text_cmds/ed/glbl.c create mode 100644 text_cmds/ed/io.c create mode 100644 text_cmds/ed/main.c create mode 100644 text_cmds/ed/re.c create mode 100644 text_cmds/ed/red.1 create mode 100644 text_cmds/ed/sub.c create mode 100644 text_cmds/ed/test/=.err create mode 100644 text_cmds/ed/test/README create mode 100644 text_cmds/ed/test/TODO create mode 100644 text_cmds/ed/test/a.d create mode 100644 text_cmds/ed/test/a.r create mode 100644 text_cmds/ed/test/a.t create mode 100644 text_cmds/ed/test/a1.err create mode 100644 text_cmds/ed/test/a2.err create mode 100644 text_cmds/ed/test/addr.d create mode 100644 text_cmds/ed/test/addr.r create mode 100644 text_cmds/ed/test/addr.t create mode 100644 text_cmds/ed/test/addr1.err create mode 100644 text_cmds/ed/test/addr2.err create mode 100644 text_cmds/ed/test/ascii.d.uu create mode 100644 text_cmds/ed/test/ascii.r.uu create mode 100644 text_cmds/ed/test/ascii.t create mode 100644 text_cmds/ed/test/bang1.d create mode 100644 text_cmds/ed/test/bang1.err create mode 100644 text_cmds/ed/test/bang1.r create mode 100644 text_cmds/ed/test/bang1.t create mode 100644 text_cmds/ed/test/bang2.err create mode 100644 text_cmds/ed/test/c.d create mode 100644 text_cmds/ed/test/c.r create mode 100644 text_cmds/ed/test/c.t create mode 100644 text_cmds/ed/test/c1.err create mode 100644 text_cmds/ed/test/c2.err create mode 100755 text_cmds/ed/test/ckscripts.sh create mode 100644 text_cmds/ed/test/d.d create mode 100644 text_cmds/ed/test/d.err create mode 100644 text_cmds/ed/test/d.r create mode 100644 text_cmds/ed/test/d.t create mode 100644 text_cmds/ed/test/e1.d create mode 100644 text_cmds/ed/test/e1.err create mode 100644 text_cmds/ed/test/e1.r create mode 100644 text_cmds/ed/test/e1.t create mode 100644 text_cmds/ed/test/e2.d create mode 100644 text_cmds/ed/test/e2.err create mode 100644 text_cmds/ed/test/e2.r create mode 100644 text_cmds/ed/test/e2.t create mode 100644 text_cmds/ed/test/e3.d create mode 100644 text_cmds/ed/test/e3.err create mode 100644 text_cmds/ed/test/e3.r create mode 100644 text_cmds/ed/test/e3.t create mode 100644 text_cmds/ed/test/e4.d create mode 100644 text_cmds/ed/test/e4.r create mode 100644 text_cmds/ed/test/e4.t create mode 100644 text_cmds/ed/test/f1.err create mode 100644 text_cmds/ed/test/f2.err create mode 100644 text_cmds/ed/test/g1.d create mode 100644 text_cmds/ed/test/g1.err create mode 100644 text_cmds/ed/test/g1.r create mode 100644 text_cmds/ed/test/g1.t create mode 100644 text_cmds/ed/test/g2.d create mode 100644 text_cmds/ed/test/g2.err create mode 100644 text_cmds/ed/test/g2.r create mode 100644 text_cmds/ed/test/g2.t create mode 100644 text_cmds/ed/test/g3.d create mode 100644 text_cmds/ed/test/g3.err create mode 100644 text_cmds/ed/test/g3.r create mode 100644 text_cmds/ed/test/g3.t create mode 100644 text_cmds/ed/test/g4.d create mode 100644 text_cmds/ed/test/g4.r create mode 100644 text_cmds/ed/test/g4.t create mode 100644 text_cmds/ed/test/g5.d create mode 100644 text_cmds/ed/test/g5.r create mode 100644 text_cmds/ed/test/g5.t create mode 100644 text_cmds/ed/test/h.err create mode 100644 text_cmds/ed/test/i.d create mode 100644 text_cmds/ed/test/i.r create mode 100644 text_cmds/ed/test/i.t create mode 100644 text_cmds/ed/test/i1.err create mode 100644 text_cmds/ed/test/i2.err create mode 100644 text_cmds/ed/test/i3.err create mode 100644 text_cmds/ed/test/j.d create mode 100644 text_cmds/ed/test/j.r create mode 100644 text_cmds/ed/test/j.t create mode 100644 text_cmds/ed/test/k.d create mode 100644 text_cmds/ed/test/k.r create mode 100644 text_cmds/ed/test/k.t create mode 100644 text_cmds/ed/test/k1.err create mode 100644 text_cmds/ed/test/k2.err create mode 100644 text_cmds/ed/test/k3.err create mode 100644 text_cmds/ed/test/k4.err create mode 100644 text_cmds/ed/test/l.d create mode 100644 text_cmds/ed/test/l.r create mode 100644 text_cmds/ed/test/l.t create mode 100644 text_cmds/ed/test/m.d create mode 100644 text_cmds/ed/test/m.err create mode 100644 text_cmds/ed/test/m.r create mode 100644 text_cmds/ed/test/m.t create mode 100755 text_cmds/ed/test/mkscripts.sh create mode 100644 text_cmds/ed/test/n.d create mode 100644 text_cmds/ed/test/n.r create mode 100644 text_cmds/ed/test/n.t create mode 100644 text_cmds/ed/test/nl.err create mode 100644 text_cmds/ed/test/nl1.d create mode 100644 text_cmds/ed/test/nl1.r create mode 100644 text_cmds/ed/test/nl1.t create mode 100644 text_cmds/ed/test/nl2.d create mode 100644 text_cmds/ed/test/nl2.r create mode 100644 text_cmds/ed/test/nl2.t create mode 100644 text_cmds/ed/test/p.d create mode 100644 text_cmds/ed/test/p.r create mode 100644 text_cmds/ed/test/p.t create mode 100644 text_cmds/ed/test/q.d create mode 100644 text_cmds/ed/test/q.r create mode 100644 text_cmds/ed/test/q.t create mode 100644 text_cmds/ed/test/q1.err create mode 100644 text_cmds/ed/test/r1.d create mode 100644 text_cmds/ed/test/r1.err create mode 100644 text_cmds/ed/test/r1.r create mode 100644 text_cmds/ed/test/r1.t create mode 100644 text_cmds/ed/test/r2.d create mode 100644 text_cmds/ed/test/r2.err create mode 100644 text_cmds/ed/test/r2.r create mode 100644 text_cmds/ed/test/r2.t create mode 100644 text_cmds/ed/test/r3.d create mode 100644 text_cmds/ed/test/r3.r create mode 100644 text_cmds/ed/test/r3.t create mode 100644 text_cmds/ed/test/s1.d create mode 100644 text_cmds/ed/test/s1.err create mode 100644 text_cmds/ed/test/s1.r create mode 100644 text_cmds/ed/test/s1.t create mode 100644 text_cmds/ed/test/s10.err create mode 100644 text_cmds/ed/test/s2.d create mode 100644 text_cmds/ed/test/s2.err create mode 100644 text_cmds/ed/test/s2.r create mode 100644 text_cmds/ed/test/s2.t create mode 100644 text_cmds/ed/test/s3.d create mode 100644 text_cmds/ed/test/s3.err create mode 100644 text_cmds/ed/test/s3.r create mode 100644 text_cmds/ed/test/s3.t create mode 100644 text_cmds/ed/test/s4.err create mode 100644 text_cmds/ed/test/s5.err create mode 100644 text_cmds/ed/test/s6.err create mode 100644 text_cmds/ed/test/s7.err create mode 100644 text_cmds/ed/test/s8.err create mode 100644 text_cmds/ed/test/s9.err create mode 100644 text_cmds/ed/test/t.d create mode 100644 text_cmds/ed/test/t.r create mode 100644 text_cmds/ed/test/t1.d create mode 100644 text_cmds/ed/test/t1.err create mode 100644 text_cmds/ed/test/t1.r create mode 100644 text_cmds/ed/test/t1.t create mode 100644 text_cmds/ed/test/t2.d create mode 100644 text_cmds/ed/test/t2.err create mode 100644 text_cmds/ed/test/t2.r create mode 100644 text_cmds/ed/test/t2.t create mode 100644 text_cmds/ed/test/u.d create mode 100644 text_cmds/ed/test/u.err create mode 100644 text_cmds/ed/test/u.r create mode 100644 text_cmds/ed/test/u.t create mode 100644 text_cmds/ed/test/v.d create mode 100644 text_cmds/ed/test/v.r create mode 100644 text_cmds/ed/test/v.t create mode 100644 text_cmds/ed/test/w.d create mode 100644 text_cmds/ed/test/w.r create mode 100644 text_cmds/ed/test/w.t create mode 100644 text_cmds/ed/test/w1.err create mode 100644 text_cmds/ed/test/w2.err create mode 100644 text_cmds/ed/test/w3.err create mode 100644 text_cmds/ed/test/x.err create mode 100644 text_cmds/ed/test/z.err create mode 100644 text_cmds/ed/undo.c create mode 100644 text_cmds/ee/Changes create mode 100644 text_cmds/ee/Makefile create mode 100644 text_cmds/ee/README.ee create mode 100755 text_cmds/ee/create.make create mode 100644 text_cmds/ee/ee.1 create mode 100644 text_cmds/ee/ee.c create mode 100644 text_cmds/ee/ee.i18n.guide create mode 100644 text_cmds/ee/ee.msg create mode 100644 text_cmds/ee/ee_version.h create mode 100755 text_cmds/ee/genstr create mode 100644 text_cmds/ee/make.default create mode 100644 text_cmds/ee/new_curse.c create mode 100644 text_cmds/ee/new_curse.h create mode 100644 text_cmds/expand/expand.1 create mode 100644 text_cmds/expand/expand.c create mode 100644 text_cmds/expand/xcodescripts/link-man-pages.sh create mode 100644 text_cmds/fmt/fmt.1 create mode 100644 text_cmds/fmt/fmt.c create mode 100644 text_cmds/fold/fold.1 create mode 100644 text_cmds/fold/fold.c create mode 100644 text_cmds/grep/file.c create mode 100644 text_cmds/grep/grep.1 create mode 100644 text_cmds/grep/grep.c create mode 100644 text_cmds/grep/grep.h create mode 100644 text_cmds/grep/queue.c create mode 100644 text_cmds/grep/util.c create mode 100644 text_cmds/head/head.1 create mode 100644 text_cmds/head/head.c create mode 100644 text_cmds/join/join.1 create mode 100644 text_cmds/join/join.c create mode 100644 text_cmds/lam/lam.1 create mode 100644 text_cmds/lam/lam.c create mode 100644 text_cmds/look/look.1 create mode 100644 text_cmds/look/look.c create mode 100644 text_cmds/look/pathnames.h create mode 100644 text_cmds/md5/commoncrypto.c create mode 100644 text_cmds/md5/commoncrypto.h create mode 100644 text_cmds/md5/md5.1 create mode 100644 text_cmds/md5/md5.c create mode 100644 text_cmds/nl/nl.1 create mode 100644 text_cmds/nl/nl.c create mode 100644 text_cmds/paste/paste.1 create mode 100644 text_cmds/paste/paste.c create mode 100644 text_cmds/pr/egetopt.c create mode 100644 text_cmds/pr/extern.h create mode 100644 text_cmds/pr/pr.1 create mode 100644 text_cmds/pr/pr.c create mode 100644 text_cmds/pr/pr.h create mode 100644 text_cmds/rev/rev.1 create mode 100644 text_cmds/rev/rev.c create mode 100644 text_cmds/rs/rs.1 create mode 100644 text_cmds/rs/rs.c create mode 100644 text_cmds/sed/POSIX create mode 100644 text_cmds/sed/TEST/hanoi.sed create mode 100644 text_cmds/sed/TEST/math.sed create mode 100644 text_cmds/sed/TEST/sed.test create mode 100644 text_cmds/sed/compile.c create mode 100644 text_cmds/sed/defs.h create mode 100644 text_cmds/sed/extern.h create mode 100644 text_cmds/sed/main.c create mode 100644 text_cmds/sed/misc.c create mode 100644 text_cmds/sed/process.c create mode 100644 text_cmds/sed/sed.1 create mode 100644 text_cmds/sort/bwstring.c create mode 100644 text_cmds/sort/bwstring.h create mode 100644 text_cmds/sort/coll.c create mode 100644 text_cmds/sort/coll.h create mode 100644 text_cmds/sort/commoncrypto.c create mode 100644 text_cmds/sort/commoncrypto.h create mode 100644 text_cmds/sort/file.c create mode 100644 text_cmds/sort/file.h create mode 100644 text_cmds/sort/mem.c create mode 100644 text_cmds/sort/mem.h create mode 100644 text_cmds/sort/nls/C.msg create mode 100644 text_cmds/sort/nls/hu_HU.ISO8859-2.msg create mode 100644 text_cmds/sort/radixsort.c create mode 100644 text_cmds/sort/radixsort.h create mode 100644 text_cmds/sort/sort.1.in create mode 100644 text_cmds/sort/sort.c create mode 100644 text_cmds/sort/sort.h create mode 100644 text_cmds/sort/testsuite/README.txt create mode 100644 text_cmds/sort/testsuite/bigsample.txt.xz create mode 100755 text_cmds/sort/testsuite/run.sh create mode 100644 text_cmds/sort/testsuite/sample.txt create mode 100644 text_cmds/sort/vsort.c create mode 100644 text_cmds/sort/vsort.h create mode 100644 text_cmds/split/split.1 create mode 100644 text_cmds/split/split.c create mode 100644 text_cmds/tail/extern.h create mode 100644 text_cmds/tail/forward.c create mode 100644 text_cmds/tail/misc.c create mode 100644 text_cmds/tail/read.c create mode 100644 text_cmds/tail/reverse.c create mode 100644 text_cmds/tail/tail.1 create mode 100644 text_cmds/tail/tail.c create mode 100644 text_cmds/tests/Makefile create mode 100644 text_cmds/tests/sort_vers.c create mode 100644 text_cmds/text_cmds.plist create mode 100644 text_cmds/text_cmds.xcodeproj/project.pbxproj create mode 100644 text_cmds/text_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 text_cmds/tr/cmap.c create mode 100644 text_cmds/tr/cmap.h create mode 100644 text_cmds/tr/cset.c create mode 100644 text_cmds/tr/cset.h create mode 100644 text_cmds/tr/extern.h create mode 100644 text_cmds/tr/str.c create mode 100644 text_cmds/tr/tr.1 create mode 100644 text_cmds/tr/tr.c create mode 100644 text_cmds/ul/ul.1 create mode 100644 text_cmds/ul/ul.c create mode 100644 text_cmds/unexpand/unexpand.c create mode 100644 text_cmds/uniq/uniq.1 create mode 100644 text_cmds/uniq/uniq.c create mode 100644 text_cmds/unvis/unvis.1 create mode 100644 text_cmds/unvis/unvis.c create mode 100644 text_cmds/vis/extern.h create mode 100644 text_cmds/vis/foldit.c create mode 100644 text_cmds/vis/vis.1 create mode 100644 text_cmds/vis/vis.c create mode 100644 text_cmds/wc/wc.1 create mode 100644 text_cmds/wc/wc.c create mode 100644 text_cmds/xcconfigs/base.xcconfig create mode 100644 text_cmds/xcconfigs/ee.xcconfig create mode 100644 text_cmds/xcconfigs/grep.xcconfig create mode 100644 text_cmds/xcconfigs/sort.xcconfig create mode 100644 text_cmds/xcodescripts/grep_variant_links.sh create mode 100644 text_cmds/xcodescripts/install-opensource.sh create mode 100644 text_cmds/xcodescripts/install-sort-man.sh (limited to 'text_cmds') 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 +__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 +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/bin/cat/cat.c,v 1.32 2005/01/10 08:39:20 imp Exp $"); + +#include +#include +#ifndef NO_UDOM_SUPPORT +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/col/col.c,v 1.19 2004/07/29 07:28:26 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/usr.bin/colrm/colrm.c,v 1.12 2004/07/29 09:09:22 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/usr.bin/column/column.c,v 1.19 2011/11/06 08:14:34 ed Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/usr.bin/comm/comm.c,v 1.21 2004/07/02 22:48:29 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/usr.bin/csplit/csplit.c,v 1.9 2004/03/22 11:15:03 tjr Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/cut/cut.c,v 1.30 2004/11/05 10:45:23 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 arguments are processed for backslash escapes, i.e., any + character preceded by a backslash is interpreted literally. If the + first unescaped character of a 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 < +__FBSDID("$FreeBSD: src/bin/ed/buf.c,v 1.22 2002/06/30 05:13:53 obrien Exp $"); + +#include +#include + +#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 - < 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 +__FBSDID("$FreeBSD: src/bin/ed/cbc.c,v 1.20 2004/04/06 20:06:47 markm Exp $"); + +#include +#include +#include +#ifdef DES +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/bin/ed/glbl.c,v 1.13 2002/06/30 05:13:53 obrien Exp $"); + +#include +#include +#include + +#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 +__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 + +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#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 && iseek = 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 +__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 +__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'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+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'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+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 diff --git a/text_cmds/ed/test/bang1.d b/text_cmds/ed/test/bang1.d new file mode 100644 index 0000000..e69de29 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 diff --git a/text_cmds/ed/test/l.r b/text_cmds/ed/test/l.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/l.t b/text_cmds/ed/test/l.t new file mode 100644 index 0000000..e69de29 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 diff --git a/text_cmds/ed/test/n.r b/text_cmds/ed/test/n.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/n.t b/text_cmds/ed/test/n.t new file mode 100644 index 0000000..e69de29 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 diff --git a/text_cmds/ed/test/p.r b/text_cmds/ed/test/p.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/p.t b/text_cmds/ed/test/p.t new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/q.d b/text_cmds/ed/test/q.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/q.r b/text_cmds/ed/test/q.r new file mode 100644 index 0000000..e69de29 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 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 +__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 +>"). +.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 +__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 +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAS_SYS_WAIT +#include +#endif + +#ifdef HAS_STDLIB +#include +#endif + +#ifdef HAS_STDARG +#include +#endif + +#ifdef HAS_UNISTD +#include +#endif + +#ifndef NO_CATGETS +#include + +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, ""; + 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 " /tmp/$$.out + +cat > $2 <> $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 +#include + +#ifdef SYS5 +#include +#else +#include +#endif + +#ifdef BSD_SELECT +#include +#include + +#ifdef SLCT_HDR +#include /* on AIX */ +#endif /* SLCT_HDR */ + +#endif /* BSD_SELECT */ + +#ifdef HAS_STDLIB +#include +#endif + +#if defined(__STDC__) +#include +#else +#include +#endif + +#ifdef HAS_UNISTD +#include +#endif + +#ifdef HAS_SYS_IOCTL +#include +#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; xNum_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 + +#ifdef SYS5 +#include +#else +#include +#include +#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 +__FBSDID("$FreeBSD: src/usr.bin/expand/expand.c,v 1.15 2004/06/24 13:42:26 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 ' option is given, we assume -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 ' option is given, then leading whitespace + * is modified slightly: 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 , -l , -p, -s, -t , -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 +__FBSDID("$FreeBSD: src/usr.bin/fmt/fmt.c,v 1.22 2004/08/02 11:10:20 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 double-space after at line end\n" +" -l turn each 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 have tabs every columns\n" +" -w set maximum width to \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"); + } + } + 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 (n0) 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= 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 +__FBSDID("$FreeBSD: src/usr.bin/fold/fold.c,v 1.13 2004/06/24 15:12:29 tjr Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2010 Dimitry Andric + * 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 +__FBSDID("$FreeBSD: src/usr.bin/grep/file.c,v 1.7 2011/10/11 22:27:23 gabor Exp $"); + +#include +#include +#include +#include + +#include +#include +#include +#ifndef WITHOUT_LZMA +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifndef WITHOUT_BZIP2 +#include +#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 + * 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 +__FBSDID("$FreeBSD: src/usr.bin/grep/grep.c,v 1.16 2012/01/15 17:01:28 eadler Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WITHOUT_FASTMATCH +#include "fastmatch.h" +#endif +#include "grep.h" + +#ifndef WITHOUT_NLS +#include +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 + * 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 +#include +#include +#include +#include +#include + +#ifndef WITHOUT_FASTMATCH +#include "fastmatch.h" +#endif + +#ifdef WITHOUT_NLS +#define getstr(n) errstr[n] +#else +#include + +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 to get a better performance. + */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/grep/queue.c,v 1.5 2011/04/07 13:03:35 gabor Exp $"); + +#include +#include + +#include +#include + +#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 + * 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 +__FBSDID("$FreeBSD: src/usr.bin/grep/util.c,v 1.19 2011/12/07 12:25:28 gabor Exp $"); + +#include +#include +#ifdef __APPLE__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif /* __APPLE__ */ +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD: src/usr.bin/head/head.c,v 1.20 2007/01/11 20:23:01 brooks Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +__FBSDID("$FreeBSD: src/usr.bin/join/join.c,v 1.20 2004/08/26 06:28:05 maxim Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__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 +#include +#include +#include +#include +#include + +#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 +__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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include + +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 +__FBSDID("$FreeBSD: src/sbin/md5/md5.c,v 1.34 2005/03/09 19:23:04 cperciva Exp $"); + +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#include +#include +#include +#endif /* !__APPLE__ */ +#include +#include +#include +#include +#include +#include + +#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 +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/paste/paste.c,v 1.14 2004/06/25 01:48:43 tjr Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/pr/egetopt.c,v 1.3 2002/06/23 20:42:30 charnier Exp $"); + +#include +#include +#include +#include + +#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 . +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 +character is output following every +.Em +found in the input. +.It Fl e Xo +.Op Ar char Ns +.Op Ar gap +.Xc +Expand each input +.Em +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 +characters in the input are expanded into the appropriate +number of +.Em s . +If any nondigit character, +.Ar char , +is specified, it is used as the input tab character. +.It Fl F +Use a +.Em +character for new pages, +instead of the default behavior that uses a +sequence of +.Em +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 s +with +.Em s +whenever two or more +adjacent +.Em s +reach column positions +.Ar gap+1 , +.Ar 2*gap+1 , +etc. +If +.Ar gap +is zero or omitted, default +.Em +settings at every eighth column position +is used. +If any nondigit character, +.Ar char , +is specified, it is used as the output +.Em +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 . +Line numbers longer than +.Ar width +columns are truncated. +.It Fl o Ar offset +Each line of output is preceded by +.Ar offset +.Em 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 s +(default for +.Ar char +is the +.Em +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 +__FBSDID("$FreeBSD: src/usr.bin/pr/pr.c,v 1.18 2004/07/26 20:24:59 charnier Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pr.h" +#include "extern.h" + +#ifdef __APPLE__ +#include +/* 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 +__FBSDID("$FreeBSD: src/usr.bin/rev/rev.c,v 1.12 2009/12/13 03:14:06 delphij Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/rs/rs.c,v 1.13 2005/04/28 12:37:15 robert Exp $"); + +#include +#include +#include +#include +#include +#include + +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 +Keith Bostic + +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 + ' +# +# 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 :\ +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}' lines1 + awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' 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' 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 script1 <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&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 +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94"; +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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): + * 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 +__FBSDID("$FreeBSD: head/lib/libmd/mdXhl.c 294037 2016-01-14 21:08:23Z jtl $"); + +#include +#include +#include +#include + +#include +#include +#include + +#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 + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#if defined(SORT_THREADS) +#include +#endif +#ifndef __APPLE__ +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +__FBSDID("$FreeBSD: head/usr.bin/sort/mem.c 281132 2015-04-06 02:35:55Z pfg $"); + +#include +#include +#include + +#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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +#include +#include + +/* + * 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 + * Copyright (C) 2012 Gabor Kovesdan + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#if defined(SORT_THREADS) +#include +#ifndef __APPLE__ +#include +#else +#include +#include +#include +#include +#endif +#endif +#include +#include +#include +#include +#include + +#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 + * Copyright (C) 2012 Gabor Kovesdan + * 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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coll.h" +#include "file.h" +#include "sort.h" + +#ifndef WITHOUT_NLS +#include +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 + * Copyright (C) 2012 Oleg Moskalenko + * 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 +#include +#include +#include +#include + +#include +#ifdef __APPLE__ +#include "commoncrypto.h" +#else +#include +#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 + +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 Binary files /dev/null and b/text_cmds/sort/testsuite/bigsample.txt.xz 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 Binary files /dev/null and b/text_cmds/sort/testsuite/sample.txt 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 + * Copyright (C) 2012 Gabor Kovesdan + * 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 +__FBSDID("$FreeBSD: head/usr.bin/sort/vsort.c 281132 2015-04-06 02:35:55Z pfg $"); + +#include + +#include +#include +#include + +#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 + * Copyright (C) 2012 Gabor Kovesdan + * 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 +__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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +__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 +#include + +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +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 @@ + + + + + + OpenSourceProject + banner + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/banner/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co banner + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Compile fix (needs sys/types.h). + + OpenSourceLicense + BSD + + + OpenSourceProject + cat + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/bin/cat/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cat + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + cksum + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/cksum/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cksum + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Compile fix (needs stdint.h). + + OpenSourceLicense + BSD + + + OpenSourceProject + col + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/col/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co col + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + colrm + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/colrm/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co colrm + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + column + OpenSourceVersion + 2011-11-06 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/column/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co column + OpenSourceImportDate + 2012-10-23 + OpenSourceLicense + BSD + + + OpenSourceProject + comm + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/comm/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co comm + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + csplit + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/csplit/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co csplit + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Rename getline to csplit_getline (7556902). + + OpenSourceLicense + BSD + + + OpenSourceProject + cut + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/cut/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co cut + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + ed + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/bin/ed/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co ed + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Conformance fixes (3676514, 3738526, 3741027, 3751351, 3754089, 3936064, 3936075). + + OpenSourceLicense + BSD + + + OpenSourceProject + ee + OpenSourceVersion + 2011-12-17 + OpenSourceWebsiteURL + http://svnweb.freebsd.org/base/head/contrib/ee/ + OpenSourceSCM + svn co http://svn.freebsd.org/base/head/contrib/ee/ + OpenSourceImportDate + 2015-08-04 + OpenSourceModifications + + + OpenSourceLicense + bsd + + + OpenSourceProject + expand + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/expand/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co expand + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Clarify -t option in manpage (3973603). + + OpenSourceLicense + BSD + + + OpenSourceProject + fmt + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/fmt/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co fmt + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + fold + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/fold/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co fold + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + bsdgrep + OpenSourceVersion + 2012-01-15 + OpenSourceWebsiteURL + http://wiki.freebsd.org/BSDgrep + OpenSourceSCM + cvs -d anoncvs@anoncvs1.FreeBSD.org:/home/ncvs co src/usr.bin/grep + OpenSourceImportDate + 2012-01-23 + OpenSourceModifications + + 9510115 (grep.c, util.c) + WITHOUT_FASTMATCH (grep.h, grep.c, util.c) + WITHOUT_LZMA (file.c, grep.c) + file.c: casts in grep_fgetln to fix warnings + file.c: adjust mmap flags + grep.c: enable REG_ENHANCED (except for -F mode) + grep.c: disable FILE_MMAP options + util.c: basename expects char * + util.c: ignore locale for binary files (10462853) + util.c: recursive stdin warning (10290183) + util.c: -w fix (10593340) + util.c: -c -q fix (10680370) + util.c, grep.h: avoid MAX_LINE_MATCHES limitation (10511988) + grep.c: Remove -P from usage statement (13475367) + util.c: Use mbtowc instead of sscanf (20248554) + + OpenSourceLicense + BSD + OpenSourceLicenseFile + bsdgrep.txt + + + OpenSourceProject + head + OpenSourceVersion + 2007-01-11 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/head/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co head + OpenSourceImportDate + 2010-05-10 + OpenSourceLicense + BSD + OpenSourceModifications + + Check ferror (6555390). + + + + OpenSourceProject + join + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/join/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co join + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Conformance fixes (3853584). + + OpenSourceLicense + BSD + + + OpenSourceProject + lam + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/lam/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co lam + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + look + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/look/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co look + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + md5 + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/sbin/md5/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co md5 + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + XXX NOT MERGED YET XXX + + OpenSourceLicense + BSD + + + OpenSourceProject + nl + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/nl/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co nl + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Conformance fix in filter() (3849850). + + OpenSourceLicense + BSD + + + OpenSourceProject + paste + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/paste/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co paste + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + pr + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/pr/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co pr + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Conformance fixes (3743086, 3936126). + + OpenSourceLicense + BSD + + + OpenSourceProject + rev + OpenSourceVersion + 2009-12-13 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/rev/ + OpenSourceCVS + cvs -d anoncvs@anoncvs1.FreeBSD.org:/home/ncvs co rev + OpenSourceImportDate + 2010-04-07 + OpenSourceModifications + + OpenSourceLicense + BSD + + + OpenSourceProject + rs + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/rs/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co rs + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Rename getline to rs_getline (7556902). + + OpenSourceLicense + BSD + + + OpenSourceProject + sed + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/sed/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co sed + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Correctly document -y option (4134578). + Rewind stdin before exit (3681024). + Handle '[' delimiter (3733839). + Handle backslash with 'y', remove handy warning (3733839). + + OpenSourceLicense + BSD + + + OpenSourceProject + sort + OpenSourceVersion + 2017-09-27 + OpenSourceWebsiteURL + https://svnweb.freebsd.org/base/head/usr.bin/sort/ + OpenSourceSCM + svn co http://svn.freebsd.org/base/head/usr.bin/sort + OpenSourceImportDate + 2017-09-27 + OpenSourceModifications + + file.c: mmap portability + file.c: fix unreachable code warning + coll.c, sort.c, sort.h: md5 portability + sort.c: update version output + sort.c: conformance failure with -k (28745165) + + OpenSourceLicense + bsd + + + OpenSourceProject + split + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/split/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co split + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + tail + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/tail/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co tail + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Don't read directories (3849683). + + OpenSourceLicense + BSD + + + OpenSourceProject + tr + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/tr/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co tr + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Add missing character classes to man page (4115259). + Handle tr -s '[a*]' (3673947). + + OpenSourceLicense + BSD + + + OpenSourceProject + ul + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/ul/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co ul + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + unexpand + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/unexpand/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co unexpand + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + uniq + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/uniq/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co uniq + OpenSourceImportDate + 2005-09-16 + OpenSourceModifications + + Rename getline to uniq_getline (7556902). + + OpenSourceLicense + BSD + + + OpenSourceProject + unvis + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/unvis/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co unvis + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + vis + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/vis/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co vis + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + OpenSourceProject + wc + OpenSourceVersion + 2005-09-16 + OpenSourceWebsiteURL + http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/wc/ + OpenSourceCVS + cvs -d freebsdanoncvs@anoncvs.FreeBSD.org:/home/ncvs co wc + OpenSourceImportDate + 2005-09-16 + OpenSourceLicense + BSD + + + 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 = ""; }; + FC6C98FB149A94EB00DDCC47 /* libcurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurses.dylib; path = /usr/lib/libcurses.dylib; sourceTree = ""; }; + 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 = ""; }; + FC7A7B4C149866AE0086576A /* banner.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = banner.c; sourceTree = ""; }; + FC7A7B4F149866AE0086576A /* cat.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cat.1; sourceTree = ""; }; + FC7A7B50149866AE0086576A /* cat.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cat.c; sourceTree = ""; }; + FC7A7B53149866AE0086576A /* col.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = col.1; sourceTree = ""; }; + FC7A7B54149866AE0086576A /* col.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = col.c; sourceTree = ""; }; + FC7A7B56149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; + FC7A7B58149866AE0086576A /* colrm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = colrm.1; sourceTree = ""; }; + FC7A7B59149866AE0086576A /* colrm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = colrm.c; sourceTree = ""; }; + FC7A7B5C149866AE0086576A /* column.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = column.1; sourceTree = ""; }; + FC7A7B5D149866AE0086576A /* column.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = column.c; sourceTree = ""; }; + FC7A7B60149866AE0086576A /* comm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = comm.1; sourceTree = ""; }; + FC7A7B61149866AE0086576A /* comm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = comm.c; sourceTree = ""; }; + FC7A7B64149866AE0086576A /* csplit.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = csplit.1; sourceTree = ""; }; + FC7A7B65149866AE0086576A /* csplit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = csplit.c; sourceTree = ""; }; + FC7A7B68149866AE0086576A /* cut.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cut.1; sourceTree = ""; }; + FC7A7B69149866AE0086576A /* cut.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cut.c; sourceTree = ""; }; + FC7A7B6C149866AE0086576A /* buf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = buf.c; sourceTree = ""; }; + FC7A7B6D149866AE0086576A /* cbc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cbc.c; sourceTree = ""; }; + FC7A7B6E149866AE0086576A /* ed.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ed.1; sourceTree = ""; }; + FC7A7B6F149866AE0086576A /* ed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ed.h; sourceTree = ""; }; + FC7A7B70149866AE0086576A /* glbl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = glbl.c; sourceTree = ""; }; + FC7A7B71149866AE0086576A /* io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = io.c; sourceTree = ""; }; + FC7A7B72149866AE0086576A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + FC7A7B74149866AE0086576A /* POSIX */ = {isa = PBXFileReference; lastKnownFileType = text; path = POSIX; sourceTree = ""; }; + FC7A7B75149866AE0086576A /* re.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = re.c; sourceTree = ""; }; + FC7A7B76149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; + FC7A7B77149866AE0086576A /* red.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = red.1; sourceTree = ""; }; + FC7A7B78149866AE0086576A /* sub.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sub.c; sourceTree = ""; }; + FC7A7B7A149866AE0086576A /* =.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = "=.err"; sourceTree = ""; }; + FC7A7B7B149866AE0086576A /* a.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = a.d; sourceTree = ""; }; + FC7A7B7C149866AE0086576A /* a.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = a.r; sourceTree = ""; }; + FC7A7B7D149866AE0086576A /* a.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = a.t; sourceTree = ""; }; + FC7A7B7E149866AE0086576A /* a1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = a1.err; sourceTree = ""; }; + FC7A7B7F149866AE0086576A /* a2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = a2.err; sourceTree = ""; }; + FC7A7B80149866AE0086576A /* addr.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = addr.d; sourceTree = ""; }; + FC7A7B81149866AE0086576A /* addr.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = addr.r; sourceTree = ""; }; + FC7A7B82149866AE0086576A /* addr.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr.t; sourceTree = ""; }; + FC7A7B83149866AE0086576A /* addr1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr1.err; sourceTree = ""; }; + FC7A7B84149866AE0086576A /* addr2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = addr2.err; sourceTree = ""; }; + FC7A7B85149866AE0086576A /* ascii.d.uu */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.d.uu; sourceTree = ""; }; + FC7A7B86149866AE0086576A /* ascii.r.uu */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.r.uu; sourceTree = ""; }; + FC7A7B87149866AE0086576A /* ascii.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = ascii.t; sourceTree = ""; }; + FC7A7B88149866AE0086576A /* bang1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = bang1.d; sourceTree = ""; }; + FC7A7B89149866AE0086576A /* bang1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang1.err; sourceTree = ""; }; + FC7A7B8A149866AE0086576A /* bang1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = bang1.r; sourceTree = ""; }; + FC7A7B8B149866AE0086576A /* bang1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang1.t; sourceTree = ""; }; + FC7A7B8C149866AE0086576A /* bang2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = bang2.err; sourceTree = ""; }; + FC7A7B8D149866AE0086576A /* c.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = c.d; sourceTree = ""; }; + FC7A7B8E149866AE0086576A /* c.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = c.r; sourceTree = ""; }; + FC7A7B8F149866AE0086576A /* c.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = c.t; sourceTree = ""; }; + FC7A7B90149866AE0086576A /* c1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = c1.err; sourceTree = ""; }; + FC7A7B91149866AE0086576A /* c2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = c2.err; sourceTree = ""; }; + FC7A7B92149866AE0086576A /* ckscripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ckscripts.sh; sourceTree = ""; }; + FC7A7B93149866AE0086576A /* d.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = d.d; sourceTree = ""; }; + FC7A7B94149866AE0086576A /* d.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = d.err; sourceTree = ""; }; + FC7A7B95149866AE0086576A /* d.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = d.r; sourceTree = ""; }; + FC7A7B96149866AE0086576A /* d.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = d.t; sourceTree = ""; }; + FC7A7B97149866AE0086576A /* e1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e1.d; sourceTree = ""; }; + FC7A7B98149866AE0086576A /* e1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e1.err; sourceTree = ""; }; + FC7A7B99149866AE0086576A /* e1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e1.r; sourceTree = ""; }; + FC7A7B9A149866AE0086576A /* e1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e1.t; sourceTree = ""; }; + FC7A7B9B149866AE0086576A /* e2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e2.d; sourceTree = ""; }; + FC7A7B9C149866AE0086576A /* e2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e2.err; sourceTree = ""; }; + FC7A7B9D149866AE0086576A /* e2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e2.r; sourceTree = ""; }; + FC7A7B9E149866AE0086576A /* e2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e2.t; sourceTree = ""; }; + FC7A7B9F149866AE0086576A /* e3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e3.d; sourceTree = ""; }; + FC7A7BA0149866AE0086576A /* e3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = e3.err; sourceTree = ""; }; + FC7A7BA1149866AE0086576A /* e3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e3.r; sourceTree = ""; }; + FC7A7BA2149866AE0086576A /* e3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e3.t; sourceTree = ""; }; + FC7A7BA3149866AE0086576A /* e4.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = e4.d; sourceTree = ""; }; + FC7A7BA4149866AE0086576A /* e4.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = e4.r; sourceTree = ""; }; + FC7A7BA5149866AE0086576A /* e4.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = e4.t; sourceTree = ""; }; + FC7A7BA6149866AE0086576A /* f1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = f1.err; sourceTree = ""; }; + FC7A7BA7149866AE0086576A /* f2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = f2.err; sourceTree = ""; }; + FC7A7BA8149866AE0086576A /* g1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g1.d; sourceTree = ""; }; + FC7A7BA9149866AE0086576A /* g1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g1.err; sourceTree = ""; }; + FC7A7BAA149866AE0086576A /* g1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g1.r; sourceTree = ""; }; + FC7A7BAB149866AE0086576A /* g1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g1.t; sourceTree = ""; }; + FC7A7BAC149866AE0086576A /* g2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g2.d; sourceTree = ""; }; + FC7A7BAD149866AE0086576A /* g2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g2.err; sourceTree = ""; }; + FC7A7BAE149866AE0086576A /* g2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g2.r; sourceTree = ""; }; + FC7A7BAF149866AE0086576A /* g2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g2.t; sourceTree = ""; }; + FC7A7BB0149866AE0086576A /* g3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g3.d; sourceTree = ""; }; + FC7A7BB1149866AE0086576A /* g3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = g3.err; sourceTree = ""; }; + FC7A7BB2149866AE0086576A /* g3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g3.r; sourceTree = ""; }; + FC7A7BB3149866AE0086576A /* g3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g3.t; sourceTree = ""; }; + FC7A7BB4149866AE0086576A /* g4.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g4.d; sourceTree = ""; }; + FC7A7BB5149866AE0086576A /* g4.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g4.r; sourceTree = ""; }; + FC7A7BB6149866AE0086576A /* g4.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g4.t; sourceTree = ""; }; + FC7A7BB7149866AE0086576A /* g5.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = g5.d; sourceTree = ""; }; + FC7A7BB8149866AE0086576A /* g5.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = g5.r; sourceTree = ""; }; + FC7A7BB9149866AE0086576A /* g5.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = g5.t; sourceTree = ""; }; + FC7A7BBA149866AE0086576A /* h.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = h.err; sourceTree = ""; }; + FC7A7BBB149866AE0086576A /* i.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = i.d; sourceTree = ""; }; + FC7A7BBC149866AE0086576A /* i.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = i.r; sourceTree = ""; }; + FC7A7BBD149866AE0086576A /* i.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = i.t; sourceTree = ""; }; + FC7A7BBE149866AE0086576A /* i1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i1.err; sourceTree = ""; }; + FC7A7BBF149866AE0086576A /* i2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i2.err; sourceTree = ""; }; + FC7A7BC0149866AE0086576A /* i3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = i3.err; sourceTree = ""; }; + FC7A7BC1149866AE0086576A /* j.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = j.d; sourceTree = ""; }; + FC7A7BC2149866AE0086576A /* j.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = j.r; sourceTree = ""; }; + FC7A7BC3149866AE0086576A /* j.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = j.t; sourceTree = ""; }; + FC7A7BC4149866AE0086576A /* k.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = k.d; sourceTree = ""; }; + FC7A7BC5149866AE0086576A /* k.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = k.r; sourceTree = ""; }; + FC7A7BC6149866AE0086576A /* k.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = k.t; sourceTree = ""; }; + FC7A7BC7149866AE0086576A /* k1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k1.err; sourceTree = ""; }; + FC7A7BC8149866AE0086576A /* k2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k2.err; sourceTree = ""; }; + FC7A7BC9149866AE0086576A /* k3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k3.err; sourceTree = ""; }; + FC7A7BCA149866AE0086576A /* k4.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = k4.err; sourceTree = ""; }; + FC7A7BCB149866AE0086576A /* l.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = l.d; sourceTree = ""; }; + FC7A7BCC149866AE0086576A /* l.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = l.r; sourceTree = ""; }; + FC7A7BCD149866AE0086576A /* l.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = l.t; sourceTree = ""; }; + FC7A7BCE149866AE0086576A /* m.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = m.d; sourceTree = ""; }; + FC7A7BCF149866AE0086576A /* m.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = m.err; sourceTree = ""; }; + FC7A7BD0149866AE0086576A /* m.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = m.r; sourceTree = ""; }; + FC7A7BD1149866AE0086576A /* m.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = m.t; sourceTree = ""; }; + FC7A7BD2149866AE0086576A /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + FC7A7BD3149866AE0086576A /* mkscripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = mkscripts.sh; sourceTree = ""; }; + FC7A7BD4149866AE0086576A /* n.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = n.d; sourceTree = ""; }; + FC7A7BD5149866AE0086576A /* n.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = n.r; sourceTree = ""; }; + FC7A7BD6149866AE0086576A /* n.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = n.t; sourceTree = ""; }; + FC7A7BD7149866AE0086576A /* nl.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl.err; sourceTree = ""; }; + FC7A7BD8149866AE0086576A /* nl1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = nl1.d; sourceTree = ""; }; + FC7A7BD9149866AE0086576A /* nl1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = nl1.r; sourceTree = ""; }; + FC7A7BDA149866AE0086576A /* nl1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl1.t; sourceTree = ""; }; + FC7A7BDB149866AE0086576A /* nl2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = nl2.d; sourceTree = ""; }; + FC7A7BDC149866AE0086576A /* nl2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = nl2.r; sourceTree = ""; }; + FC7A7BDD149866AE0086576A /* nl2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = nl2.t; sourceTree = ""; }; + FC7A7BDE149866AE0086576A /* p.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = p.d; sourceTree = ""; }; + FC7A7BDF149866AE0086576A /* p.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = p.r; sourceTree = ""; }; + FC7A7BE0149866AE0086576A /* p.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = p.t; sourceTree = ""; }; + FC7A7BE1149866AE0086576A /* q.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = q.d; sourceTree = ""; }; + FC7A7BE2149866AE0086576A /* q.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = q.r; sourceTree = ""; }; + FC7A7BE3149866AE0086576A /* q.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = q.t; sourceTree = ""; }; + FC7A7BE4149866AE0086576A /* q1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = q1.err; sourceTree = ""; }; + FC7A7BE5149866AE0086576A /* r1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r1.d; sourceTree = ""; }; + FC7A7BE6149866AE0086576A /* r1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = r1.err; sourceTree = ""; }; + FC7A7BE7149866AE0086576A /* r1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r1.r; sourceTree = ""; }; + FC7A7BE8149866AE0086576A /* r1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r1.t; sourceTree = ""; }; + FC7A7BE9149866AE0086576A /* r2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r2.d; sourceTree = ""; }; + FC7A7BEA149866AE0086576A /* r2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = r2.err; sourceTree = ""; }; + FC7A7BEB149866AE0086576A /* r2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r2.r; sourceTree = ""; }; + FC7A7BEC149866AE0086576A /* r2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r2.t; sourceTree = ""; }; + FC7A7BED149866AE0086576A /* r3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = r3.d; sourceTree = ""; }; + FC7A7BEE149866AE0086576A /* r3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = r3.r; sourceTree = ""; }; + FC7A7BEF149866AE0086576A /* r3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = r3.t; sourceTree = ""; }; + FC7A7BF0149866AE0086576A /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; + FC7A7BF1149866AE0086576A /* s1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s1.d; sourceTree = ""; }; + FC7A7BF2149866AE0086576A /* s1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s1.err; sourceTree = ""; }; + FC7A7BF3149866AE0086576A /* s1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s1.r; sourceTree = ""; }; + FC7A7BF4149866AE0086576A /* s1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s1.t; sourceTree = ""; }; + FC7A7BF5149866AE0086576A /* s10.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s10.err; sourceTree = ""; }; + FC7A7BF6149866AE0086576A /* s2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s2.d; sourceTree = ""; }; + FC7A7BF7149866AE0086576A /* s2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s2.err; sourceTree = ""; }; + FC7A7BF8149866AE0086576A /* s2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s2.r; sourceTree = ""; }; + FC7A7BF9149866AE0086576A /* s2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s2.t; sourceTree = ""; }; + FC7A7BFA149866AE0086576A /* s3.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = s3.d; sourceTree = ""; }; + FC7A7BFB149866AE0086576A /* s3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s3.err; sourceTree = ""; }; + FC7A7BFC149866AE0086576A /* s3.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = s3.r; sourceTree = ""; }; + FC7A7BFD149866AE0086576A /* s3.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = s3.t; sourceTree = ""; }; + FC7A7BFE149866AE0086576A /* s4.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s4.err; sourceTree = ""; }; + FC7A7BFF149866AF0086576A /* s5.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s5.err; sourceTree = ""; }; + FC7A7C00149866AF0086576A /* s6.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s6.err; sourceTree = ""; }; + FC7A7C01149866AF0086576A /* s7.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s7.err; sourceTree = ""; }; + FC7A7C02149866AF0086576A /* s8.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s8.err; sourceTree = ""; }; + FC7A7C03149866AF0086576A /* s9.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = s9.err; sourceTree = ""; }; + FC7A7C04149866AF0086576A /* t.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t.d; sourceTree = ""; }; + FC7A7C05149866AF0086576A /* t.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t.r; sourceTree = ""; }; + FC7A7C06149866AF0086576A /* t1.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t1.d; sourceTree = ""; }; + FC7A7C07149866AF0086576A /* t1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = t1.err; sourceTree = ""; }; + FC7A7C08149866AF0086576A /* t1.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t1.r; sourceTree = ""; }; + FC7A7C09149866AF0086576A /* t1.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = t1.t; sourceTree = ""; }; + FC7A7C0A149866AF0086576A /* t2.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = t2.d; sourceTree = ""; }; + FC7A7C0B149866AF0086576A /* t2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = t2.err; sourceTree = ""; }; + FC7A7C0C149866AF0086576A /* t2.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = t2.r; sourceTree = ""; }; + FC7A7C0D149866AF0086576A /* t2.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = t2.t; sourceTree = ""; }; + FC7A7C0E149866AF0086576A /* TODO */ = {isa = PBXFileReference; lastKnownFileType = text; path = TODO; sourceTree = ""; }; + FC7A7C0F149866AF0086576A /* u.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = u.d; sourceTree = ""; }; + FC7A7C10149866AF0086576A /* u.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = u.err; sourceTree = ""; }; + FC7A7C11149866AF0086576A /* u.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = u.r; sourceTree = ""; }; + FC7A7C12149866AF0086576A /* u.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = u.t; sourceTree = ""; }; + FC7A7C13149866AF0086576A /* v.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = v.d; sourceTree = ""; }; + FC7A7C14149866AF0086576A /* v.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = v.r; sourceTree = ""; }; + FC7A7C15149866AF0086576A /* v.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = v.t; sourceTree = ""; }; + FC7A7C16149866AF0086576A /* w.d */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.dtrace; path = w.d; sourceTree = ""; }; + FC7A7C17149866AF0086576A /* w.r */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = w.r; sourceTree = ""; }; + FC7A7C18149866AF0086576A /* w.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = w.t; sourceTree = ""; }; + FC7A7C19149866AF0086576A /* w1.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w1.err; sourceTree = ""; }; + FC7A7C1A149866AF0086576A /* w2.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w2.err; sourceTree = ""; }; + FC7A7C1B149866AF0086576A /* w3.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = w3.err; sourceTree = ""; }; + FC7A7C1C149866AF0086576A /* x.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = x.err; sourceTree = ""; }; + FC7A7C1D149866AF0086576A /* z.err */ = {isa = PBXFileReference; lastKnownFileType = text; path = z.err; sourceTree = ""; }; + FC7A7C1E149866AF0086576A /* undo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = undo.c; sourceTree = ""; }; + FC7A7C20149866AF0086576A /* expand.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = expand.1; sourceTree = ""; }; + FC7A7C21149866AF0086576A /* expand.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = expand.c; sourceTree = ""; }; + FC7A7C24149866AF0086576A /* fmt.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fmt.1; sourceTree = ""; }; + FC7A7C25149866AF0086576A /* fmt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fmt.c; sourceTree = ""; }; + FC7A7C28149866AF0086576A /* fold.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fold.1; sourceTree = ""; }; + FC7A7C29149866AF0086576A /* fold.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fold.c; sourceTree = ""; }; + FC7A7C2C149866AF0086576A /* head.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = head.1; sourceTree = ""; }; + FC7A7C2D149866AF0086576A /* head.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = head.c; sourceTree = ""; }; + FC7A7C30149866AF0086576A /* join.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = join.1; sourceTree = ""; }; + FC7A7C31149866AF0086576A /* join.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = join.c; sourceTree = ""; }; + FC7A7C34149866AF0086576A /* lam.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = lam.1; sourceTree = ""; }; + FC7A7C35149866AF0086576A /* lam.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = lam.c; sourceTree = ""; }; + FC7A7C38149866AF0086576A /* look.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = look.1; sourceTree = ""; }; + FC7A7C39149866AF0086576A /* look.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = look.c; sourceTree = ""; }; + FC7A7C3B149866AF0086576A /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = ""; }; + FC7A7C3D149866AF0086576A /* commoncrypto.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = ""; }; + FC7A7C3E149866AF0086576A /* commoncrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = ""; }; + FC7A7C40149866AF0086576A /* md5.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = md5.1; sourceTree = ""; }; + FC7A7C41149866AF0086576A /* md5.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = md5.c; sourceTree = ""; }; + FC7A7C44149866AF0086576A /* nl.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = nl.1; sourceTree = ""; }; + FC7A7C45149866AF0086576A /* nl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nl.c; sourceTree = ""; }; + FC7A7C48149866AF0086576A /* paste.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = paste.1; sourceTree = ""; }; + FC7A7C49149866AF0086576A /* paste.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = paste.c; sourceTree = ""; }; + FC7A7C4B149866AF0086576A /* egetopt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = egetopt.c; sourceTree = ""; }; + FC7A7C4C149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FC7A7C4E149866AF0086576A /* pr.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pr.1; sourceTree = ""; }; + FC7A7C4F149866AF0086576A /* pr.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pr.c; sourceTree = ""; }; + FC7A7C50149866AF0086576A /* pr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pr.h; sourceTree = ""; }; + FC7A7C53149866AF0086576A /* rev.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rev.1; sourceTree = ""; }; + FC7A7C54149866AF0086576A /* rev.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rev.c; sourceTree = ""; }; + FC7A7C57149866AF0086576A /* rs.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rs.1; sourceTree = ""; }; + FC7A7C58149866AF0086576A /* rs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rs.c; sourceTree = ""; }; + FC7A7C5A149866AF0086576A /* compile.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compile.c; sourceTree = ""; }; + FC7A7C5B149866AF0086576A /* defs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = defs.h; sourceTree = ""; }; + FC7A7C5C149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FC7A7C5D149866AF0086576A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + FC7A7C5F149866AF0086576A /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = ""; }; + FC7A7C60149866AF0086576A /* POSIX */ = {isa = PBXFileReference; lastKnownFileType = text; path = POSIX; sourceTree = ""; }; + FC7A7C61149866AF0086576A /* process.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = process.c; sourceTree = ""; }; + FC7A7C62149866AF0086576A /* sed.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = sed.1; sourceTree = ""; }; + FC7A7C64149866AF0086576A /* hanoi.sed */ = {isa = PBXFileReference; lastKnownFileType = text; path = hanoi.sed; sourceTree = ""; }; + FC7A7C65149866AF0086576A /* math.sed */ = {isa = PBXFileReference; lastKnownFileType = text; path = math.sed; sourceTree = ""; }; + FC7A7C66149866AF0086576A /* sed.test */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sed.test; sourceTree = ""; }; + FC7A7C93149866AF0086576A /* sort.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sort.c; sourceTree = ""; }; + FC7A7CB4149866AF0086576A /* split.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = split.1; sourceTree = ""; }; + FC7A7CB5149866AF0086576A /* split.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = split.c; sourceTree = ""; }; + FC7A7CB7149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FC7A7CB8149866AF0086576A /* forward.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = forward.c; sourceTree = ""; }; + FC7A7CBA149866AF0086576A /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = ""; }; + FC7A7CBB149866AF0086576A /* read.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = read.c; sourceTree = ""; }; + FC7A7CBC149866AF0086576A /* reverse.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = reverse.c; sourceTree = ""; }; + FC7A7CBD149866AF0086576A /* tail.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tail.1; sourceTree = ""; }; + FC7A7CBE149866AF0086576A /* tail.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tail.c; sourceTree = ""; }; + FC7A7CBF149866AF0086576A /* text_cmds.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = text_cmds.plist; sourceTree = ""; }; + FC7A7CC1149866AF0086576A /* cmap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmap.c; sourceTree = ""; }; + FC7A7CC2149866AF0086576A /* cmap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cmap.h; sourceTree = ""; }; + FC7A7CC3149866AF0086576A /* cset.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cset.c; sourceTree = ""; }; + FC7A7CC4149866AF0086576A /* cset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cset.h; sourceTree = ""; }; + FC7A7CC5149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FC7A7CC7149866AF0086576A /* str.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = str.c; sourceTree = ""; }; + FC7A7CC8149866AF0086576A /* tr.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tr.1; sourceTree = ""; }; + FC7A7CC9149866AF0086576A /* tr.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tr.c; sourceTree = ""; }; + FC7A7CCC149866AF0086576A /* ul.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ul.1; sourceTree = ""; }; + FC7A7CCD149866AF0086576A /* ul.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ul.c; sourceTree = ""; }; + FC7A7CD0149866AF0086576A /* unexpand.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unexpand.c; sourceTree = ""; }; + FC7A7CD3149866AF0086576A /* uniq.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uniq.1; sourceTree = ""; }; + FC7A7CD4149866AF0086576A /* uniq.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = uniq.c; sourceTree = ""; }; + FC7A7CD7149866AF0086576A /* unvis.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = unvis.1; sourceTree = ""; }; + FC7A7CD8149866AF0086576A /* unvis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unvis.c; sourceTree = ""; }; + FC7A7CDA149866AF0086576A /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FC7A7CDB149866AF0086576A /* foldit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = foldit.c; sourceTree = ""; }; + FC7A7CDD149866AF0086576A /* vis.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = vis.1; sourceTree = ""; }; + FC7A7CDE149866AF0086576A /* vis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vis.c; sourceTree = ""; }; + FC7A7CE1149866AF0086576A /* wc.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = wc.1; sourceTree = ""; }; + FC7A7CE2149866AF0086576A /* wc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wc.c; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; + FD3561F91B827F9F008A70F6 /* grep.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = grep.1; sourceTree = ""; }; + FD3561FA1B827F9F008A70F6 /* grep.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = grep.c; sourceTree = ""; }; + FD3561FB1B827F9F008A70F6 /* grep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = grep.h; sourceTree = ""; }; + FD3561FC1B827F9F008A70F6 /* queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = queue.c; sourceTree = ""; }; + FD3561FD1B827F9F008A70F6 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = ""; }; + FD3562031B82803F008A70F6 /* grep.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = grep.xcconfig; sourceTree = ""; }; + 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 = ""; }; + FDA49B961B718762003B4F3C /* ee.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = ee.1; sourceTree = ""; }; + FDF283571DAED0C000CF8C36 /* bwstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwstring.c; sourceTree = ""; }; + FDF283581DAED0C000CF8C36 /* bwstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bwstring.h; sourceTree = ""; }; + FDF283591DAED0C000CF8C36 /* coll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coll.c; sourceTree = ""; }; + FDF2835A1DAED0C000CF8C36 /* coll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coll.h; sourceTree = ""; }; + FDF2835B1DAED0C000CF8C36 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = ""; }; + FDF2835C1DAED0C000CF8C36 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = ""; }; + FDF2835D1DAED0C000CF8C36 /* mem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mem.c; sourceTree = ""; }; + FDF2835E1DAED0C000CF8C36 /* mem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem.h; sourceTree = ""; }; + FDF283601DAED0C000CF8C36 /* C.msg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = C.msg; sourceTree = ""; }; + FDF283611DAED0C000CF8C36 /* hu_HU.ISO8859-2.msg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "hu_HU.ISO8859-2.msg"; sourceTree = ""; }; + FDF283621DAED0C000CF8C36 /* radixsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = radixsort.c; sourceTree = ""; }; + FDF283631DAED0C000CF8C36 /* radixsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = radixsort.h; sourceTree = ""; }; + FDF283641DAED0C000CF8C36 /* sort.1.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = sort.1.in; sourceTree = ""; }; + FDF283651DAED0C000CF8C36 /* sort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sort.h; sourceTree = ""; }; + FDF283661DAED0C000CF8C36 /* vsort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsort.c; sourceTree = ""; }; + FDF283671DAED0C000CF8C36 /* vsort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vsort.h; sourceTree = ""; }; + FDF2836E1DAED24800CF8C36 /* sort.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = sort.xcconfig; sourceTree = ""; }; + FDF2836F1DAED7C700CF8C36 /* commoncrypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = ""; }; + FDF283701DAED7C700CF8C36 /* commoncrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = ""; }; +/* 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 = ""; + }; + 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 = ""; + }; + FC7A7B4A149866AE0086576A /* banner */ = { + isa = PBXGroup; + children = ( + FC7A7B4B149866AE0086576A /* banner.6 */, + FC7A7B4C149866AE0086576A /* banner.c */, + ); + path = banner; + sourceTree = ""; + }; + FC7A7B4E149866AE0086576A /* cat */ = { + isa = PBXGroup; + children = ( + FC7A7B4F149866AE0086576A /* cat.1 */, + FC7A7B50149866AE0086576A /* cat.c */, + ); + path = cat; + sourceTree = ""; + }; + FC7A7B52149866AE0086576A /* col */ = { + isa = PBXGroup; + children = ( + FC7A7B53149866AE0086576A /* col.1 */, + FC7A7B54149866AE0086576A /* col.c */, + FC7A7B56149866AE0086576A /* README */, + ); + path = col; + sourceTree = ""; + }; + FC7A7B57149866AE0086576A /* colrm */ = { + isa = PBXGroup; + children = ( + FC7A7B58149866AE0086576A /* colrm.1 */, + FC7A7B59149866AE0086576A /* colrm.c */, + ); + path = colrm; + sourceTree = ""; + }; + FC7A7B5B149866AE0086576A /* column */ = { + isa = PBXGroup; + children = ( + FC7A7B5C149866AE0086576A /* column.1 */, + FC7A7B5D149866AE0086576A /* column.c */, + ); + path = column; + sourceTree = ""; + }; + FC7A7B5F149866AE0086576A /* comm */ = { + isa = PBXGroup; + children = ( + FC7A7B60149866AE0086576A /* comm.1 */, + FC7A7B61149866AE0086576A /* comm.c */, + ); + path = comm; + sourceTree = ""; + }; + FC7A7B63149866AE0086576A /* csplit */ = { + isa = PBXGroup; + children = ( + FC7A7B64149866AE0086576A /* csplit.1 */, + FC7A7B65149866AE0086576A /* csplit.c */, + ); + path = csplit; + sourceTree = ""; + }; + FC7A7B67149866AE0086576A /* cut */ = { + isa = PBXGroup; + children = ( + FC7A7B68149866AE0086576A /* cut.1 */, + FC7A7B69149866AE0086576A /* cut.c */, + ); + path = cut; + sourceTree = ""; + }; + 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 = ""; + }; + 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 = ""; + }; + FC7A7C1F149866AF0086576A /* expand */ = { + isa = PBXGroup; + children = ( + FC7A7C20149866AF0086576A /* expand.1 */, + FC7A7C21149866AF0086576A /* expand.c */, + ); + path = expand; + sourceTree = ""; + }; + FC7A7C23149866AF0086576A /* fmt */ = { + isa = PBXGroup; + children = ( + FC7A7C24149866AF0086576A /* fmt.1 */, + FC7A7C25149866AF0086576A /* fmt.c */, + ); + path = fmt; + sourceTree = ""; + }; + FC7A7C27149866AF0086576A /* fold */ = { + isa = PBXGroup; + children = ( + FC7A7C28149866AF0086576A /* fold.1 */, + FC7A7C29149866AF0086576A /* fold.c */, + ); + path = fold; + sourceTree = ""; + }; + FC7A7C2B149866AF0086576A /* head */ = { + isa = PBXGroup; + children = ( + FC7A7C2C149866AF0086576A /* head.1 */, + FC7A7C2D149866AF0086576A /* head.c */, + ); + path = head; + sourceTree = ""; + }; + FC7A7C2F149866AF0086576A /* join */ = { + isa = PBXGroup; + children = ( + FC7A7C30149866AF0086576A /* join.1 */, + FC7A7C31149866AF0086576A /* join.c */, + ); + path = join; + sourceTree = ""; + }; + FC7A7C33149866AF0086576A /* lam */ = { + isa = PBXGroup; + children = ( + FC7A7C34149866AF0086576A /* lam.1 */, + FC7A7C35149866AF0086576A /* lam.c */, + ); + path = lam; + sourceTree = ""; + }; + FC7A7C37149866AF0086576A /* look */ = { + isa = PBXGroup; + children = ( + FC7A7C38149866AF0086576A /* look.1 */, + FC7A7C39149866AF0086576A /* look.c */, + FC7A7C3B149866AF0086576A /* pathnames.h */, + ); + path = look; + sourceTree = ""; + }; + FC7A7C3C149866AF0086576A /* md5 */ = { + isa = PBXGroup; + children = ( + FC7A7C3D149866AF0086576A /* commoncrypto.c */, + FC7A7C3E149866AF0086576A /* commoncrypto.h */, + FC7A7C40149866AF0086576A /* md5.1 */, + FC7A7C41149866AF0086576A /* md5.c */, + ); + path = md5; + sourceTree = ""; + }; + FC7A7C42149866AF0086576A /* nl */ = { + isa = PBXGroup; + children = ( + FC7A7C44149866AF0086576A /* nl.1 */, + FC7A7C45149866AF0086576A /* nl.c */, + ); + path = nl; + sourceTree = ""; + }; + FC7A7C46149866AF0086576A /* paste */ = { + isa = PBXGroup; + children = ( + FC7A7C48149866AF0086576A /* paste.1 */, + FC7A7C49149866AF0086576A /* paste.c */, + ); + path = paste; + sourceTree = ""; + }; + FC7A7C4A149866AF0086576A /* pr */ = { + isa = PBXGroup; + children = ( + FC7A7C4B149866AF0086576A /* egetopt.c */, + FC7A7C4C149866AF0086576A /* extern.h */, + FC7A7C4E149866AF0086576A /* pr.1 */, + FC7A7C4F149866AF0086576A /* pr.c */, + FC7A7C50149866AF0086576A /* pr.h */, + ); + path = pr; + sourceTree = ""; + }; + FC7A7C51149866AF0086576A /* rev */ = { + isa = PBXGroup; + children = ( + FC7A7C53149866AF0086576A /* rev.1 */, + FC7A7C54149866AF0086576A /* rev.c */, + ); + path = rev; + sourceTree = ""; + }; + FC7A7C55149866AF0086576A /* rs */ = { + isa = PBXGroup; + children = ( + FC7A7C57149866AF0086576A /* rs.1 */, + FC7A7C58149866AF0086576A /* rs.c */, + ); + path = rs; + sourceTree = ""; + }; + 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 = ""; + }; + FC7A7C63149866AF0086576A /* TEST */ = { + isa = PBXGroup; + children = ( + FC7A7C64149866AF0086576A /* hanoi.sed */, + FC7A7C65149866AF0086576A /* math.sed */, + FC7A7C66149866AF0086576A /* sed.test */, + ); + path = TEST; + sourceTree = ""; + }; + 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 = ""; + }; + FC7A7CB2149866AF0086576A /* split */ = { + isa = PBXGroup; + children = ( + FC7A7CB4149866AF0086576A /* split.1 */, + FC7A7CB5149866AF0086576A /* split.c */, + ); + path = split; + sourceTree = ""; + }; + 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 = ""; + }; + 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 = ""; + }; + FC7A7CCA149866AF0086576A /* ul */ = { + isa = PBXGroup; + children = ( + FC7A7CCC149866AF0086576A /* ul.1 */, + FC7A7CCD149866AF0086576A /* ul.c */, + FC6C98FB149A94EB00DDCC47 /* libcurses.dylib */, + ); + path = ul; + sourceTree = ""; + }; + FC7A7CCE149866AF0086576A /* unexpand */ = { + isa = PBXGroup; + children = ( + FC7A7CD0149866AF0086576A /* unexpand.c */, + ); + path = unexpand; + sourceTree = ""; + }; + FC7A7CD1149866AF0086576A /* uniq */ = { + isa = PBXGroup; + children = ( + FC7A7CD3149866AF0086576A /* uniq.1 */, + FC7A7CD4149866AF0086576A /* uniq.c */, + ); + path = uniq; + sourceTree = ""; + }; + FC7A7CD5149866AF0086576A /* unvis */ = { + isa = PBXGroup; + children = ( + FC7A7CD7149866AF0086576A /* unvis.1 */, + FC7A7CD8149866AF0086576A /* unvis.c */, + ); + path = unvis; + sourceTree = ""; + }; + FC7A7CD9149866AF0086576A /* vis */ = { + isa = PBXGroup; + children = ( + FC7A7CDA149866AF0086576A /* extern.h */, + FC7A7CDB149866AF0086576A /* foldit.c */, + FC7A7CDD149866AF0086576A /* vis.1 */, + FC7A7CDE149866AF0086576A /* vis.c */, + ); + path = vis; + sourceTree = ""; + }; + FC7A7CDF149866AF0086576A /* wc */ = { + isa = PBXGroup; + children = ( + FC7A7CE1149866AF0086576A /* wc.1 */, + FC7A7CE2149866AF0086576A /* wc.c */, + ); + path = wc; + sourceTree = ""; + }; + FD2BDC151B7188240053EE6B /* xcconfigs */ = { + isa = PBXGroup; + children = ( + FD2BDC161B7188240053EE6B /* ee.xcconfig */, + FD3562031B82803F008A70F6 /* grep.xcconfig */, + FDF2836E1DAED24800CF8C36 /* sort.xcconfig */, + ); + path = xcconfigs; + sourceTree = ""; + }; + 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 = ""; + }; + FDA49B8F1B7186CE003B4F3C /* ee */ = { + isa = PBXGroup; + children = ( + FDA49B961B718762003B4F3C /* ee.1 */, + FDA49B941B718738003B4F3C /* ee.c */, + ); + path = ee; + sourceTree = ""; + }; + FDF2835F1DAED0C000CF8C36 /* nls */ = { + isa = PBXGroup; + children = ( + FDF283601DAED0C000CF8C36 /* C.msg */, + FDF283611DAED0C000CF8C36 /* hu_HU.ISO8859-2.msg */, + ); + path = nls; + sourceTree = ""; + }; +/* 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 @@ + + + + + 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 +__FBSDID("$FreeBSD: src/usr.bin/tr/cmap.c,v 1.2 2004/07/14 08:36:09 tjr Exp $"); + +#include +#include +#include +#include +#include +#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 +#include +#include + +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 +__FBSDID("$FreeBSD: src/usr.bin/tr/cset.c,v 1.3 2004/07/14 08:33:14 tjr Exp $"); + +#include +#include +#include +#include +#include +#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 +#include +#include + +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 + +#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 + +__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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +.It "\eb +.It "\ef +.It "\en +.It "\er +.It "\et +.It "\ev +.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 +.It "alpha +.It "blank +.It "cntrl +.It "digit +.It "graph +.It "ideogram +.It "lower +.It "phonogram +.It "print +.It "punct +.It "rune +.It "space +.It "special +.It "upper +.It "xdigit +.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 + +__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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 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 + +__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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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, ""); + 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 + +__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 + +#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 + +__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 +#include +#include +#include +#include +#include +#include + +#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 , or 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 +__FBSDID("$FreeBSD: src/usr.bin/wc/wc.c,v 1.21 2004/12/27 22:27:56 josef Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 -- cgit v1.2.3-56-ge451