summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Gates <evan.gates@gmail.com>2016-09-26 14:10:05 -0700
committerEvan Gates <evan.gates@gmail.com>2016-09-26 14:10:05 -0700
commitd9bec403e27530294e6790e3692a2a112e1fe63e (patch)
tree9b315a2ae4a6c4a06874200bb324573e9a5945e8
parent4f870d894b290a9be67e1f3677da11dbcb0da8b6 (diff)
add 9base source, build rc, troff, and getflags
more stali.mk files can be added in the future when decisions are made as to which tools should come from 9base and which from sbase/ubase/hbase
-rw-r--r--bin/9base/.hgtags6
-rw-r--r--bin/9base/.origin1
-rw-r--r--bin/9base/LICENSE287
-rw-r--r--bin/9base/Makefile90
-rw-r--r--bin/9base/README24
-rw-r--r--bin/9base/TODO5
-rw-r--r--bin/9base/ascii/Makefile10
-rw-r--r--bin/9base/ascii/ascii.1160
-rw-r--r--bin/9base/ascii/ascii.c181
-rw-r--r--bin/9base/awk/Makefile13
-rw-r--r--bin/9base/awk/README13
-rw-r--r--bin/9base/awk/awk.1527
-rw-r--r--bin/9base/awk/awk.h185
-rw-r--r--bin/9base/awk/awkgram.y489
-rw-r--r--bin/9base/awk/lex.c570
-rw-r--r--bin/9base/awk/lib.c713
-rw-r--r--bin/9base/awk/main.c198
-rw-r--r--bin/9base/awk/maketab.c169
-rw-r--r--bin/9base/awk/parse.c272
-rw-r--r--bin/9base/awk/proctab.c206
-rw-r--r--bin/9base/awk/proto.h180
-rw-r--r--bin/9base/awk/re.c325
-rw-r--r--bin/9base/awk/run.c1899
-rw-r--r--bin/9base/awk/tran.c435
-rw-r--r--bin/9base/basename/Makefile10
-rw-r--r--bin/9base/basename/basename.135
-rw-r--r--bin/9base/basename/basename.c41
-rw-r--r--bin/9base/bc/Makefile19
-rw-r--r--bin/9base/bc/bc.1292
-rw-r--r--bin/9base/bc/bc.y988
-rw-r--r--bin/9base/bc/bclib246
-rw-r--r--bin/9base/cal/Makefile10
-rw-r--r--bin/9base/cal/cal.146
-rw-r--r--bin/9base/cal/cal.c313
-rw-r--r--bin/9base/cat/Makefile10
-rw-r--r--bin/9base/cat/cat.1108
-rw-r--r--bin/9base/cat/cat.c36
-rw-r--r--bin/9base/cleanname/Makefile10
-rw-r--r--bin/9base/cleanname/cleanname.132
-rw-r--r--bin/9base/cleanname/cleanname.c44
-rw-r--r--bin/9base/cmp/Makefile10
-rw-r--r--bin/9base/cmp/cmp.157
-rw-r--r--bin/9base/cmp/cmp.c112
-rw-r--r--bin/9base/config.mk25
-rw-r--r--bin/9base/date/Makefile10
-rw-r--r--bin/9base/date/date.159
-rw-r--r--bin/9base/date/date.c72
-rw-r--r--bin/9base/dc/Makefile10
-rw-r--r--bin/9base/dc/dc.1257
-rw-r--r--bin/9base/dc/dc.c2287
-rw-r--r--bin/9base/dd/Makefile10
-rw-r--r--bin/9base/dd/dd.10
-rw-r--r--bin/9base/dd/dd.c660
-rw-r--r--bin/9base/diff/Makefile12
-rw-r--r--bin/9base/diff/diff.1163
-rw-r--r--bin/9base/diff/diff.h27
-rw-r--r--bin/9base/diff/diffdir.c113
-rw-r--r--bin/9base/diff/diffio.c387
-rw-r--r--bin/9base/diff/diffreg.c420
-rw-r--r--bin/9base/diff/main.c270
-rw-r--r--bin/9base/du/Makefile10
-rw-r--r--bin/9base/du/du.19
-rw-r--r--bin/9base/du/du.c194
-rw-r--r--bin/9base/echo/Makefile10
-rw-r--r--bin/9base/echo/echo.126
-rw-r--r--bin/9base/echo/echo.c38
-rw-r--r--bin/9base/ed/Makefile10
-rw-r--r--bin/9base/ed/ed.1683
-rw-r--r--bin/9base/ed/ed.c1617
-rw-r--r--bin/9base/factor/Makefile11
-rw-r--r--bin/9base/factor/factor.166
-rw-r--r--bin/9base/factor/factor.c96
-rw-r--r--bin/9base/fmt/Makefile10
-rw-r--r--bin/9base/fmt/fmt.190
-rw-r--r--bin/9base/fmt/fmt.c240
-rw-r--r--bin/9base/fortune/Makefile11
-rw-r--r--bin/9base/fortune/fortune.123
-rw-r--r--bin/9base/fortune/fortune.c92
-rw-r--r--bin/9base/freq/Makefile11
-rw-r--r--bin/9base/freq/freq.140
-rw-r--r--bin/9base/freq/freq.c111
-rw-r--r--bin/9base/getflags/Makefile11
-rw-r--r--bin/9base/getflags/getflags.177
-rw-r--r--bin/9base/getflags/getflags.c82
-rw-r--r--bin/9base/getflags/stali.mk12
-rw-r--r--bin/9base/grep/Makefile13
-rw-r--r--bin/9base/grep/comp.c277
-rw-r--r--bin/9base/grep/grep.1124
-rw-r--r--bin/9base/grep/grep.h125
-rw-r--r--bin/9base/grep/grep.y226
-rw-r--r--bin/9base/grep/main.c267
-rw-r--r--bin/9base/grep/sub.c317
-rw-r--r--bin/9base/hoc/Makefile13
-rw-r--r--bin/9base/hoc/code.c666
-rw-r--r--bin/9base/hoc/hoc.1144
-rw-r--r--bin/9base/hoc/hoc.h83
-rw-r--r--bin/9base/hoc/hoc.y398
-rw-r--r--bin/9base/hoc/init.c69
-rw-r--r--bin/9base/hoc/math.c75
-rw-r--r--bin/9base/hoc/symbol.c55
-rw-r--r--bin/9base/join/Makefile10
-rw-r--r--bin/9base/join/join.1147
-rw-r--r--bin/9base/join/join.c369
-rw-r--r--bin/9base/lib9/LICENSE19
-rw-r--r--bin/9base/lib9/Makefile231
-rw-r--r--bin/9base/lib9/_exits.c10
-rw-r--r--bin/9base/lib9/_p9dialparse.c184
-rw-r--r--bin/9base/lib9/_p9dir.c243
-rw-r--r--bin/9base/lib9/_p9translate.c46
-rw-r--r--bin/9base/lib9/announce.c152
-rw-r--r--bin/9base/lib9/argv0.c9
-rw-r--r--bin/9base/lib9/atexit.c56
-rw-r--r--bin/9base/lib9/atnotify.c58
-rw-r--r--bin/9base/lib9/atoi.c9
-rw-r--r--bin/9base/lib9/atol.c9
-rw-r--r--bin/9base/lib9/atoll.c9
-rw-r--r--bin/9base/lib9/auth.h169
-rw-r--r--bin/9base/lib9/await.c137
-rw-r--r--bin/9base/lib9/bio.h91
-rw-r--r--bin/9base/lib9/bio/_lib9.h12
-rw-r--r--bin/9base/lib9/bio/bbuffered.c20
-rw-r--r--bin/9base/lib9/bio/bcat.c46
-rw-r--r--bin/9base/lib9/bio/bfildes.c9
-rw-r--r--bin/9base/lib9/bio/bflush.c33
-rw-r--r--bin/9base/lib9/bio/bgetc.c53
-rw-r--r--bin/9base/lib9/bio/bgetd.c36
-rw-r--r--bin/9base/lib9/bio/bgetrune.c47
-rw-r--r--bin/9base/lib9/bio/binit.c153
-rw-r--r--bin/9base/lib9/bio/boffset.c25
-rw-r--r--bin/9base/lib9/bio/bprint.c14
-rw-r--r--bin/9base/lib9/bio/bputc.c20
-rw-r--r--bin/9base/lib9/bio/bputrune.c23
-rw-r--r--bin/9base/lib9/bio/brdline.c94
-rw-r--r--bin/9base/lib9/bio/brdstr.c111
-rw-r--r--bin/9base/lib9/bio/bread.c45
-rw-r--r--bin/9base/lib9/bio/bseek.c60
-rw-r--r--bin/9base/lib9/bio/bvprint.c38
-rw-r--r--bin/9base/lib9/bio/bwrite.c38
-rw-r--r--bin/9base/lib9/bio/lib9.std.h26
-rw-r--r--bin/9base/lib9/cistrcmp.c26
-rw-r--r--bin/9base/lib9/cistrncmp.c28
-rw-r--r--bin/9base/lib9/cistrstr.c23
-rw-r--r--bin/9base/lib9/cleanname.c52
-rw-r--r--bin/9base/lib9/convD2M.c132
-rw-r--r--bin/9base/lib9/convM2D.c130
-rw-r--r--bin/9base/lib9/convM2S.c352
-rw-r--r--bin/9base/lib9/convS2M.c435
-rw-r--r--bin/9base/lib9/create.c75
-rwxr-xr-xbin/9base/lib9/crypt.c68
-rw-r--r--bin/9base/lib9/ctime.c181
-rw-r--r--bin/9base/lib9/date.c125
-rw-r--r--bin/9base/lib9/debugmalloc.c166
-rw-r--r--bin/9base/lib9/dial.c149
-rw-r--r--bin/9base/lib9/dirfstat.c29
-rw-r--r--bin/9base/lib9/dirfwstat.c66
-rw-r--r--bin/9base/lib9/dirmodefmt.c62
-rw-r--r--bin/9base/lib9/dirread.c212
-rw-r--r--bin/9base/lib9/dirstat.c32
-rw-r--r--bin/9base/lib9/dirwstat.c31
-rw-r--r--bin/9base/lib9/dup.c12
-rw-r--r--bin/9base/lib9/encodefmt.c76
-rw-r--r--bin/9base/lib9/errstr.c81
-rw-r--r--bin/9base/lib9/exec.c9
-rw-r--r--bin/9base/lib9/execl.c29
-rw-r--r--bin/9base/lib9/exitcode.c9
-rw-r--r--bin/9base/lib9/fcall.h149
-rw-r--r--bin/9base/lib9/fcallfmt.c253
-rw-r--r--bin/9base/lib9/fmt.h116
-rw-r--r--bin/9base/lib9/fmt/LICENSE22
-rw-r--r--bin/9base/lib9/fmt/NOTICE19
-rw-r--r--bin/9base/lib9/fmt/README19
-rw-r--r--bin/9base/lib9/fmt/charstod.c73
-rw-r--r--bin/9base/lib9/fmt/dofmt.c617
-rw-r--r--bin/9base/lib9/fmt/dorfmt.c50
-rw-r--r--bin/9base/lib9/fmt/errfmt.c16
-rw-r--r--bin/9base/lib9/fmt/fltfmt.c668
-rw-r--r--bin/9base/lib9/fmt/fmt.c220
-rw-r--r--bin/9base/lib9/fmt/fmtdef.h105
-rw-r--r--bin/9base/lib9/fmt/fmtfd.c36
-rw-r--r--bin/9base/lib9/fmt/fmtfdflush.c22
-rw-r--r--bin/9base/lib9/fmt/fmtlocale.c55
-rw-r--r--bin/9base/lib9/fmt/fmtlock.c15
-rw-r--r--bin/9base/lib9/fmt/fmtnull.c33
-rw-r--r--bin/9base/lib9/fmt/fmtprint.c36
-rw-r--r--bin/9base/lib9/fmt/fmtquote.c259
-rw-r--r--bin/9base/lib9/fmt/fmtrune.c28
-rw-r--r--bin/9base/lib9/fmt/fmtstr.c16
-rw-r--r--bin/9base/lib9/fmt/fmtvprint.c37
-rw-r--r--bin/9base/lib9/fmt/fprint.c17
-rw-r--r--bin/9base/lib9/fmt/nan.h4
-rw-r--r--bin/9base/lib9/fmt/nan64.c76
-rw-r--r--bin/9base/lib9/fmt/plan9.h38
-rw-r--r--bin/9base/lib9/fmt/pow10.c45
-rw-r--r--bin/9base/lib9/fmt/print.c17
-rw-r--r--bin/9base/lib9/fmt/runefmtstr.c16
-rw-r--r--bin/9base/lib9/fmt/runeseprint.c18
-rw-r--r--bin/9base/lib9/fmt/runesmprint.c18
-rw-r--r--bin/9base/lib9/fmt/runesnprint.c19
-rw-r--r--bin/9base/lib9/fmt/runesprint.c18
-rw-r--r--bin/9base/lib9/fmt/runevseprint.c29
-rw-r--r--bin/9base/lib9/fmt/runevsmprint.c86
-rw-r--r--bin/9base/lib9/fmt/runevsnprint.c28
-rw-r--r--bin/9base/lib9/fmt/seprint.c17
-rw-r--r--bin/9base/lib9/fmt/smprint.c17
-rw-r--r--bin/9base/lib9/fmt/snprint.c18
-rw-r--r--bin/9base/lib9/fmt/sprint.c30
-rw-r--r--bin/9base/lib9/fmt/strtod.c520
-rw-r--r--bin/9base/lib9/fmt/strtod.h4
-rw-r--r--bin/9base/lib9/fmt/test.c53
-rw-r--r--bin/9base/lib9/fmt/test2.c9
-rw-r--r--bin/9base/lib9/fmt/test3.c52
-rw-r--r--bin/9base/lib9/fmt/vfprint.c21
-rw-r--r--bin/9base/lib9/fmt/vseprint.c28
-rw-r--r--bin/9base/lib9/fmt/vsmprint.c83
-rw-r--r--bin/9base/lib9/fmt/vsnprint.c28
-rw-r--r--bin/9base/lib9/fmtlock2.c16
-rw-r--r--bin/9base/lib9/fork.c22
-rw-r--r--bin/9base/lib9/get9root.c18
-rw-r--r--bin/9base/lib9/getcallerpc-386.c7
-rw-r--r--bin/9base/lib9/getcallerpc-PowerMacintosh.c7
-rw-r--r--bin/9base/lib9/getcallerpc-arm.c8
-rw-r--r--bin/9base/lib9/getcallerpc-power.c11
-rw-r--r--bin/9base/lib9/getcallerpc-ppc.c7
-rw-r--r--bin/9base/lib9/getcallerpc-sun4u.s5
-rw-r--r--bin/9base/lib9/getcallerpc-x86_64.c7
-rw-r--r--bin/9base/lib9/getenv.c26
-rw-r--r--bin/9base/lib9/getfields.c36
-rw-r--r--bin/9base/lib9/getnetconn.c134
-rw-r--r--bin/9base/lib9/getns.c97
-rw-r--r--bin/9base/lib9/getuser.c16
-rw-r--r--bin/9base/lib9/getwd.c10
-rw-r--r--bin/9base/lib9/jmp-FreeBSD.s3
-rw-r--r--bin/9base/lib9/jmp.c17
-rw-r--r--bin/9base/lib9/lib9.h2
-rw-r--r--bin/9base/lib9/libc.h949
-rw-r--r--bin/9base/lib9/lnrand.c18
-rw-r--r--bin/9base/lib9/lock.c55
-rw-r--r--bin/9base/lib9/lrand.c83
-rw-r--r--bin/9base/lib9/main.c13
-rw-r--r--bin/9base/lib9/malloc.c56
-rw-r--r--bin/9base/lib9/malloctag.c15
-rw-r--r--bin/9base/lib9/mallocz.c15
-rw-r--r--bin/9base/lib9/nan.c27
-rw-r--r--bin/9base/lib9/nan.h4
-rw-r--r--bin/9base/lib9/needsrcquote.c12
-rw-r--r--bin/9base/lib9/needstack.c8
-rw-r--r--bin/9base/lib9/netcrypt.c18
-rw-r--r--bin/9base/lib9/netmkaddr.c62
-rw-r--r--bin/9base/lib9/notify.c273
-rw-r--r--bin/9base/lib9/nrand.c17
-rw-r--r--bin/9base/lib9/nulldir.c9
-rw-r--r--bin/9base/lib9/open.c62
-rw-r--r--bin/9base/lib9/opentemp.c20
-rw-r--r--bin/9base/lib9/pin.c11
-rw-r--r--bin/9base/lib9/pipe.c15
-rw-r--r--bin/9base/lib9/post9p.c83
-rw-r--r--bin/9base/lib9/postnote.c34
-rw-r--r--bin/9base/lib9/priv.c32
-rw-r--r--bin/9base/lib9/qlock.c166
-rw-r--r--bin/9base/lib9/quote.c136
-rw-r--r--bin/9base/lib9/rand.c7
-rw-r--r--bin/9base/lib9/read9pmsg.c31
-rw-r--r--bin/9base/lib9/readcons.c101
-rw-r--r--bin/9base/lib9/readn.c21
-rw-r--r--bin/9base/lib9/regex/README7
-rw-r--r--bin/9base/lib9/regex/cvt11
-rw-r--r--bin/9base/lib9/regex/lib9.h2
-rw-r--r--bin/9base/lib9/regex/lib9.std.h10
-rw-r--r--bin/9base/lib9/regex/regaux.c112
-rw-r--r--bin/9base/lib9/regex/regcomp.c555
-rw-r--r--bin/9base/lib9/regex/regcomp.h74
-rw-r--r--bin/9base/lib9/regex/regerror.c14
-rw-r--r--bin/9base/lib9/regex/regexec.c231
-rw-r--r--bin/9base/lib9/regex/regsub.c63
-rw-r--r--bin/9base/lib9/regex/rregexec.c212
-rw-r--r--bin/9base/lib9/regex/rregsub.c63
-rw-r--r--bin/9base/lib9/regex/test.c46
-rw-r--r--bin/9base/lib9/regex/test2.c20
-rw-r--r--bin/9base/lib9/regexp.7133
-rw-r--r--bin/9base/lib9/regexp.h1
-rw-r--r--bin/9base/lib9/regexp9.h96
-rw-r--r--bin/9base/lib9/rfork.c128
-rw-r--r--bin/9base/lib9/searchpath.c62
-rw-r--r--bin/9base/lib9/sec/libsec.h366
-rw-r--r--bin/9base/lib9/sec/md5.c147
-rw-r--r--bin/9base/lib9/sec/md5block.c267
-rw-r--r--bin/9base/lib9/sec/md5pickle.c39
-rw-r--r--bin/9base/lib9/sec/os.h2
-rw-r--r--bin/9base/lib9/sec/sha1.c127
-rw-r--r--bin/9base/lib9/sec/sha1block.c187
-rw-r--r--bin/9base/lib9/sec/sha1pickle.c38
-rw-r--r--bin/9base/lib9/seek.c8
-rw-r--r--bin/9base/lib9/sendfd.c88
-rw-r--r--bin/9base/lib9/sleep.c47
-rw-r--r--bin/9base/lib9/st4nkO6D1
-rw-r--r--bin/9base/lib9/stali.mk213
-rw-r--r--bin/9base/lib9/strdup.c17
-rw-r--r--bin/9base/lib9/strecpy.c16
-rw-r--r--bin/9base/lib9/sysfatal.c21
-rw-r--r--bin/9base/lib9/syslog.c119
-rw-r--r--bin/9base/lib9/sysname.c30
-rw-r--r--bin/9base/lib9/test.c8
-rw-r--r--bin/9base/lib9/testfltfmt.c183
-rw-r--r--bin/9base/lib9/testfmt.c148
-rw-r--r--bin/9base/lib9/testfork.c21
-rw-r--r--bin/9base/lib9/testprint.c14
-rw-r--r--bin/9base/lib9/time.c58
-rw-r--r--bin/9base/lib9/tm2sec.c110
-rw-r--r--bin/9base/lib9/tokenize.c106
-rw-r--r--bin/9base/lib9/truerand.c27
-rw-r--r--bin/9base/lib9/u.h190
-rw-r--r--bin/9base/lib9/u16.c52
-rw-r--r--bin/9base/lib9/u32.c109
-rw-r--r--bin/9base/lib9/u64.c126
-rw-r--r--bin/9base/lib9/udp.c52
-rw-r--r--bin/9base/lib9/unsharp.c44
-rw-r--r--bin/9base/lib9/utf.h54
-rw-r--r--bin/9base/lib9/utf/LICENSE13
-rw-r--r--bin/9base/lib9/utf/NOTICE13
-rw-r--r--bin/9base/lib9/utf/README13
-rw-r--r--bin/9base/lib9/utf/plan9.h29
-rw-r--r--bin/9base/lib9/utf/rune.c217
-rw-r--r--bin/9base/lib9/utf/runestrcat.c25
-rw-r--r--bin/9base/lib9/utf/runestrchr.c35
-rw-r--r--bin/9base/lib9/utf/runestrcmp.c35
-rw-r--r--bin/9base/lib9/utf/runestrcpy.c28
-rw-r--r--bin/9base/lib9/utf/runestrdup.c30
-rw-r--r--bin/9base/lib9/utf/runestrecpy.c32
-rw-r--r--bin/9base/lib9/utf/runestrlen.c24
-rw-r--r--bin/9base/lib9/utf/runestrncat.c32
-rw-r--r--bin/9base/lib9/utf/runestrncmp.c37
-rw-r--r--bin/9base/lib9/utf/runestrncpy.c33
-rw-r--r--bin/9base/lib9/utf/runestrrchr.c30
-rw-r--r--bin/9base/lib9/utf/runestrstr.c44
-rw-r--r--bin/9base/lib9/utf/runetype.c1151
-rw-r--r--bin/9base/lib9/utf/utfecpy.c37
-rw-r--r--bin/9base/lib9/utf/utflen.c37
-rw-r--r--bin/9base/lib9/utf/utfnlen.c41
-rw-r--r--bin/9base/lib9/utf/utfrrune.c45
-rw-r--r--bin/9base/lib9/utf/utfrune.c44
-rw-r--r--bin/9base/lib9/utf/utfutf.c41
-rw-r--r--bin/9base/lib9/wait.c54
-rw-r--r--bin/9base/lib9/waitpid.c20
-rw-r--r--bin/9base/lib9/write.c23
-rw-r--r--bin/9base/lib9/zoneinfo.c215
-rw-r--r--bin/9base/lib9/zoneinfo.h19
-rw-r--r--bin/9base/listen1/Makefile10
-rw-r--r--bin/9base/listen1/listen1.139
-rw-r--r--bin/9base/listen1/listen1.c105
-rw-r--r--bin/9base/look/Makefile10
-rw-r--r--bin/9base/look/look.185
-rw-r--r--bin/9base/look/look.c349
-rw-r--r--bin/9base/ls/Makefile11
-rw-r--r--bin/9base/ls/ls.1172
-rw-r--r--bin/9base/ls/ls.c308
-rw-r--r--bin/9base/md5sum/Makefile10
-rw-r--r--bin/9base/md5sum/md5sum.10
-rw-r--r--bin/9base/md5sum/md5sum.c61
-rw-r--r--bin/9base/mk/Makefile17
-rw-r--r--bin/9base/mk/NOTICE27
-rw-r--r--bin/9base/mk/README7
-rw-r--r--bin/9base/mk/arc.c52
-rw-r--r--bin/9base/mk/archive.c253
-rw-r--r--bin/9base/mk/bufblock.c88
-rw-r--r--bin/9base/mk/env.c149
-rw-r--r--bin/9base/mk/file.c90
-rw-r--r--bin/9base/mk/fns.h88
-rw-r--r--bin/9base/mk/graph.c279
-rw-r--r--bin/9base/mk/job.c33
-rw-r--r--bin/9base/mk/lex.c146
-rw-r--r--bin/9base/mk/main.c287
-rw-r--r--bin/9base/mk/match.c49
-rw-r--r--bin/9base/mk/mk.1691
-rw-r--r--bin/9base/mk/mk.c234
-rw-r--r--bin/9base/mk/mk.h185
-rw-r--r--bin/9base/mk/mkfile.test12
-rw-r--r--bin/9base/mk/parse.c318
-rw-r--r--bin/9base/mk/rc.c194
-rw-r--r--bin/9base/mk/recipe.c117
-rw-r--r--bin/9base/mk/rule.c112
-rw-r--r--bin/9base/mk/run.c296
-rw-r--r--bin/9base/mk/sh.c206
-rw-r--r--bin/9base/mk/shell.c80
-rw-r--r--bin/9base/mk/shprint.c125
-rw-r--r--bin/9base/mk/symtab.c97
-rw-r--r--bin/9base/mk/sys.h5
-rw-r--r--bin/9base/mk/sys.std.h27
-rw-r--r--bin/9base/mk/unix.c341
-rw-r--r--bin/9base/mk/var.c41
-rw-r--r--bin/9base/mk/varsub.c252
-rw-r--r--bin/9base/mk/word.c189
-rw-r--r--bin/9base/mkdir/Makefile11
-rw-r--r--bin/9base/mkdir/mkdir.143
-rw-r--r--bin/9base/mkdir/mkdir.c80
-rw-r--r--bin/9base/mtime/Makefile11
-rw-r--r--bin/9base/mtime/mtime.122
-rw-r--r--bin/9base/mtime/mtime.c33
-rw-r--r--bin/9base/pbd/Makefile10
-rw-r--r--bin/9base/pbd/pbd.10
-rw-r--r--bin/9base/pbd/pbd.c19
-rw-r--r--bin/9base/primes/Makefile11
-rw-r--r--bin/9base/primes/primes.10
-rw-r--r--bin/9base/primes/primes.c131
-rw-r--r--bin/9base/rc/Makefile53
-rw-r--r--bin/9base/rc/_ytab0
-rw-r--r--bin/9base/rc/code.c486
-rw-r--r--bin/9base/rc/exec.c1000
-rw-r--r--bin/9base/rc/exec.h76
-rw-r--r--bin/9base/rc/fmtquote.c162
-rw-r--r--bin/9base/rc/fns.h67
-rw-r--r--bin/9base/rc/getflags.c244
-rw-r--r--bin/9base/rc/getflags.h7
-rw-r--r--bin/9base/rc/glob.c254
-rw-r--r--bin/9base/rc/havefork.c242
-rw-r--r--bin/9base/rc/haventfork.c230
-rw-r--r--bin/9base/rc/here.c150
-rw-r--r--bin/9base/rc/io.c261
-rw-r--r--bin/9base/rc/io.h30
-rw-r--r--bin/9base/rc/lex.c378
-rw-r--r--bin/9base/rc/pcmd.c147
-rw-r--r--bin/9base/rc/pfnc.c71
-rw-r--r--bin/9base/rc/plan9ish.c604
-rw-r--r--bin/9base/rc/rc.1997
-rw-r--r--bin/9base/rc/rc.h146
-rw-r--r--bin/9base/rc/rcmain39
-rw-r--r--bin/9base/rc/simple.c504
-rw-r--r--bin/9base/rc/stali.mk32
-rw-r--r--bin/9base/rc/subr.c77
-rw-r--r--bin/9base/rc/syn.y91
-rw-r--r--bin/9base/rc/trap.c37
-rw-r--r--bin/9base/rc/tree.c146
-rw-r--r--bin/9base/rc/unixcrap.c236
-rw-r--r--bin/9base/rc/var.c173
-rw-r--r--bin/9base/rc/x.tab.h34
-rw-r--r--bin/9base/rc/y.tab.c1059
-rw-r--r--bin/9base/read/Makefile10
-rw-r--r--bin/9base/read/read.1123
-rw-r--r--bin/9base/read/read.c123
-rw-r--r--bin/9base/rm/Makefile10
-rw-r--r--bin/9base/rm/rm.128
-rw-r--r--bin/9base/rm/rm.c112
-rw-r--r--bin/9base/sam/Makefile37
-rw-r--r--bin/9base/sam/README29
-rw-r--r--bin/9base/sam/_libc.h40
-rw-r--r--bin/9base/sam/address.c240
-rw-r--r--bin/9base/sam/buff.c302
-rw-r--r--bin/9base/sam/cmd.c608
-rw-r--r--bin/9base/sam/disk.c122
-rw-r--r--bin/9base/sam/err39
-rw-r--r--bin/9base/sam/error.c144
-rw-r--r--bin/9base/sam/errors.h65
-rw-r--r--bin/9base/sam/file.c610
-rw-r--r--bin/9base/sam/io.c278
-rw-r--r--bin/9base/sam/list.c96
-rw-r--r--bin/9base/sam/mesg.c848
-rw-r--r--bin/9base/sam/mesg.h131
-rw-r--r--bin/9base/sam/moveto.c173
-rw-r--r--bin/9base/sam/multi.c123
-rw-r--r--bin/9base/sam/parse.h68
-rw-r--r--bin/9base/sam/plan9.c185
-rw-r--r--bin/9base/sam/plumb.h17
-rw-r--r--bin/9base/sam/rasp.c340
-rw-r--r--bin/9base/sam/regexp.c802
-rw-r--r--bin/9base/sam/sam.1908
-rw-r--r--bin/9base/sam/sam.c741
-rw-r--r--bin/9base/sam/sam.h408
-rw-r--r--bin/9base/sam/shell.c165
-rw-r--r--bin/9base/sam/string.c193
-rw-r--r--bin/9base/sam/sys.c60
-rw-r--r--bin/9base/sam/unix.c222
-rw-r--r--bin/9base/sam/util.c54
-rw-r--r--bin/9base/sam/xec.c508
-rw-r--r--bin/9base/sed/Makefile10
-rw-r--r--bin/9base/sed/sed.1387
-rw-r--r--bin/9base/sed/sed.c1453
-rw-r--r--bin/9base/seq/Makefile10
-rw-r--r--bin/9base/seq/seq.175
-rw-r--r--bin/9base/seq/seq.c92
-rw-r--r--bin/9base/sha1sum/Makefile10
-rw-r--r--bin/9base/sha1sum/sha1sum.10
-rw-r--r--bin/9base/sha1sum/sha1sum.c61
-rw-r--r--bin/9base/sleep/Makefile13
-rw-r--r--bin/9base/sleep/sleep.131
-rw-r--r--bin/9base/sleep/sleep.c13
-rw-r--r--bin/9base/sort/Makefile10
-rw-r--r--bin/9base/sort/sort.1262
-rw-r--r--bin/9base/sort/sort.c1766
-rw-r--r--bin/9base/split/Makefile10
-rw-r--r--bin/9base/split/split.182
-rw-r--r--bin/9base/split/split.c189
-rw-r--r--bin/9base/ssam/Makefile21
-rw-r--r--bin/9base/ssam/ssam41
-rw-r--r--bin/9base/ssam/ssam.172
-rw-r--r--bin/9base/stali.mk13
-rw-r--r--bin/9base/std.mk35
-rw-r--r--bin/9base/strings/Makefile10
-rw-r--r--bin/9base/strings/strings.128
-rw-r--r--bin/9base/strings/strings.c90
-rw-r--r--bin/9base/tail/Makefile11
-rw-r--r--bin/9base/tail/tail.187
-rw-r--r--bin/9base/tail/tail.c363
-rw-r--r--bin/9base/tee/Makefile10
-rw-r--r--bin/9base/tee/tee.128
-rw-r--r--bin/9base/tee/tee.c75
-rw-r--r--bin/9base/test/Makefile13
-rw-r--r--bin/9base/test/test.1211
-rw-r--r--bin/9base/test/test.c411
-rw-r--r--bin/9base/touch/Makefile10
-rw-r--r--bin/9base/touch/touch.135
-rw-r--r--bin/9base/touch/touch.c68
-rw-r--r--bin/9base/tr/Makefile10
-rw-r--r--bin/9base/tr/tr.197
-rw-r--r--bin/9base/tr/tr.c356
-rw-r--r--bin/9base/troff/FIXES821
-rw-r--r--bin/9base/troff/Makefile23
-rw-r--r--bin/9base/troff/README31
-rw-r--r--bin/9base/troff/cvt45
-rw-r--r--bin/9base/troff/dwbinit.c318
-rw-r--r--bin/9base/troff/dwbinit.h19
-rw-r--r--bin/9base/troff/ext.h187
-rw-r--r--bin/9base/troff/find1
-rw-r--r--bin/9base/troff/fns.h391
-rw-r--r--bin/9base/troff/font/devutf/0100to25ff1596
-rw-r--r--bin/9base/troff/font/devutf/AB306
-rw-r--r--bin/9base/troff/font/devutf/AI306
-rw-r--r--bin/9base/troff/font/devutf/AR306
-rw-r--r--bin/9base/troff/font/devutf/AX306
-rw-r--r--bin/9base/troff/font/devutf/B306
-rw-r--r--bin/9base/troff/font/devutf/BI306
-rw-r--r--bin/9base/troff/font/devutf/C1900
-rw-r--r--bin/9base/troff/font/devutf/CB304
-rw-r--r--bin/9base/troff/font/devutf/CI304
-rw-r--r--bin/9base/troff/font/devutf/CO304
-rw-r--r--bin/9base/troff/font/devutf/CW1900
-rw-r--r--bin/9base/troff/font/devutf/CX304
-rw-r--r--bin/9base/troff/font/devutf/CY136
-rw-r--r--bin/9base/troff/font/devutf/DESC31
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSans1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSansBold1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSansBoldOblique1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSansOblique1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSerif1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSerifBold1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSerifBoldOblique1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuCondensedSerifOblique1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuMonoSans1357
-rw-r--r--bin/9base/troff/font/devutf/DejaVuMonoSansBold1216
-rw-r--r--bin/9base/troff/font/devutf/DejaVuMonoSansBoldOblique1230
-rw-r--r--bin/9base/troff/font/devutf/DejaVuMonoSansOblique1357
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSans1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSansBold1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSansBoldOblique1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSansOblique1717
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSerif1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSerifBold1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSerifBoldOblique1469
-rw-r--r--bin/9base/troff/font/devutf/DejaVuSerifOblique1469
-rw-r--r--bin/9base/troff/font/devutf/GR105
-rw-r--r--bin/9base/troff/font/devutf/H1902
-rw-r--r--bin/9base/troff/font/devutf/HB306
-rw-r--r--bin/9base/troff/font/devutf/HI306
-rw-r--r--bin/9base/troff/font/devutf/HK305
-rw-r--r--bin/9base/troff/font/devutf/HL305
-rw-r--r--bin/9base/troff/font/devutf/HM306
-rw-r--r--bin/9base/troff/font/devutf/HX306
-rw-r--r--bin/9base/troff/font/devutf/Helvetica-Narrow306
-rw-r--r--bin/9base/troff/font/devutf/Helvetica-Narrow-Bold306
-rw-r--r--bin/9base/troff/font/devutf/Helvetica-Narrow-BoldOblique306
-rw-r--r--bin/9base/troff/font/devutf/Helvetica-Narrow-Oblique306
-rw-r--r--bin/9base/troff/font/devutf/I305
-rw-r--r--bin/9base/troff/font/devutf/Jp7
-rw-r--r--bin/9base/troff/font/devutf/KB306
-rw-r--r--bin/9base/troff/font/devutf/KI306
-rw-r--r--bin/9base/troff/font/devutf/KR306
-rw-r--r--bin/9base/troff/font/devutf/KX306
-rw-r--r--bin/9base/troff/font/devutf/LINKFILE6
-rw-r--r--bin/9base/troff/font/devutf/LucidaCW192
-rw-r--r--bin/9base/troff/font/devutf/LucidaSans1803
-rw-r--r--bin/9base/troff/font/devutf/LucidaSansB194
-rw-r--r--bin/9base/troff/font/devutf/LucidaSansCW192
-rw-r--r--bin/9base/troff/font/devutf/LucidaSansCW83192
-rw-r--r--bin/9base/troff/font/devutf/LucidaSansI194
-rw-r--r--bin/9base/troff/font/devutf/LuxiMono396
-rw-r--r--bin/9base/troff/font/devutf/LuxiMono-Bold396
-rw-r--r--bin/9base/troff/font/devutf/LuxiMono-BoldOblique395
-rw-r--r--bin/9base/troff/font/devutf/LuxiMono-Oblique395
-rw-r--r--bin/9base/troff/font/devutf/LuxiSans392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSans-Bold392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSans-BoldOblique392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSans-Oblique392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSerif392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSerif-Bold392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSerif-BoldOblique392
-rw-r--r--bin/9base/troff/font/devutf/LuxiSerif-Oblique392
-rw-r--r--bin/9base/troff/font/devutf/NB306
-rw-r--r--bin/9base/troff/font/devutf/NI306
-rw-r--r--bin/9base/troff/font/devutf/NR306
-rw-r--r--bin/9base/troff/font/devutf/NX306
-rw-r--r--bin/9base/troff/font/devutf/PA1902
-rw-r--r--bin/9base/troff/font/devutf/PB306
-rw-r--r--bin/9base/troff/font/devutf/PI306
-rw-r--r--bin/9base/troff/font/devutf/PX306
-rw-r--r--bin/9base/troff/font/devutf/R1902
-rw-r--r--bin/9base/troff/font/devutf/R.nomath1542
-rw-r--r--bin/9base/troff/font/devutf/S298
-rw-r--r--bin/9base/troff/font/devutf/S132
-rw-r--r--bin/9base/troff/font/devutf/Syntax98
-rw-r--r--bin/9base/troff/font/devutf/SyntaxB98
-rw-r--r--bin/9base/troff/font/devutf/SyntaxI98
-rw-r--r--bin/9base/troff/font/devutf/UnivMath1105
-rw-r--r--bin/9base/troff/font/devutf/UnivMath2104
-rw-r--r--bin/9base/troff/font/devutf/UnivMath3105
-rw-r--r--bin/9base/troff/font/devutf/UnivMath4105
-rw-r--r--bin/9base/troff/font/devutf/UnivMath5105
-rw-r--r--bin/9base/troff/font/devutf/UnivMath6105
-rw-r--r--bin/9base/troff/font/devutf/ZD289
-rw-r--r--bin/9base/troff/font/devutf/ZI308
-rw-r--r--bin/9base/troff/font/devutf/charlib/#263a12
-rw-r--r--bin/9base/troff/font/devutf/charlib/1218
-rw-r--r--bin/9base/troff/font/devutf/charlib/1418
-rw-r--r--bin/9base/troff/font/devutf/charlib/3418
-rw-r--r--bin/9base/troff/font/devutf/charlib/DG27
-rw-r--r--bin/9base/troff/font/devutf/charlib/FA486
-rw-r--r--bin/9base/troff/font/devutf/charlib/F_i4
-rw-r--r--bin/9base/troff/font/devutf/charlib/F_l4
-rw-r--r--bin/9base/troff/font/devutf/charlib/L1159
-rw-r--r--bin/9base/troff/font/devutf/charlib/LA41
-rw-r--r--bin/9base/troff/font/devutf/charlib/LH3764
-rw-r--r--bin/9base/troff/font/devutf/charlib/LH.example131
-rw-r--r--bin/9base/troff/font/devutf/charlib/LV202
-rw-r--r--bin/9base/troff/font/devutf/charlib/PC25
-rw-r--r--bin/9base/troff/font/devutf/charlib/RC13
-rw-r--r--bin/9base/troff/font/devutf/charlib/README16
-rw-r--r--bin/9base/troff/font/devutf/charlib/S_l104
-rw-r--r--bin/9base/troff/font/devutf/charlib/_b_x12
-rw-r--r--bin/9base/troff/font/devutf/charlib/_c_i8
-rw-r--r--bin/9base/troff/font/devutf/charlib/_f_f4
-rw-r--r--bin/9base/troff/font/devutf/charlib/_lH166
-rw-r--r--bin/9base/troff/font/devutf/charlib/_l_c36
-rw-r--r--bin/9base/troff/font/devutf/charlib/_l_f36
-rw-r--r--bin/9base/troff/font/devutf/charlib/_l_h166
-rw-r--r--bin/9base/troff/font/devutf/charlib/_o_b8
-rw-r--r--bin/9base/troff/font/devutf/charlib/_p_w140
-rw-r--r--bin/9base/troff/font/devutf/charlib/_rH157
-rw-r--r--bin/9base/troff/font/devutf/charlib/_r_c36
-rw-r--r--bin/9base/troff/font/devutf/charlib/_r_f36
-rw-r--r--bin/9base/troff/font/devutf/charlib/_r_h157
-rw-r--r--bin/9base/troff/font/devutf/charlib/_s_q12
-rw-r--r--bin/9base/troff/font/devutf/charlib/~=4
-rw-r--r--bin/9base/troff/font/devutf/mkMAP5
-rw-r--r--bin/9base/troff/font/devutf/shell.lib1238
-rw-r--r--bin/9base/troff/font/devutf/utfmap47
-rw-r--r--bin/9base/troff/hyphen.tex4466
-rw-r--r--bin/9base/troff/hytab.c126
-rw-r--r--bin/9base/troff/mbwc.c166
-rw-r--r--bin/9base/troff/mbwc.h5
-rw-r--r--bin/9base/troff/n1.c1134
-rw-r--r--bin/9base/troff/n10.c549
-rw-r--r--bin/9base/troff/n2.c325
-rw-r--r--bin/9base/troff/n3.c954
-rw-r--r--bin/9base/troff/n4.c828
-rw-r--r--bin/9base/troff/n5.c1150
-rw-r--r--bin/9base/troff/n6.c363
-rw-r--r--bin/9base/troff/n7.c837
-rw-r--r--bin/9base/troff/n8.c545
-rw-r--r--bin/9base/troff/n9.c489
-rw-r--r--bin/9base/troff/ni.c390
-rw-r--r--bin/9base/troff/stali.mk24
-rw-r--r--bin/9base/troff/suftab.c612
-rw-r--r--bin/9base/troff/t10.c513
-rw-r--r--bin/9base/troff/t11.c260
-rw-r--r--bin/9base/troff/t6.c889
-rw-r--r--bin/9base/troff/tdef.h673
-rw-r--r--bin/9base/troff/term/tab.37154
-rw-r--r--bin/9base/troff/term/tab.450154
-rw-r--r--bin/9base/troff/term/tab.450-12154
-rw-r--r--bin/9base/troff/term/tab.dumb154
-rw-r--r--bin/9base/troff/term/tab.i300154
-rw-r--r--bin/9base/troff/term/tab.lp154
-rw-r--r--bin/9base/troff/term/tab.post154
-rw-r--r--bin/9base/troff/term/tab.think220
-rw-r--r--bin/9base/troff/term/tab.thinkbold220
-rw-r--r--bin/9base/troff/term/tab.thinksmall220
-rw-r--r--bin/9base/troff/term/tab.utf237
-rw-r--r--bin/9base/troff/tmac/complet.11275
-rw-r--r--bin/9base/troff/tmac/cover.11273
-rw-r--r--bin/9base/troff/tmac/me/acm.me55
-rw-r--r--bin/9base/troff/tmac/me/chars.me53
-rw-r--r--bin/9base/troff/tmac/me/deltext.me20
-rw-r--r--bin/9base/troff/tmac/me/eqn.me77
-rw-r--r--bin/9base/troff/tmac/me/float.me63
-rw-r--r--bin/9base/troff/tmac/me/footnote.me83
-rw-r--r--bin/9base/troff/tmac/me/index.me70
-rw-r--r--bin/9base/troff/tmac/me/local.me10
-rw-r--r--bin/9base/troff/tmac/me/null.me3
-rw-r--r--bin/9base/troff/tmac/me/revisions157
-rw-r--r--bin/9base/troff/tmac/me/sh.me108
-rw-r--r--bin/9base/troff/tmac/me/tbl.me109
-rw-r--r--bin/9base/troff/tmac/me/thesis.me19
-rw-r--r--bin/9base/troff/tmac/mmn2829
-rw-r--r--bin/9base/troff/tmac/mmt2792
-rw-r--r--bin/9base/troff/tmac/name.sed4
-rw-r--r--bin/9base/troff/tmac/sendcover5
-rw-r--r--bin/9base/troff/tmac/strings.mm61
-rw-r--r--bin/9base/troff/tmac/tmac.an530
-rw-r--r--bin/9base/troff/tmac/tmac.anhtml18
-rw-r--r--bin/9base/troff/tmac/tmac.antimes511
-rw-r--r--bin/9base/troff/tmac/tmac.bits44
-rw-r--r--bin/9base/troff/tmac/tmac.cs1150
-rw-r--r--bin/9base/troff/tmac/tmac.e997
-rw-r--r--bin/9base/troff/tmac/tmac.html94
-rw-r--r--bin/9base/troff/tmac/tmac.jsdisp63
-rw-r--r--bin/9base/troff/tmac/tmac.m3
-rw-r--r--bin/9base/troff/tmac/tmac.mcs2014
-rw-r--r--bin/9base/troff/tmac/tmac.nihongo1
-rw-r--r--bin/9base/troff/tmac/tmac.org2
-rw-r--r--bin/9base/troff/tmac/tmac.pictures154
-rw-r--r--bin/9base/troff/tmac/tmac.pm966
-rw-r--r--bin/9base/troff/tmac/tmac.psychrefs51
-rw-r--r--bin/9base/troff/tmac/tmac.ptx17
-rw-r--r--bin/9base/troff/tmac/tmac.rscover170
-rw-r--r--bin/9base/troff/tmac/tmac.s1614
-rw-r--r--bin/9base/troff/tmac/tmac.scover330
-rw-r--r--bin/9base/troff/tmac/tmac.sdisp61
-rw-r--r--bin/9base/troff/tmac/tmac.skeep91
-rw-r--r--bin/9base/troff/tmac/tmac.soft1031
-rw-r--r--bin/9base/troff/tmac/tmac.spe142
-rw-r--r--bin/9base/troff/tmac/tmac.srefs156
-rw-r--r--bin/9base/troff/tmac/tmac.twb96
-rw-r--r--bin/9base/troff/tmac/tmac.uni107
-rw-r--r--bin/9base/troff/tmac/tmac.v519
-rw-r--r--bin/9base/troff/troff.1199
-rw-r--r--bin/9base/troff/unansi49
-rw-r--r--bin/9base/unicode/Makefile10
-rw-r--r--bin/9base/unicode/unicode.10
-rw-r--r--bin/9base/unicode/unicode.c122
-rw-r--r--bin/9base/uniq/Makefile10
-rw-r--r--bin/9base/uniq/uniq.159
-rw-r--r--bin/9base/uniq/uniq.c169
-rw-r--r--bin/9base/unutf/Makefile10
-rw-r--r--bin/9base/unutf/unutf.10
-rw-r--r--bin/9base/unutf/unutf.c20
-rw-r--r--bin/9base/urlencode/Makefile10
-rw-r--r--bin/9base/urlencode/urlencode.120
-rw-r--r--bin/9base/urlencode/urlencode.c103
-rw-r--r--bin/9base/wc/Makefile10
-rw-r--r--bin/9base/wc/wc.153
-rw-r--r--bin/9base/wc/wc.c352
-rw-r--r--bin/9base/yacc.mk40
-rwxr-xr-xbin/9base/yacc/9yacc4
-rw-r--r--bin/9base/yacc/Makefile15
-rw-r--r--bin/9base/yacc/yacc.1176
-rw-r--r--bin/9base/yacc/yacc.c2980
-rw-r--r--bin/9base/yacc/yaccpar276
-rw-r--r--bin/9base/yacc/yaccpars273
-rw-r--r--bin/stali.mk35
-rw-r--r--config.mk3
757 files changed, 182023 insertions, 16 deletions
diff --git a/bin/9base/.hgtags b/bin/9base/.hgtags
new file mode 100644
index 00000000..d266c8f9
--- /dev/null
+++ b/bin/9base/.hgtags
@@ -0,0 +1,6 @@
+49087403b7b7fc7e64f52e71fc576b92bb8aa158 1
+538338114742f2e81f6c52a5a323bdf7ca0d5d86 2
+7c1decda5b50405d3b62daa8369aa24c82e7b615 3
+25d1757fba6bcef82866bad87dcff6f2333673cc 4
+5f3d19e583ff4504cbc6247b711e728bd602d6f2 5
+31578905a0591473f94a6d20d149d6f9f5402194 6
diff --git a/bin/9base/.origin b/bin/9base/.origin
new file mode 100644
index 00000000..c6617570
--- /dev/null
+++ b/bin/9base/.origin
@@ -0,0 +1 @@
+git://suckless.org/9base#de27e3fba58c3dd467c29ea9a1aea6f9e65a5299
diff --git a/bin/9base/LICENSE b/bin/9base/LICENSE
new file mode 100644
index 00000000..f806f91c
--- /dev/null
+++ b/bin/9base/LICENSE
@@ -0,0 +1,287 @@
+The rare bits touched by Anselm R. Garbe are under following LICENSE:
+
+MIT/X Consortium License
+
+© 2005-2012 Anselm R Garbe <anselm@garbe.us>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+===================================================================
+
+The Plan 9 software is provided under the terms of the
+Lucent Public License, Version 1.02, reproduced below,
+with the following notable exceptions:
+
+1. No right is granted to create derivative works of or
+ to redistribute (other than with the Plan 9 Operating System)
+ the screen imprinter fonts identified in subdirectory
+ /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida
+ Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans
+ Typewriter83), identified in subdirectory /sys/lib/postscript/font.
+ These directories contain material copyrights by B&H Inc. and Y&Y Inc.
+
+2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font
+ are subject to the GNU GPL, reproduced in the file /LICENSE.gpl.
+
+3. The ghostscript program in the subdirectory /sys/src/cmd/gs is
+ covered by the Aladdin Free Public License, reproduced in the file
+ /LICENSE.afpl.
+
+Other, less notable exceptions are marked in the file tree with
+COPYING, COPYRIGHT, or LICENSE files.
+
+===================================================================
+
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+ Program, and
+ b. in the case of each Contributor,
+
+ i. changes to the Program, and
+ ii. additions to the Program;
+
+ where such changes and/or additions to the Program were added to the
+ Program by such Contributor itself or anyone acting on such
+ Contributor's behalf, and the Contributor explicitly consents, in
+ accordance with Section 3C, to characterization of the changes and/or
+ additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+ b. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor, if
+ any, in source code and object code form. The patent license granted
+ by a Contributor shall also apply to the combination of the
+ Contribution of that Contributor and the Program if, at the time the
+ Contribution is added by the Contributor, such addition of the
+ Contribution causes such combination to be covered by the Licensed
+ Patents. The patent license granted by a Contributor shall not apply
+ to (i) any other combinations which include the Contribution, nor to
+ (ii) Contributions of other Contributors. No hardware per se is
+ licensed hereunder.
+
+ c. Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity. Each
+ Contributor disclaims any liability to Recipient for claims brought by
+ any other entity based on infringement of intellectual property rights
+ or otherwise. As a condition to exercising the rights and licenses
+ granted hereunder, each Recipient hereby assumes sole responsibility
+ to secure any other intellectual property rights needed, if any. For
+ example, if a third party patent license is required to allow
+ Recipient to distribute the Program, it is Recipient's responsibility
+ to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright
+ license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+ form, a copy of this Agreement or Distributor's own license agreement
+ is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+ license agreement:
+
+ i. effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+ ii. effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits; and
+ iii. states that any provisions which differ from this Agreement are
+ offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+ location in the Program:
+
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+ Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
diff --git a/bin/9base/Makefile b/bin/9base/Makefile
new file mode 100644
index 00000000..65a8a407
--- /dev/null
+++ b/bin/9base/Makefile
@@ -0,0 +1,90 @@
+# 9base - Plan 9 userland for Unix
+
+include config.mk
+
+SUBDIRS = lib9\
+ yacc\
+ ascii\
+ awk\
+ basename\
+ bc\
+ cal\
+ cat\
+ cleanname\
+ cmp\
+ date\
+ dc\
+ du\
+ dd\
+ diff\
+ echo\
+ ed\
+ factor\
+ fortune\
+ fmt\
+ freq\
+ getflags\
+ grep\
+ hoc\
+ join\
+ listen1\
+ look\
+ ls\
+ md5sum\
+ mk\
+ mkdir\
+ mtime\
+ pbd\
+ primes\
+ rc\
+ read\
+ rm\
+ sam\
+ sha1sum\
+ sed\
+ seq\
+ sleep\
+ sort\
+ split\
+ ssam\
+ strings\
+ tail\
+ tee\
+ test\
+ touch\
+ tr\
+ troff\
+ unicode\
+ uniq\
+ unutf\
+ urlencode\
+ wc
+
+all:
+ @echo 9base build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+ @chmod 755 yacc/9yacc
+ @for i in ${SUBDIRS}; do cd $$i; ${MAKE} || exit; cd ..; done;
+
+clean:
+ @for i in ${SUBDIRS}; do cd $$i; ${MAKE} clean || exit; cd ..; done
+ @rm -f 9base-${VERSION}.tar.gz
+ @echo cleaned 9base
+
+install: all
+ @for i in ${SUBDIRS}; do cd $$i; ${MAKE} install || exit; cd ..; done
+ @echo installed 9base to ${DESTDIR}${PREFIX}
+
+uninstall:
+ @for i in ${SUBDIRS}; do cd $$i; ${MAKE} uninstall || exit; cd ..; done
+ @echo uninstalled 9base
+
+dist: clean
+ @mkdir -p 9base-${VERSION}
+ @cp -R Makefile README LICENSE std.mk yacc.mk config.mk ${SUBDIRS} 9base-${VERSION}
+ @tar -cf 9base-${VERSION}.tar 9base-${VERSION}
+ @gzip 9base-${VERSION}.tar
+ @rm -rf 9base-${VERSION}
+ @echo created distribution 9base-${VERSION}.tar.gz
diff --git a/bin/9base/README b/bin/9base/README
new file mode 100644
index 00000000..f1153d12
--- /dev/null
+++ b/bin/9base/README
@@ -0,0 +1,24 @@
+Abstract
+--------
+This is a port of various original Plan 9 tools for Unix, based on
+plan9port[1]. See the LICENSE file for license details.
+
+
+Installation
+------------
+Edit config.mk to match your local setup and execute 'make install'
+(if necessary as root). By default, 9base is installed into its own
+hierarchy, /usr/local/plan9. This is done to avoid conflicts with the
+standard tools of your system.
+
+
+Credits
+-------
+Many thanks go to Lucent, the Bell Labs which developed this fine
+stuff and to Russ Cox for his plan9port.
+
+References
+----------
+[1] http://swtch.com/plan9port/
+
+--Anselm R Garbe
diff --git a/bin/9base/TODO b/bin/9base/TODO
new file mode 100644
index 00000000..907a6dfe
--- /dev/null
+++ b/bin/9base/TODO
@@ -0,0 +1,5 @@
+Missing commands:
+
+* file (from 9front)
+* cp (also missing from p9p too)
+* mv (also missing from p9p too)
diff --git a/bin/9base/ascii/Makefile b/bin/9base/ascii/Makefile
new file mode 100644
index 00000000..d43d33c8
--- /dev/null
+++ b/bin/9base/ascii/Makefile
@@ -0,0 +1,10 @@
+# ascii - ascii unix port from plan9
+# Depends on ../lib9
+
+TARG = ascii
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/ascii/ascii.1 b/bin/9base/ascii/ascii.1
new file mode 100644
index 00000000..0857805e
--- /dev/null
+++ b/bin/9base/ascii/ascii.1
@@ -0,0 +1,160 @@
+.TH ASCII 1
+.SH NAME
+ascii, unicode \- interpret ASCII, Unicode characters
+.SH SYNOPSIS
+.B ascii
+[
+.B -8
+]
+[
+.BI -oxdb n
+]
+[
+.B -nct
+]
+[
+.I text
+]
+.PP
+.B unicode
+[
+.B -nt
+]
+.IB hexmin - hexmax
+.PP
+.B unicode
+[
+.B -t
+]
+.I hex
+[
+\&...
+]
+.PP
+.B unicode
+[
+.B -n
+]
+.I characters
+.PP
+.B look
+.I hex
+.B \*9/lib/unicode
+.SH DESCRIPTION
+.I Ascii
+prints the
+.SM ASCII
+values corresponding to characters and
+.I vice
+.IR versa ;
+under the
+.B -8
+option, the
+.SM ISO
+Latin-1 extensions (codes 0200-0377) are included.
+The values are interpreted in a settable numeric base;
+.B -o
+specifies octal,
+.B -d
+decimal,
+.B -x
+hexadecimal (the default), and
+.BI -b n
+base
+.IR n .
+.PP
+With no arguments,
+.I ascii
+prints a table of the character set in the specified base.
+Characters of
+.I text
+are converted to their
+.SM ASCII
+values, one per line. If, however, the first
+.I text
+argument is a valid number in the specified base, conversion
+goes the opposite way.
+Control characters are printed as two- or three-character mnemonics.
+Other options are:
+.TP
+.B -n
+Force numeric output.
+.TP
+.B -c
+Force character output.
+.TP
+.B -t
+Convert from numbers to running text; do not interpret
+control characters or insert newlines.
+.PP
+.I Unicode
+is similar; it converts between
+.SM UTF
+and character values from the Unicode Standard (see
+.IR utf (7)).
+If given a range of hexadecimal numbers,
+.I unicode
+prints a table of the specified Unicode characters \(em their values and
+.SM UTF
+representations.
+Otherwise it translates from
+.SM UTF
+to numeric value or vice versa,
+depending on the appearance of the supplied text;
+the
+.B -n
+option forces numeric output to avoid ambiguity with numeric characters.
+If converting to
+.SM UTF ,
+the characters are printed one per line unless the
+.B -t
+flag is set, in which case the output is a single string
+containing only the specified characters.
+Unlike
+.IR ascii ,
+.I unicode
+treats no characters specially.
+.PP
+The output of
+.I ascii
+and
+.I unicode
+may be unhelpful if the characters printed are not available in the current font.
+.PP
+The file
+.B \*9/lib/unicode
+contains a
+table of characters and descriptions, sorted in hexadecimal order,
+suitable for
+.IR look (1)
+on the lower case
+.I hex
+values of characters.
+.SH EXAMPLES
+.TP
+.B "ascii -d"
+Print the
+.SM ASCII
+table base 10.
+.TP
+.B "unicode p"
+Print the hex value of `p'.
+.TP
+.B "unicode 2200-22f1"
+Print a table of miscellaneous mathematical symbols.
+.TP
+.B "look 039 \*9/lib/unicode"
+See the start of the Greek alphabet's encoding in the Unicode Standard.
+.SH FILES
+.TP
+.B \*9/lib/unicode
+table of characters and descriptions.
+.SH SOURCE
+.B \*9/src/cmd/ascii.c
+.br
+.B \*9/src/cmd/unicode.c
+.SH "SEE ALSO"
+.IR look (1),
+.IR tcs (1),
+.IR utf (7),
+.IR font (7)
diff --git a/bin/9base/ascii/ascii.c b/bin/9base/ascii/ascii.c
new file mode 100644
index 00000000..ee2cb4ca
--- /dev/null
+++ b/bin/9base/ascii/ascii.c
@@ -0,0 +1,181 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define MAXBASE 36
+
+void usage(void);
+void put(int);
+void putn(int, int);
+void puttext(char *);
+void putnum(char *);
+int btoi(char *);
+int value(int, int);
+int isnum(char *);
+
+char *str[256]={
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs ", "ht ", "nl ", "vt ", "np ", "cr ", "so ", "si ",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "us ",
+ "sp ", " ! ", " \" ", " # ", " $ ", " % ", " & ", " ' ",
+ " ( ", " ) ", " * ", " + ", " , ", " - ", " . ", " / ",
+ " 0 ", " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ",
+ " 8 ", " 9 ", " : ", " ; ", " < ", " = ", " > ", " ? ",
+ " @ ", " A ", " B ", " C ", " D ", " E ", " F ", " G ",
+ " H ", " I ", " J ", " K ", " L ", " M ", " N ", " O ",
+ " P ", " Q ", " R ", " S ", " T ", " U ", " V ", " W ",
+ " X ", " Y ", " Z ", " [ ", " \\ ", " ] ", " ^ ", " _ ",
+ " ` ", " a ", " b ", " c ", " d ", " e ", " f ", " g ",
+ " h ", " i ", " j ", " k ", " l ", " m ", " n ", " o ",
+ " p ", " q ", " r ", " s ", " t ", " u ", " v ", " w ",
+ " x ", " y ", " z ", " { ", " | ", " } ", " ~ ", "del",
+ "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87",
+ "x88", "x89", "x8a", "x8b", "x8c", "x8d", "x8e", "x8f",
+ "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97",
+ "x98", "x99", "x9a", "x9b", "x9c", "x9d", "x9e", "x9f",
+ "xa0", " ¡ ", " ¢ ", " £ ", " ¤ ", " ¥ ", " ¦ ", " § ",
+ " ¨ ", " © ", " ª ", " « ", " ¬ ", " ­ ", " ® ", " ¯ ",
+ " ° ", " ± ", " ² ", " ³ ", " ´ ", " µ ", " ¶ ", " · ",
+ " ¸ ", " ¹ ", " º ", " » ", " ¼ ", " ½ ", " ¾ ", " ¿ ",
+ " À ", " Á ", " Â ", " Ã ", " Ä ", " Å ", " Æ ", " Ç ",
+ " È ", " É ", " Ê ", " Ë ", " Ì ", " Í ", " Î ", " Ï ",
+ " Ð ", " Ñ ", " Ò ", " Ó ", " Ô ", " Õ ", " Ö ", " × ",
+ " Ø ", " Ù ", " Ú ", " Û ", " Ü ", " Ý ", " Þ ", " ß ",
+ " à ", " á ", " â ", " ã ", " ä ", " å ", " æ ", " ç ",
+ " è ", " é ", " ê ", " ë ", " ì ", " í ", " î ", " ï ",
+ " ð ", " ñ ", " ò ", " ó ", " ô ", " õ ", " ö ", " ÷ ",
+ " ø ", " ù ", " ú ", " û ", " ü ", " ý ", " þ ", " ÿ "
+};
+
+char Ncol[]={
+ 0,0,7,5,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+};
+
+int nchars=128;
+int base=16;
+int ncol;
+int text=1;
+int strip=0;
+Biobuf bin;
+
+void
+main(int argc, char **argv)
+{
+ int i;
+
+ Binit(&bin, 1, OWRITE);
+ ARGBEGIN{
+ case '8':
+ nchars=256; break;
+ case 'x':
+ base=16; break;
+ case 'o':
+ base=8; break;
+ case 'd':
+ base=10; break;
+ case 'b':
+ base=strtoul(EARGF(usage()), 0, 0);
+ if(base<2||base>MAXBASE)
+ usage();
+ break;
+ case 'n':
+ text=0; break;
+ case 't':
+ strip=1;
+ /* fall through */
+ case 'c':
+ text=2; break;
+ default:
+ usage();
+ }ARGEND
+
+ ncol=Ncol[base];
+ if(argc==0){
+ for(i=0;i<nchars;i++){
+ put(i);
+ if((i&7)==7)
+ Bprint(&bin, "|\n");
+ }
+ }else{
+ if(text==1)
+ text=isnum(argv[0]);
+ while(argc--)
+ if(text)
+ puttext(*argv++);
+ else
+ putnum(*argv++);
+ }
+ Bputc(&bin, '\n');
+ exits(0);
+}
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-8] [-xod | -b8] [-ncst] [--] [text]\n", argv0);
+ exits("usage");
+}
+void
+put(int i)
+{
+ Bputc(&bin, '|');
+ putn(i, ncol);
+ Bprint(&bin, " %s", str[i]);
+}
+char dig[]="0123456789abcdefghijklmnopqrstuvwxyz";
+void
+putn(int n, int ndig)
+{
+ if(ndig==0)
+ return;
+ putn(n/base, ndig-1);
+ Bputc(&bin, dig[n%base]);
+}
+void
+puttext(char *s)
+{
+ int n;
+ n=btoi(s)&0377;
+ if(strip)
+ Bputc(&bin, n);
+ else
+ Bprint(&bin, "%s\n", str[n]);
+}
+void
+putnum(char *s)
+{
+ while(*s){
+ putn(*s++&0377, ncol);
+ Bputc(&bin, '\n');
+ }
+}
+int
+btoi(char *s)
+{
+ int n;
+ n=0;
+ while(*s)
+ n=n*base+value(*s++, 0);
+ return(n);
+}
+int
+value(int c, int f)
+{
+ char *s;
+ for(s=dig; s<dig+base; s++)
+ if(*s==c)
+ return(s-dig);
+ if(f)
+ return(-1);
+ fprint(2, "%s: bad input char %c\n", argv0, c);
+ exits("bad");
+ return 0; /* to keep ken happy */
+}
+int
+isnum(char *s)
+{
+ while(*s)
+ if(value(*s++, 1)==-1)
+ return(0);
+ return(1);
+}
diff --git a/bin/9base/awk/Makefile b/bin/9base/awk/Makefile
new file mode 100644
index 00000000..d0e86b3c
--- /dev/null
+++ b/bin/9base/awk/Makefile
@@ -0,0 +1,13 @@
+# awk - awk unix port from plan9
+# Depends on ../lib9
+
+TARG = awk
+OFILES = re.o lex.o main.o parse.o proctab.o tran.o lib.o run.o y.tab.o
+YFILES = awkgram.y
+MANFILES = awk.1
+
+include ../yacc.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/awk/README b/bin/9base/awk/README
new file mode 100644
index 00000000..bcf46126
--- /dev/null
+++ b/bin/9base/awk/README
@@ -0,0 +1,13 @@
+This 'awk' source is directly downloaded from the Plan 9 source
+
+http://cm.bell-labs.com/sources/plan9/sys/src/cmd/awk/
+
+as such, it's copyright is held by Lucent Technologies and distributed under the
+Lucent Public License version 1.02 [http://www.opensource.org/licenses/lucent1.02.php].
+
+Modifications were made by Jeff Sickel in order to build using Plan 9 from User
+Space [http://swtch.com/plan9port/] to the following files:
+
+ mkfile
+ re.c
+
diff --git a/bin/9base/awk/awk.1 b/bin/9base/awk/awk.1
new file mode 100644
index 00000000..a0ea5e41
--- /dev/null
+++ b/bin/9base/awk/awk.1
@@ -0,0 +1,527 @@
+.TH AWK 1
+.SH NAME
+awk \- pattern-directed scanning and processing language
+.SH SYNOPSIS
+.B awk
+[
+.BI -F fs
+]
+[
+.BI -v
+.I var=value
+]
+[
+.BI -mr n
+]
+[
+.BI -mf n
+]
+[
+.B -f
+.I prog
+[
+.I prog
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Awk
+scans each input
+.I file
+for lines that match any of a set of patterns specified literally in
+.IR prog
+or in one or more files
+specified as
+.B -f
+.IR file .
+With each pattern
+there can be an associated action that will be performed
+when a line of a
+.I file
+matches the pattern.
+Each line is matched against the
+pattern portion of every pattern-action statement;
+the associated action is performed for each matched pattern.
+The file name
+.L -
+means the standard input.
+Any
+.IR file
+of the form
+.I var=value
+is treated as an assignment, not a file name,
+and is executed at the time it would have been opened if it were a file name.
+The option
+.B -v
+followed by
+.I var=value
+is an assignment to be done before
+.I prog
+is executed;
+any number of
+.B -v
+options may be present.
+.B \-F
+.IR fs
+option defines the input field separator to be the regular expression
+.IR fs .
+.PP
+An input line is normally made up of fields separated by white space,
+or by regular expression
+.BR FS .
+The fields are denoted
+.BR $1 ,
+.BR $2 ,
+\&..., while
+.B $0
+refers to the entire line.
+If
+.BR FS
+is null, the input line is split into one field per character.
+.PP
+To compensate for inadequate implementation of storage management,
+the
+.B \-mr
+option can be used to set the maximum size of the input record,
+and the
+.B \-mf
+option to set the maximum number of fields.
+.PP
+A pattern-action statement has the form
+.IP
+.IB pattern " { " action " }
+.PP
+A missing
+.BI { " action " }
+means print the line;
+a missing pattern always matches.
+Pattern-action statements are separated by newlines or semicolons.
+.PP
+An action is a sequence of statements.
+A statement can be one of the following:
+.PP
+.EX
+.ta \w'\fLdelete array[expression]'u
+if(\fI expression \fP)\fI statement \fP\fR[ \fPelse\fI statement \fP\fR]\fP
+while(\fI expression \fP)\fI statement\fP
+for(\fI expression \fP;\fI expression \fP;\fI expression \fP)\fI statement\fP
+for(\fI var \fPin\fI array \fP)\fI statement\fP
+do\fI statement \fPwhile(\fI expression \fP)
+break
+continue
+{\fR [\fP\fI statement ... \fP\fR] \fP}
+\fIexpression\fP #\fR commonly\fP\fI var = expression\fP
+print\fR [ \fP\fIexpression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
+printf\fI format \fP\fR[ \fP,\fI expression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
+return\fR [ \fP\fIexpression \fP\fR]\fP
+next #\fR skip remaining patterns on this input line\fP
+nextfile #\fR skip rest of this file, open next, start at top\fP
+delete\fI array\fP[\fI expression \fP] #\fR delete an array element\fP
+delete\fI array\fP #\fR delete all elements of array\fP
+exit\fR [ \fP\fIexpression \fP\fR]\fP #\fR exit immediately; status is \fP\fIexpression\fP
+.EE
+.DT
+.PP
+Statements are terminated by
+semicolons, newlines or right braces.
+An empty
+.I expression-list
+stands for
+.BR $0 .
+String constants are quoted \&\fL"\ "\fR,
+with the usual C escapes recognized within.
+Expressions take on string or numeric values as appropriate,
+and are built using the operators
+.B + \- * / % ^
+(exponentiation), and concatenation (indicated by white space).
+The operators
+.B
+! ++ \-\- += \-= *= /= %= ^= > >= < <= == != ?:
+are also available in expressions.
+Variables may be scalars, array elements
+(denoted
+.IB x [ i ] )
+or fields.
+Variables are initialized to the null string.
+Array subscripts may be any string,
+not necessarily numeric;
+this allows for a form of associative memory.
+Multiple subscripts such as
+.B [i,j,k]
+are permitted; the constituents are concatenated,
+separated by the value of
+.BR SUBSEP .
+.PP
+The
+.B print
+statement prints its arguments on the standard output
+(or on a file if
+.BI > file
+or
+.BI >> file
+is present or on a pipe if
+.BI | cmd
+is present), separated by the current output field separator,
+and terminated by the output record separator.
+.I file
+and
+.I cmd
+may be literal names or parenthesized expressions;
+identical string values in different statements denote
+the same open file.
+The
+.B printf
+statement formats its expression list according to the format
+(see
+.IR fprintf (2)) .
+The built-in function
+.BI close( expr )
+closes the file or pipe
+.IR expr .
+The built-in function
+.BI fflush( expr )
+flushes any buffered output for the file or pipe
+.IR expr .
+.PP
+The mathematical functions
+.BR exp ,
+.BR log ,
+.BR sqrt ,
+.BR sin ,
+.BR cos ,
+and
+.BR atan2
+are built in.
+Other built-in functions:
+.TF length
+.TP
+.B length
+the length of its argument
+taken as a string,
+or of
+.B $0
+if no argument.
+.TP
+.B rand
+random number on (0,1)
+.TP
+.B srand
+sets seed for
+.B rand
+and returns the previous seed.
+.TP
+.B int
+truncates to an integer value
+.TP
+.B utf
+converts its numerical argument, a character number, to a
+.SM UTF
+string
+.TP
+.BI substr( s , " m" , " n\fL)
+the
+.IR n -character
+substring of
+.I s
+that begins at position
+.IR m
+counted from 1.
+.TP
+.BI index( s , " t" )
+the position in
+.I s
+where the string
+.I t
+occurs, or 0 if it does not.
+.TP
+.BI match( s , " r" )
+the position in
+.I s
+where the regular expression
+.I r
+occurs, or 0 if it does not.
+The variables
+.B RSTART
+and
+.B RLENGTH
+are set to the position and length of the matched string.
+.TP
+.BI split( s , " a" , " fs\fL)
+splits the string
+.I s
+into array elements
+.IB a [1]\f1,
+.IB a [2]\f1,
+\&...,
+.IB a [ n ]\f1,
+and returns
+.IR n .
+The separation is done with the regular expression
+.I fs
+or with the field separator
+.B FS
+if
+.I fs
+is not given.
+An empty string as field separator splits the string
+into one array element per character.
+.TP
+.BI sub( r , " t" , " s\fL)
+substitutes
+.I t
+for the first occurrence of the regular expression
+.I r
+in the string
+.IR s .
+If
+.I s
+is not given,
+.B $0
+is used.
+.TP
+.B gsub
+same as
+.B sub
+except that all occurrences of the regular expression
+are replaced;
+.B sub
+and
+.B gsub
+return the number of replacements.
+.TP
+.BI sprintf( fmt , " expr" , " ...\fL)
+the string resulting from formatting
+.I expr ...
+according to the
+.I printf
+format
+.I fmt
+.TP
+.BI system( cmd )
+executes
+.I cmd
+and returns its exit status
+.TP
+.BI tolower( str )
+returns a copy of
+.I str
+with all upper-case characters translated to their
+corresponding lower-case equivalents.
+.TP
+.BI toupper( str )
+returns a copy of
+.I str
+with all lower-case characters translated to their
+corresponding upper-case equivalents.
+.PD
+.PP
+The ``function''
+.B getline
+sets
+.B $0
+to the next input record from the current input file;
+.B getline
+.BI < file
+sets
+.B $0
+to the next record from
+.IR file .
+.B getline
+.I x
+sets variable
+.I x
+instead.
+Finally,
+.IB cmd " | getline
+pipes the output of
+.I cmd
+into
+.BR getline ;
+each call of
+.B getline
+returns the next line of output from
+.IR cmd .
+In all cases,
+.B getline
+returns 1 for a successful input,
+0 for end of file, and \-1 for an error.
+.PP
+Patterns are arbitrary Boolean combinations
+(with
+.BR "! || &&" )
+of regular expressions and
+relational expressions.
+Regular expressions are as in
+.IR regexp (6).
+Isolated regular expressions
+in a pattern apply to the entire line.
+Regular expressions may also occur in
+relational expressions, using the operators
+.BR ~
+and
+.BR !~ .
+.BI / re /
+is a constant regular expression;
+any string (constant or variable) may be used
+as a regular expression, except in the position of an isolated regular expression
+in a pattern.
+.PP
+A pattern may consist of two patterns separated by a comma;
+in this case, the action is performed for all lines
+from an occurrence of the first pattern
+though an occurrence of the second.
+.PP
+A relational expression is one of the following:
+.IP
+.I expression matchop regular-expression
+.br
+.I expression relop expression
+.br
+.IB expression " in " array-name
+.br
+.BI ( expr , expr,... ") in " array-name
+.PP
+where a
+.I relop
+is any of the six relational operators in C,
+and a
+.I matchop
+is either
+.B ~
+(matches)
+or
+.B !~
+(does not match).
+A conditional is an arithmetic expression,
+a relational expression,
+or a Boolean combination
+of these.
+.PP
+The special patterns
+.B BEGIN
+and
+.B END
+may be used to capture control before the first input line is read
+and after the last.
+.B BEGIN
+and
+.B END
+do not combine with other patterns.
+.PP
+Variable names with special meanings:
+.TF FILENAME
+.TP
+.B CONVFMT
+conversion format used when converting numbers
+(default
+.BR "%.6g" )
+.TP
+.B FS
+regular expression used to separate fields; also settable
+by option
+.BI \-F fs\f1.
+.TP
+.BR NF
+number of fields in the current record
+.TP
+.B NR
+ordinal number of the current record
+.TP
+.B FNR
+ordinal number of the current record in the current file
+.TP
+.B FILENAME
+the name of the current input file
+.TP
+.B RS
+input record separator (default newline)
+.TP
+.B OFS
+output field separator (default blank)
+.TP
+.B ORS
+output record separator (default newline)
+.TP
+.B OFMT
+output format for numbers (default
+.BR "%.6g" )
+.TP
+.B SUBSEP
+separates multiple subscripts (default 034)
+.TP
+.B ARGC
+argument count, assignable
+.TP
+.B ARGV
+argument array, assignable;
+non-null members are taken as file names
+.TP
+.B ENVIRON
+array of environment variables; subscripts are names.
+.PD
+.PP
+Functions may be defined (at the position of a pattern-action statement) thus:
+.IP
+.L
+function foo(a, b, c) { ...; return x }
+.PP
+Parameters are passed by value if scalar and by reference if array name;
+functions may be called recursively.
+Parameters are local to the function; all other variables are global.
+Thus local variables may be created by providing excess parameters in
+the function definition.
+.SH EXAMPLES
+.TP
+.L
+length($0) > 72
+Print lines longer than 72 characters.
+.TP
+.L
+{ print $2, $1 }
+Print first two fields in opposite order.
+.PP
+.EX
+BEGIN { FS = ",[ \et]*|[ \et]+" }
+ { print $2, $1 }
+.EE
+.ns
+.IP
+Same, with input fields separated by comma and/or blanks and tabs.
+.PP
+.EX
+ { s += $1 }
+END { print "sum is", s, " average is", s/NR }
+.EE
+.ns
+.IP
+Add up first column, print sum and average.
+.TP
+.L
+/start/, /stop/
+Print all lines between start/stop pairs.
+.PP
+.EX
+BEGIN { # Simulate echo(1)
+ for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i]
+ printf "\en"
+ exit }
+.EE
+.SH SOURCE
+.B /sys/src/cmd/awk
+.SH SEE ALSO
+.IR sed (1),
+.IR regexp (6),
+.br
+A. V. Aho, B. W. Kernighan, P. J. Weinberger,
+.I
+The AWK Programming Language,
+Addison-Wesley, 1988. ISBN 0-201-07981-X
+.SH BUGS
+There are no explicit conversions between numbers and strings.
+To force an expression to be treated as a number add 0 to it;
+to force it to be treated as a string concatenate
+\&\fL""\fP to it.
+.br
+The scope rules for variables in functions are a botch;
+the syntax is worse.
diff --git a/bin/9base/awk/awk.h b/bin/9base/awk/awk.h
new file mode 100644
index 00000000..1853381d
--- /dev/null
+++ b/bin/9base/awk/awk.h
@@ -0,0 +1,185 @@
+/*
+Copyright (c) Lucent Technologies 1997
+ All Rights Reserved
+
+*/
+
+typedef double Awkfloat;
+
+/* unsigned char is more trouble than it's worth */
+
+typedef unsigned char uschar;
+
+#define xfree(a) { if ((a) != NULL) { free((char *) a); a = NULL; } }
+
+#define DEBUG
+#ifdef DEBUG
+ /* uses have to be doubly parenthesized */
+# define dprintf(x) if (dbg) printf x
+#else
+# define dprintf(x)
+#endif
+
+extern char errbuf[];
+
+extern int compile_time; /* 1 if compiling, 0 if running */
+extern int safe; /* 0 => unsafe, 1 => safe */
+
+#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */
+extern int recsize; /* size of current record, orig RECSIZE */
+
+extern char **FS;
+extern char **RS;
+extern char **ORS;
+extern char **OFS;
+extern char **OFMT;
+extern Awkfloat *NR;
+extern Awkfloat *FNR;
+extern Awkfloat *NF;
+extern char **FILENAME;
+extern char **SUBSEP;
+extern Awkfloat *RSTART;
+extern Awkfloat *RLENGTH;
+
+extern char *record; /* points to $0 */
+extern int lineno; /* line number in awk program */
+extern int errorflag; /* 1 if error has occurred */
+extern int donefld; /* 1 if record broken into fields */
+extern int donerec; /* 1 if record is valid (no fld has changed */
+extern char inputFS[]; /* FS at time of input, for field splitting */
+
+extern int dbg;
+
+extern char *patbeg; /* beginning of pattern matched */
+extern int patlen; /* length of pattern matched. set in b.c */
+
+/* Cell: all information about a variable or constant */
+
+typedef struct Cell {
+ uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */
+ uschar csub; /* CCON, CTEMP, CFLD, etc. */
+ char *nval; /* name, for variables only */
+ char *sval; /* string value */
+ Awkfloat fval; /* value as number */
+ int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
+ struct Cell *cnext; /* ptr to next if chained */
+} Cell;
+
+typedef struct Array { /* symbol table array */
+ int nelem; /* elements in table right now */
+ int size; /* size of tab */
+ Cell **tab; /* hash table pointers */
+} Array;
+
+#define NSYMTAB 50 /* initial size of a symbol table */
+extern Array *symtab;
+
+extern Cell *nrloc; /* NR */
+extern Cell *fnrloc; /* FNR */
+extern Cell *nfloc; /* NF */
+extern Cell *rstartloc; /* RSTART */
+extern Cell *rlengthloc; /* RLENGTH */
+
+/* Cell.tval values: */
+#define NUM 01 /* number value is valid */
+#define STR 02 /* string value is valid */
+#define DONTFREE 04 /* string space is not freeable */
+#define CON 010 /* this is a constant */
+#define ARR 020 /* this is an array */
+#define FCN 040 /* this is a function name */
+#define FLD 0100 /* this is a field $1, $2, ... */
+#define REC 0200 /* this is $0 */
+
+
+/* function types */
+#define FLENGTH 1
+#define FSQRT 2
+#define FEXP 3
+#define FLOG 4
+#define FINT 5
+#define FSYSTEM 6
+#define FRAND 7
+#define FSRAND 8
+#define FSIN 9
+#define FCOS 10
+#define FATAN 11
+#define FTOUPPER 12
+#define FTOLOWER 13
+#define FFLUSH 14
+#define FUTF 15
+
+/* Node: parse tree is made of nodes, with Cell's at bottom */
+
+typedef struct Node {
+ int ntype;
+ struct Node *nnext;
+ int lineno;
+ int nobj;
+ struct Node *narg[1]; /* variable: actual size set by calling malloc */
+} Node;
+
+#define NIL ((Node *) 0)
+
+extern Node *winner;
+extern Node *nullstat;
+extern Node *nullnode;
+
+/* ctypes */
+#define OCELL 1
+#define OBOOL 2
+#define OJUMP 3
+
+/* Cell subtypes: csub */
+#define CFREE 7
+#define CCOPY 6
+#define CCON 5
+#define CTEMP 4
+#define CNAME 3
+#define CVAR 2
+#define CFLD 1
+#define CUNK 0
+
+/* bool subtypes */
+#define BTRUE 11
+#define BFALSE 12
+
+/* jump subtypes */
+#define JEXIT 21
+#define JNEXT 22
+#define JBREAK 23
+#define JCONT 24
+#define JRET 25
+#define JNEXTFILE 26
+
+/* node types */
+#define NVALUE 1
+#define NSTAT 2
+#define NEXPR 3
+
+
+extern int pairstack[], paircnt;
+
+#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
+#define isvalue(n) ((n)->ntype == NVALUE)
+#define isexpr(n) ((n)->ntype == NEXPR)
+#define isjump(n) ((n)->ctype == OJUMP)
+#define isexit(n) ((n)->csub == JEXIT)
+#define isbreak(n) ((n)->csub == JBREAK)
+#define iscont(n) ((n)->csub == JCONT)
+#define isnext(n) ((n)->csub == JNEXT)
+#define isnextfile(n) ((n)->csub == JNEXTFILE)
+#define isret(n) ((n)->csub == JRET)
+#define isrec(n) ((n)->tval & REC)
+#define isfld(n) ((n)->tval & FLD)
+#define isstr(n) ((n)->tval & STR)
+#define isnum(n) ((n)->tval & NUM)
+#define isarr(n) ((n)->tval & ARR)
+#define isfcn(n) ((n)->tval & FCN)
+#define istrue(n) ((n)->csub == BTRUE)
+#define istemp(n) ((n)->csub == CTEMP)
+#define isargument(n) ((n)->nobj == ARG)
+/* #define freeable(p) (!((p)->tval & DONTFREE)) */
+#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR )
+
+#include "proto.h"
+
diff --git a/bin/9base/awk/awkgram.y b/bin/9base/awk/awkgram.y
new file mode 100644
index 00000000..31e188c3
--- /dev/null
+++ b/bin/9base/awk/awkgram.y
@@ -0,0 +1,489 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+%{
+#include <stdio.h>
+#include <string.h>
+#include "awk.h"
+
+#define makedfa(a,b) compre(a)
+
+void checkdup(Node *list, Cell *item);
+int yywrap(void) { return(1); }
+
+Node *beginloc = 0;
+Node *endloc = 0;
+int infunc = 0; /* = 1 if in arglist or body of func */
+int inloop = 0; /* = 1 if in while, for, do */
+char *curfname = 0; /* current function name */
+Node *arglist = 0; /* list of args for current function */
+%}
+
+%union {
+ Node *p;
+ Cell *cp;
+ int i;
+ char *s;
+}
+
+%token <i> FIRSTTOKEN /* must be first */
+%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND
+%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
+%token <i> ARRAY
+%token <i> MATCH NOTMATCH MATCHOP
+%token <i> FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS
+%token <i> AND BOR APPEND EQ GE GT LE LT NE IN
+%token <i> ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
+%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
+%token <i> ADD MINUS MULT DIVIDE MOD
+%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
+%token <i> PRINT PRINTF SPRINTF
+%token <p> ELSE INTEST CONDEXPR
+%token <i> POSTINCR PREINCR POSTDECR PREDECR
+%token <cp> VAR IVAR VARNF CALL NUMBER STRING
+%token <s> REGEXPR
+
+%type <p> pas pattern ppattern plist pplist patlist prarg term re
+%type <p> pa_pat pa_stat pa_stats
+%type <s> reg_expr
+%type <p> simple_stmt opt_simple_stmt stmt stmtlist
+%type <p> var varname funcname varlist
+%type <p> for if else while
+%type <i> do st
+%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
+%type <i> subop print
+
+%right ASGNOP
+%right '?'
+%right ':'
+%left BOR
+%left AND
+%left GETLINE
+%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
+%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
+%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
+%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
+%left REGEXPR VAR VARNF IVAR WHILE '('
+%left CAT
+%left '+' '-'
+%left '*' '/' '%'
+%left NOT UMINUS
+%right POWER
+%right DECR INCR
+%left INDIRECT
+%token LASTTOKEN /* must be last */
+
+%%
+
+program:
+ pas { if (errorflag==0)
+ winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
+ | error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
+ ;
+
+and:
+ AND | and NL
+ ;
+
+bor:
+ BOR | bor NL
+ ;
+
+comma:
+ ',' | comma NL
+ ;
+
+do:
+ DO | do NL
+ ;
+
+else:
+ ELSE | else NL
+ ;
+
+for:
+ FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
+ { --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
+ | FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
+ { --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
+ | FOR '(' varname IN varname rparen {inloop++;} stmt
+ { --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
+ ;
+
+funcname:
+ VAR { setfname($1); }
+ | CALL { setfname($1); }
+ ;
+
+if:
+ IF '(' pattern rparen { $$ = notnull($3); }
+ ;
+
+lbrace:
+ '{' | lbrace NL
+ ;
+
+nl:
+ NL | nl NL
+ ;
+
+opt_nl:
+ /* empty */ { $$ = 0; }
+ | nl
+ ;
+
+opt_pst:
+ /* empty */ { $$ = 0; }
+ | pst
+ ;
+
+
+opt_simple_stmt:
+ /* empty */ { $$ = 0; }
+ | simple_stmt
+ ;
+
+pas:
+ opt_pst { $$ = 0; }
+ | opt_pst pa_stats opt_pst { $$ = $2; }
+ ;
+
+pa_pat:
+ pattern { $$ = notnull($1); }
+ ;
+
+pa_stat:
+ pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
+ | pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); }
+ | pa_pat ',' pa_pat { $$ = pa2stat($1, $3, stat2(PRINT, rectonode(), NIL)); }
+ | pa_pat ',' pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $3, $5); }
+ | lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); }
+ | XBEGIN lbrace stmtlist '}'
+ { beginloc = linkum(beginloc, $3); $$ = 0; }
+ | XEND lbrace stmtlist '}'
+ { endloc = linkum(endloc, $3); $$ = 0; }
+ | FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
+ { infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
+ ;
+
+pa_stats:
+ pa_stat
+ | pa_stats opt_pst pa_stat { $$ = linkum($1, $3); }
+ ;
+
+patlist:
+ pattern
+ | patlist comma pattern { $$ = linkum($1, $3); }
+ ;
+
+ppattern:
+ var ASGNOP ppattern { $$ = op2($2, $1, $3); }
+ | ppattern '?' ppattern ':' ppattern %prec '?'
+ { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
+ | ppattern bor ppattern %prec BOR
+ { $$ = op2(BOR, notnull($1), notnull($3)); }
+ | ppattern and ppattern %prec AND
+ { $$ = op2(AND, notnull($1), notnull($3)); }
+ | ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
+ | ppattern MATCHOP ppattern
+ { if (constnode($3))
+ $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
+ else
+ $$ = op3($2, (Node *)1, $1, $3); }
+ | ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
+ | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
+ | ppattern term %prec CAT { $$ = op2(CAT, $1, $2); }
+ | re
+ | term
+ ;
+
+pattern:
+ var ASGNOP pattern { $$ = op2($2, $1, $3); }
+ | pattern '?' pattern ':' pattern %prec '?'
+ { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
+ | pattern bor pattern %prec BOR
+ { $$ = op2(BOR, notnull($1), notnull($3)); }
+ | pattern and pattern %prec AND
+ { $$ = op2(AND, notnull($1), notnull($3)); }
+ | pattern EQ pattern { $$ = op2($2, $1, $3); }
+ | pattern GE pattern { $$ = op2($2, $1, $3); }
+ | pattern GT pattern { $$ = op2($2, $1, $3); }
+ | pattern LE pattern { $$ = op2($2, $1, $3); }
+ | pattern LT pattern { $$ = op2($2, $1, $3); }
+ | pattern NE pattern { $$ = op2($2, $1, $3); }
+ | pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
+ | pattern MATCHOP pattern
+ { if (constnode($3))
+ $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
+ else
+ $$ = op3($2, (Node *)1, $1, $3); }
+ | pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
+ | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
+ | pattern '|' GETLINE var {
+ if (safe) SYNTAX("cmd | getline is unsafe");
+ else $$ = op3(GETLINE, $4, itonp($2), $1); }
+ | pattern '|' GETLINE {
+ if (safe) SYNTAX("cmd | getline is unsafe");
+ else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
+ | pattern term %prec CAT { $$ = op2(CAT, $1, $2); }
+ | re
+ | term
+ ;
+
+plist:
+ pattern comma pattern { $$ = linkum($1, $3); }
+ | plist comma pattern { $$ = linkum($1, $3); }
+ ;
+
+pplist:
+ ppattern
+ | pplist comma ppattern { $$ = linkum($1, $3); }
+ ;
+
+prarg:
+ /* empty */ { $$ = rectonode(); }
+ | pplist
+ | '(' plist ')' { $$ = $2; }
+ ;
+
+print:
+ PRINT | PRINTF
+ ;
+
+pst:
+ NL | ';' | pst NL | pst ';'
+ ;
+
+rbrace:
+ '}' | rbrace NL
+ ;
+
+re:
+ reg_expr
+ { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
+ | NOT re { $$ = op1(NOT, notnull($2)); }
+ ;
+
+reg_expr:
+ '/' {startreg();} REGEXPR '/' { $$ = $3; }
+ ;
+
+rparen:
+ ')' | rparen NL
+ ;
+
+simple_stmt:
+ print prarg '|' term {
+ if (safe) SYNTAX("print | is unsafe");
+ else $$ = stat3($1, $2, itonp($3), $4); }
+ | print prarg APPEND term {
+ if (safe) SYNTAX("print >> is unsafe");
+ else $$ = stat3($1, $2, itonp($3), $4); }
+ | print prarg GT term {
+ if (safe) SYNTAX("print > is unsafe");
+ else $$ = stat3($1, $2, itonp($3), $4); }
+ | print prarg { $$ = stat3($1, $2, NIL, NIL); }
+ | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
+ | DELETE varname { $$ = stat2(DELETE, makearr($2), 0); }
+ | pattern { $$ = exptostat($1); }
+ | error { yyclearin; SYNTAX("illegal statement"); }
+ ;
+
+st:
+ nl
+ | ';' opt_nl
+ ;
+
+stmt:
+ BREAK st { if (!inloop) SYNTAX("break illegal outside of loops");
+ $$ = stat1(BREAK, NIL); }
+ | CLOSE pattern st { $$ = stat1(CLOSE, $2); }
+ | CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops");
+ $$ = stat1(CONTINUE, NIL); }
+ | do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
+ { $$ = stat2(DO, $3, notnull($7)); }
+ | EXIT pattern st { $$ = stat1(EXIT, $2); }
+ | EXIT st { $$ = stat1(EXIT, NIL); }
+ | for
+ | if stmt else stmt { $$ = stat3(IF, $1, $2, $4); }
+ | if stmt { $$ = stat3(IF, $1, $2, NIL); }
+ | lbrace stmtlist rbrace { $$ = $2; }
+ | NEXT st { if (infunc)
+ SYNTAX("next is illegal inside a function");
+ $$ = stat1(NEXT, NIL); }
+ | NEXTFILE st { if (infunc)
+ SYNTAX("nextfile is illegal inside a function");
+ $$ = stat1(NEXTFILE, NIL); }
+ | RETURN pattern st { $$ = stat1(RETURN, $2); }
+ | RETURN st { $$ = stat1(RETURN, NIL); }
+ | simple_stmt st
+ | while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); }
+ | ';' opt_nl { $$ = 0; }
+ ;
+
+stmtlist:
+ stmt
+ | stmtlist stmt { $$ = linkum($1, $2); }
+ ;
+
+subop:
+ SUB | GSUB
+ ;
+
+term:
+ term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); }
+ | term '+' term { $$ = op2(ADD, $1, $3); }
+ | term '-' term { $$ = op2(MINUS, $1, $3); }
+ | term '*' term { $$ = op2(MULT, $1, $3); }
+ | term '/' term { $$ = op2(DIVIDE, $1, $3); }
+ | term '%' term { $$ = op2(MOD, $1, $3); }
+ | term POWER term { $$ = op2(POWER, $1, $3); }
+ | '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
+ | '+' term %prec UMINUS { $$ = $2; }
+ | NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
+ | BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
+ | BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
+ | BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); }
+ | CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
+ | CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); }
+ | DECR var { $$ = op1(PREDECR, $2); }
+ | INCR var { $$ = op1(PREINCR, $2); }
+ | var DECR { $$ = op1(POSTDECR, $1); }
+ | var INCR { $$ = op1(POSTINCR, $1); }
+ | GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); }
+ | GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); }
+ | GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); }
+ | GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); }
+ | INDEX '(' pattern comma pattern ')'
+ { $$ = op2(INDEX, $3, $5); }
+ | INDEX '(' pattern comma reg_expr ')'
+ { SYNTAX("index() doesn't permit regular expressions");
+ $$ = op2(INDEX, $3, (Node*)$5); }
+ | '(' pattern ')' { $$ = $2; }
+ | MATCHFCN '(' pattern comma reg_expr ')'
+ { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
+ | MATCHFCN '(' pattern comma pattern ')'
+ { if (constnode($5))
+ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
+ else
+ $$ = op3(MATCHFCN, (Node *)1, $3, $5); }
+ | NUMBER { $$ = celltonode($1, CCON); }
+ | SPLIT '(' pattern comma varname comma pattern ')' /* string */
+ { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
+ | SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */
+ { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
+ | SPLIT '(' pattern comma varname ')'
+ { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */
+ | SPRINTF '(' patlist ')' { $$ = op1($1, $3); }
+ | STRING { $$ = celltonode($1, CCON); }
+ | subop '(' reg_expr comma pattern ')'
+ { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
+ | subop '(' pattern comma pattern ')'
+ { if (constnode($3))
+ $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
+ else
+ $$ = op4($1, (Node *)1, $3, $5, rectonode()); }
+ | subop '(' reg_expr comma pattern comma var ')'
+ { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
+ | subop '(' pattern comma pattern comma var ')'
+ { if (constnode($3))
+ $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
+ else
+ $$ = op4($1, (Node *)1, $3, $5, $7); }
+ | SUBSTR '(' pattern comma pattern comma pattern ')'
+ { $$ = op3(SUBSTR, $3, $5, $7); }
+ | SUBSTR '(' pattern comma pattern ')'
+ { $$ = op3(SUBSTR, $3, $5, NIL); }
+ | var
+ ;
+
+var:
+ varname
+ | varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); }
+ | IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
+ | INDIRECT term { $$ = op1(INDIRECT, $2); }
+ ;
+
+varlist:
+ /* nothing */ { arglist = $$ = 0; }
+ | VAR { arglist = $$ = celltonode($1,CVAR); }
+ | varlist comma VAR {
+ checkdup($1, $3);
+ arglist = $$ = linkum($1,celltonode($3,CVAR)); }
+ ;
+
+varname:
+ VAR { $$ = celltonode($1, CVAR); }
+ | ARG { $$ = op1(ARG, itonp($1)); }
+ | VARNF { $$ = op1(VARNF, (Node *) $1); }
+ ;
+
+
+while:
+ WHILE '(' pattern rparen { $$ = notnull($3); }
+ ;
+
+%%
+
+void setfname(Cell *p)
+{
+ if (isarr(p))
+ SYNTAX("%s is an array, not a function", p->nval);
+ else if (isfcn(p))
+ SYNTAX("you can't define function %s more than once", p->nval);
+ curfname = p->nval;
+}
+
+int constnode(Node *p)
+{
+ return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
+}
+
+char *strnode(Node *p)
+{
+ return ((Cell *)(p->narg[0]))->sval;
+}
+
+Node *notnull(Node *n)
+{
+ switch (n->nobj) {
+ case LE: case LT: case EQ: case NE: case GT: case GE:
+ case BOR: case AND: case NOT:
+ return n;
+ default:
+ return op2(NE, n, nullnode);
+ }
+}
+
+void checkdup(Node *vl, Cell *cp) /* check if name already in list */
+{
+ char *s = cp->nval;
+ for ( ; vl; vl = vl->nnext) {
+ if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
+ SYNTAX("duplicate argument %s", s);
+ break;
+ }
+ }
+}
+
diff --git a/bin/9base/awk/lex.c b/bin/9base/awk/lex.c
new file mode 100644
index 00000000..74a99030
--- /dev/null
+++ b/bin/9base/awk/lex.c
@@ -0,0 +1,570 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "awk.h"
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+extern int infunc;
+
+int lineno = 1;
+int bracecnt = 0;
+int brackcnt = 0;
+int parencnt = 0;
+
+typedef struct Keyword {
+ char *word;
+ int sub;
+ int type;
+} Keyword;
+
+Keyword keywords[] ={ /* keep sorted: binary searched */
+ { "BEGIN", XBEGIN, XBEGIN },
+ { "END", XEND, XEND },
+ { "NF", VARNF, VARNF },
+ { "atan2", FATAN, BLTIN },
+ { "break", BREAK, BREAK },
+ { "close", CLOSE, CLOSE },
+ { "continue", CONTINUE, CONTINUE },
+ { "cos", FCOS, BLTIN },
+ { "delete", DELETE, DELETE },
+ { "do", DO, DO },
+ { "else", ELSE, ELSE },
+ { "exit", EXIT, EXIT },
+ { "exp", FEXP, BLTIN },
+ { "fflush", FFLUSH, BLTIN },
+ { "for", FOR, FOR },
+ { "func", FUNC, FUNC },
+ { "function", FUNC, FUNC },
+ { "getline", GETLINE, GETLINE },
+ { "gsub", GSUB, GSUB },
+ { "if", IF, IF },
+ { "in", IN, IN },
+ { "index", INDEX, INDEX },
+ { "int", FINT, BLTIN },
+ { "length", FLENGTH, BLTIN },
+ { "log", FLOG, BLTIN },
+ { "match", MATCHFCN, MATCHFCN },
+ { "next", NEXT, NEXT },
+ { "nextfile", NEXTFILE, NEXTFILE },
+ { "print", PRINT, PRINT },
+ { "printf", PRINTF, PRINTF },
+ { "rand", FRAND, BLTIN },
+ { "return", RETURN, RETURN },
+ { "sin", FSIN, BLTIN },
+ { "split", SPLIT, SPLIT },
+ { "sprintf", SPRINTF, SPRINTF },
+ { "sqrt", FSQRT, BLTIN },
+ { "srand", FSRAND, BLTIN },
+ { "sub", SUB, SUB },
+ { "substr", SUBSTR, SUBSTR },
+ { "system", FSYSTEM, BLTIN },
+ { "tolower", FTOLOWER, BLTIN },
+ { "toupper", FTOUPPER, BLTIN },
+ { "utf", FUTF, BLTIN },
+ { "while", WHILE, WHILE },
+};
+
+#define DEBUG
+#ifdef DEBUG
+#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
+#else
+#define RET(x) return(x)
+#endif
+
+int peek(void)
+{
+ int c = input();
+ unput(c);
+ return c;
+}
+
+int gettok(char **pbuf, int *psz) /* get next input token */
+{
+ int c;
+ char *buf = *pbuf;
+ int sz = *psz;
+ char *bp = buf;
+
+ c = input();
+ if (c == 0)
+ return 0;
+ buf[0] = c;
+ buf[1] = 0;
+ if (!isalnum(c) && c != '.' && c != '_')
+ return c;
+
+ *bp++ = c;
+ if (isalpha(c) || c == '_') { /* it's a varname */
+ for ( ; (c = input()) != 0; ) {
+ if (bp-buf >= sz)
+ if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
+ FATAL( "out of space for name %.10s...", buf );
+ if (isalnum(c) || c == '_')
+ *bp++ = c;
+ else {
+ *bp = 0;
+ unput(c);
+ break;
+ }
+ }
+ } else { /* it's a number */
+ char *rem;
+ /* read input until can't be a number */
+ for ( ; (c = input()) != 0; ) {
+ if (bp-buf >= sz)
+ if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
+ FATAL( "out of space for number %.10s...", buf );
+ if (isdigit(c) || c == 'e' || c == 'E'
+ || c == '.' || c == '+' || c == '-')
+ *bp++ = c;
+ else {
+ unput(c);
+ break;
+ }
+ }
+ *bp = 0;
+ strtod(buf, &rem); /* parse the number */
+ unputstr(rem); /* put rest back for later */
+ rem[0] = 0;
+ }
+ *pbuf = buf;
+ *psz = sz;
+ return buf[0];
+}
+
+int word(char *);
+int string(void);
+int regexpr(void);
+int sc = 0; /* 1 => return a } right now */
+int reg = 0; /* 1 => return a REGEXPR now */
+
+int yylex(void)
+{
+ int c;
+ static char *buf = 0;
+ static int bufsize = 500;
+
+ if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL)
+ FATAL( "out of space in yylex" );
+ if (sc) {
+ sc = 0;
+ RET('}');
+ }
+ if (reg) {
+ reg = 0;
+ return regexpr();
+ }
+ for (;;) {
+ c = gettok(&buf, &bufsize);
+ if (c == 0)
+ return 0;
+ if (isalpha(c) || c == '_')
+ return word(buf);
+ if (isdigit(c) || c == '.') {
+ yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab);
+ /* should this also have STR set? */
+ RET(NUMBER);
+ }
+
+ yylval.i = c;
+ switch (c) {
+ case '\n': /* {EOL} */
+ RET(NL);
+ case '\r': /* assume \n is coming */
+ case ' ': /* {WS}+ */
+ case '\t':
+ break;
+ case '#': /* #.* strip comments */
+ while ((c = input()) != '\n' && c != 0)
+ ;
+ unput(c);
+ break;
+ case ';':
+ RET(';');
+ case '\\':
+ if (peek() == '\n') {
+ input();
+ } else if (peek() == '\r') {
+ input(); input(); /* \n */
+ lineno++;
+ } else {
+ RET(c);
+ }
+ break;
+ case '&':
+ if (peek() == '&') {
+ input(); RET(AND);
+ } else
+ RET('&');
+ case '|':
+ if (peek() == '|') {
+ input(); RET(BOR);
+ } else
+ RET('|');
+ case '!':
+ if (peek() == '=') {
+ input(); yylval.i = NE; RET(NE);
+ } else if (peek() == '~') {
+ input(); yylval.i = NOTMATCH; RET(MATCHOP);
+ } else
+ RET(NOT);
+ case '~':
+ yylval.i = MATCH;
+ RET(MATCHOP);
+ case '<':
+ if (peek() == '=') {
+ input(); yylval.i = LE; RET(LE);
+ } else {
+ yylval.i = LT; RET(LT);
+ }
+ case '=':
+ if (peek() == '=') {
+ input(); yylval.i = EQ; RET(EQ);
+ } else {
+ yylval.i = ASSIGN; RET(ASGNOP);
+ }
+ case '>':
+ if (peek() == '=') {
+ input(); yylval.i = GE; RET(GE);
+ } else if (peek() == '>') {
+ input(); yylval.i = APPEND; RET(APPEND);
+ } else {
+ yylval.i = GT; RET(GT);
+ }
+ case '+':
+ if (peek() == '+') {
+ input(); yylval.i = INCR; RET(INCR);
+ } else if (peek() == '=') {
+ input(); yylval.i = ADDEQ; RET(ASGNOP);
+ } else
+ RET('+');
+ case '-':
+ if (peek() == '-') {
+ input(); yylval.i = DECR; RET(DECR);
+ } else if (peek() == '=') {
+ input(); yylval.i = SUBEQ; RET(ASGNOP);
+ } else
+ RET('-');
+ case '*':
+ if (peek() == '=') { /* *= */
+ input(); yylval.i = MULTEQ; RET(ASGNOP);
+ } else if (peek() == '*') { /* ** or **= */
+ input(); /* eat 2nd * */
+ if (peek() == '=') {
+ input(); yylval.i = POWEQ; RET(ASGNOP);
+ } else {
+ RET(POWER);
+ }
+ } else
+ RET('*');
+ case '/':
+ RET('/');
+ case '%':
+ if (peek() == '=') {
+ input(); yylval.i = MODEQ; RET(ASGNOP);
+ } else
+ RET('%');
+ case '^':
+ if (peek() == '=') {
+ input(); yylval.i = POWEQ; RET(ASGNOP);
+ } else
+ RET(POWER);
+
+ case '$':
+ /* BUG: awkward, if not wrong */
+ c = gettok(&buf, &bufsize);
+ if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
+ unputstr(buf);
+ RET(INDIRECT);
+ } else if (isalpha(c)) {
+ if (strcmp(buf, "NF") == 0) { /* very special */
+ unputstr("(NF)");
+ RET(INDIRECT);
+ }
+ yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
+ RET(IVAR);
+ } else {
+ unputstr(buf);
+ RET(INDIRECT);
+ }
+
+ case '}':
+ if (--bracecnt < 0)
+ SYNTAX( "extra }" );
+ sc = 1;
+ RET(';');
+ case ']':
+ if (--brackcnt < 0)
+ SYNTAX( "extra ]" );
+ RET(']');
+ case ')':
+ if (--parencnt < 0)
+ SYNTAX( "extra )" );
+ RET(')');
+ case '{':
+ bracecnt++;
+ RET('{');
+ case '[':
+ brackcnt++;
+ RET('[');
+ case '(':
+ parencnt++;
+ RET('(');
+
+ case '"':
+ return string(); /* BUG: should be like tran.c ? */
+
+ default:
+ RET(c);
+ }
+ }
+}
+
+int string(void)
+{
+ int c, n;
+ char *s, *bp;
+ static char *buf = 0;
+ static int bufsz = 500;
+
+ if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of space for strings");
+ for (bp = buf; (c = input()) != '"'; ) {
+ if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, 0))
+ FATAL("out of space for string %.10s...", buf);
+ switch (c) {
+ case '\n':
+ case '\r':
+ case 0:
+ SYNTAX( "non-terminated string %.10s...", buf );
+ lineno++;
+ break;
+ case '\\':
+ c = input();
+ switch (c) {
+ case '"': *bp++ = '"'; break;
+ case 'n': *bp++ = '\n'; break;
+ case 't': *bp++ = '\t'; break;
+ case 'f': *bp++ = '\f'; break;
+ case 'r': *bp++ = '\r'; break;
+ case 'b': *bp++ = '\b'; break;
+ case 'v': *bp++ = '\v'; break;
+ case 'a': *bp++ = '\007'; break;
+ case '\\': *bp++ = '\\'; break;
+
+ case '0': case '1': case '2': /* octal: \d \dd \ddd */
+ case '3': case '4': case '5': case '6': case '7':
+ n = c - '0';
+ if ((c = peek()) >= '0' && c < '8') {
+ n = 8 * n + input() - '0';
+ if ((c = peek()) >= '0' && c < '8')
+ n = 8 * n + input() - '0';
+ }
+ *bp++ = n;
+ break;
+
+ case 'x': /* hex \x0-9a-fA-F + */
+ { char xbuf[100], *px;
+ for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) {
+ if (isdigit(c)
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'))
+ *px++ = c;
+ else
+ break;
+ }
+ *px = 0;
+ unput(c);
+ sscanf(xbuf, "%x", &n);
+ *bp++ = n;
+ break;
+ }
+
+ default:
+ *bp++ = c;
+ break;
+ }
+ break;
+ default:
+ *bp++ = c;
+ break;
+ }
+ }
+ *bp = 0;
+ s = tostring(buf);
+ *bp++ = ' '; *bp++ = 0;
+ yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
+ RET(STRING);
+}
+
+
+int binsearch(char *w, Keyword *kp, int n)
+{
+ int cond, low, mid, high;
+
+ low = 0;
+ high = n - 1;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if ((cond = strcmp(w, kp[mid].word)) < 0)
+ high = mid - 1;
+ else if (cond > 0)
+ low = mid + 1;
+ else
+ return mid;
+ }
+ return -1;
+}
+
+int word(char *w)
+{
+ Keyword *kp;
+ int c, n;
+
+ n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
+ kp = keywords + n;
+ if (n != -1) { /* found in table */
+ yylval.i = kp->sub;
+ switch (kp->type) { /* special handling */
+ case FSYSTEM:
+ if (safe)
+ SYNTAX( "system is unsafe" );
+ RET(kp->type);
+ case FUNC:
+ if (infunc)
+ SYNTAX( "illegal nested function" );
+ RET(kp->type);
+ case RETURN:
+ if (!infunc)
+ SYNTAX( "return not in function" );
+ RET(kp->type);
+ case VARNF:
+ yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
+ RET(VARNF);
+ default:
+ RET(kp->type);
+ }
+ }
+ c = peek(); /* look for '(' */
+ if (c != '(' && infunc && (n=isarg(w)) >= 0) {
+ yylval.i = n;
+ RET(ARG);
+ } else {
+ yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
+ if (c == '(') {
+ RET(CALL);
+ } else {
+ RET(VAR);
+ }
+ }
+}
+
+void startreg(void) /* next call to yyles will return a regular expression */
+{
+ reg = 1;
+}
+
+int regexpr(void)
+{
+ int c;
+ static char *buf = 0;
+ static int bufsz = 500;
+ char *bp;
+
+ if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of space for rex expr");
+ bp = buf;
+ for ( ; (c = input()) != '/' && c != 0; ) {
+ if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, 0))
+ FATAL("out of space for reg expr %.10s...", buf);
+ if (c == '\n') {
+ SYNTAX( "newline in regular expression %.10s...", buf );
+ unput('\n');
+ break;
+ } else if (c == '\\') {
+ *bp++ = '\\';
+ *bp++ = input();
+ } else {
+ *bp++ = c;
+ }
+ }
+ *bp = 0;
+ yylval.s = tostring(buf);
+ unput('/');
+ RET(REGEXPR);
+}
+
+/* low-level lexical stuff, sort of inherited from lex */
+
+char ebuf[300];
+char *ep = ebuf;
+char yysbuf[100]; /* pushback buffer */
+char *yysptr = yysbuf;
+FILE *yyin = 0;
+
+int input(void) /* get next lexical input character */
+{
+ int c;
+ extern char *lexprog;
+
+ if (yysptr > yysbuf)
+ c = *--yysptr;
+ else if (lexprog != NULL) { /* awk '...' */
+ if ((c = *lexprog) != 0)
+ lexprog++;
+ } else /* awk -f ... */
+ c = pgetc();
+ if (c == '\n')
+ lineno++;
+ else if (c == EOF)
+ c = 0;
+ if (ep >= ebuf + sizeof ebuf)
+ ep = ebuf;
+ return *ep++ = c;
+}
+
+void unput(int c) /* put lexical character back on input */
+{
+ if (c == '\n')
+ lineno--;
+ if (yysptr >= yysbuf + sizeof(yysbuf))
+ FATAL("pushed back too much: %.20s...", yysbuf);
+ *yysptr++ = c;
+ if (--ep < ebuf)
+ ep = ebuf + sizeof(ebuf) - 1;
+}
+
+void unputstr(char *s) /* put a string back on input */
+{
+ int i;
+
+ for (i = strlen(s)-1; i >= 0; i--)
+ unput(s[i]);
+}
+
diff --git a/bin/9base/awk/lib.c b/bin/9base/awk/lib.c
new file mode 100644
index 00000000..6a6849c5
--- /dev/null
+++ b/bin/9base/awk/lib.c
@@ -0,0 +1,713 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "awk.h"
+#include "y.tab.h"
+
+FILE *infile = NULL;
+char *file = "";
+char *record;
+int recsize = RECSIZE;
+char *fields;
+int fieldssize = RECSIZE;
+
+Cell **fldtab; /* pointers to Cells */
+char inputFS[100] = " ";
+
+#define MAXFLD 200
+int nfields = MAXFLD; /* last allocated slot for $i */
+
+int donefld; /* 1 = implies rec broken into fields */
+int donerec; /* 1 = record is valid (no flds have changed) */
+
+int lastfld = 0; /* last used field */
+int argno = 1; /* current input argument number */
+extern Awkfloat *ARGC;
+
+static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
+static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
+
+void recinit(unsigned int n)
+{
+ record = (char *) malloc(n);
+ fields = (char *) malloc(n);
+ fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
+ if (record == NULL || fields == NULL || fldtab == NULL)
+ FATAL("out of space for $0 and fields");
+ fldtab[0] = (Cell *) malloc(sizeof (Cell));
+ *fldtab[0] = dollar0;
+ fldtab[0]->sval = record;
+ fldtab[0]->nval = tostring("0");
+ makefields(1, nfields);
+}
+
+void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
+{
+ char temp[50];
+ int i;
+
+ for (i = n1; i <= n2; i++) {
+ fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
+ if (fldtab[i] == NULL)
+ FATAL("out of space in makefields %d", i);
+ *fldtab[i] = dollar1;
+ sprintf(temp, "%d", i);
+ fldtab[i]->nval = tostring(temp);
+ }
+}
+
+void initgetrec(void)
+{
+ int i;
+ char *p;
+
+ for (i = 1; i < *ARGC; i++) {
+ if (!isclvar(p = getargv(i))) { /* find 1st real filename */
+ setsval(lookup("FILENAME", symtab), getargv(i));
+ return;
+ }
+ setclvar(p); /* a commandline assignment before filename */
+ argno++;
+ }
+ infile = stdin; /* no filenames, so use stdin */
+}
+
+int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */
+{ /* note: cares whether buf == record */
+ int c;
+ static int firsttime = 1;
+ char *buf = *pbuf;
+ int bufsize = *pbufsize;
+
+ if (firsttime) {
+ firsttime = 0;
+ initgetrec();
+ }
+ dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
+ *RS, *FS, *ARGC, *FILENAME) );
+ if (isrecord) {
+ donefld = 0;
+ donerec = 1;
+ }
+ buf[0] = 0;
+ while (argno < *ARGC || infile == stdin) {
+ dprintf( ("argno=%d, file=|%s|\n", argno, file) );
+ if (infile == NULL) { /* have to open a new file */
+ file = getargv(argno);
+ if (*file == '\0') { /* it's been zapped */
+ argno++;
+ continue;
+ }
+ if (isclvar(file)) { /* a var=value arg */
+ setclvar(file);
+ argno++;
+ continue;
+ }
+ *FILENAME = file;
+ dprintf( ("opening file %s\n", file) );
+ if (*file == '-' && *(file+1) == '\0')
+ infile = stdin;
+ else if ((infile = fopen(file, "r")) == NULL)
+ FATAL("can't open file %s", file);
+ setfval(fnrloc, 0.0);
+ }
+ c = readrec(&buf, &bufsize, infile);
+ if (c != 0 || buf[0] != '\0') { /* normal record */
+ if (isrecord) {
+ if (freeable(fldtab[0]))
+ xfree(fldtab[0]->sval);
+ fldtab[0]->sval = buf; /* buf == record */
+ fldtab[0]->tval = REC | STR | DONTFREE;
+ if (is_number(fldtab[0]->sval)) {
+ fldtab[0]->fval = atof(fldtab[0]->sval);
+ fldtab[0]->tval |= NUM;
+ }
+ }
+ setfval(nrloc, nrloc->fval+1);
+ setfval(fnrloc, fnrloc->fval+1);
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ return 1;
+ }
+ /* EOF arrived on this file; set up next */
+ if (infile != stdin)
+ fclose(infile);
+ infile = NULL;
+ argno++;
+ }
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ return 0; /* true end of file */
+}
+
+void nextfile(void)
+{
+ if (infile != stdin)
+ fclose(infile);
+ infile = NULL;
+ argno++;
+}
+
+int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
+{
+ int sep, c;
+ char *rr, *buf = *pbuf;
+ int bufsize = *pbufsize;
+
+ if (strlen(*FS) >= sizeof(inputFS))
+ FATAL("field separator %.10s... is too long", *FS);
+ strcpy(inputFS, *FS); /* for subsequent field splitting */
+ if ((sep = **RS) == 0) {
+ sep = '\n';
+ while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
+ ;
+ if (c != EOF)
+ ungetc(c, inf);
+ }
+ for (rr = buf; ; ) {
+ for (; (c=getc(inf)) != sep && c != EOF; ) {
+ if (rr-buf+1 > bufsize)
+ if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
+ FATAL("input record `%.30s...' too long", buf);
+ *rr++ = c;
+ }
+ if (**RS == sep || c == EOF)
+ break;
+ if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
+ break;
+ if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
+ FATAL("input record `%.30s...' too long", buf);
+ *rr++ = '\n';
+ *rr++ = c;
+ }
+ if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
+ FATAL("input record `%.30s...' too long", buf);
+ *rr = 0;
+ dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ return c == EOF && rr == buf ? 0 : 1;
+}
+
+char *getargv(int n) /* get ARGV[n] */
+{
+ Cell *x;
+ char *s, temp[50];
+ extern Array *ARGVtab;
+
+ sprintf(temp, "%d", n);
+ x = setsymtab(temp, "", 0.0, STR, ARGVtab);
+ s = getsval(x);
+ dprintf( ("getargv(%d) returns |%s|\n", n, s) );
+ return s;
+}
+
+void setclvar(char *s) /* set var=value from s */
+{
+ char *p;
+ Cell *q;
+
+ for (p=s; *p != '='; p++)
+ ;
+ *p++ = 0;
+ p = qstring(p, '\0');
+ q = setsymtab(s, p, 0.0, STR, symtab);
+ setsval(q, p);
+ if (is_number(q->sval)) {
+ q->fval = atof(q->sval);
+ q->tval |= NUM;
+ }
+ dprintf( ("command line set %s to |%s|\n", s, p) );
+}
+
+
+void fldbld(void) /* create fields from current record */
+{
+ /* this relies on having fields[] the same length as $0 */
+ /* the fields are all stored in this one array with \0's */
+ char *r, *fr, sep;
+ Cell *p;
+ int i, j, n;
+
+ if (donefld)
+ return;
+ if (!isstr(fldtab[0]))
+ getsval(fldtab[0]);
+ r = fldtab[0]->sval;
+ n = strlen(r);
+ if (n > fieldssize) {
+ xfree(fields);
+ if ((fields = (char *) malloc(n+1)) == NULL)
+ FATAL("out of space for fields in fldbld %d", n);
+ fieldssize = n;
+ }
+ fr = fields;
+ i = 0; /* number of fields accumulated here */
+ if (strlen(inputFS) > 1) { /* it's a regular expression */
+ i = refldbld(r, inputFS);
+ } else if ((sep = *inputFS) == ' ') { /* default whitespace */
+ for (i = 0; ; ) {
+ while (*r == ' ' || *r == '\t' || *r == '\n')
+ r++;
+ if (*r == 0)
+ break;
+ i++;
+ if (i > nfields)
+ growfldtab(i);
+ if (freeable(fldtab[i]))
+ xfree(fldtab[i]->sval);
+ fldtab[i]->sval = fr;
+ fldtab[i]->tval = FLD | STR | DONTFREE;
+ do
+ *fr++ = *r++;
+ while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
+ *fr++ = 0;
+ }
+ *fr = 0;
+ } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
+ for (i = 0; *r != 0; r++) {
+ char buf[2];
+ i++;
+ if (i > nfields)
+ growfldtab(i);
+ if (freeable(fldtab[i]))
+ xfree(fldtab[i]->sval);
+ buf[0] = *r;
+ buf[1] = 0;
+ fldtab[i]->sval = tostring(buf);
+ fldtab[i]->tval = FLD | STR;
+ }
+ *fr = 0;
+ } else if (*r != 0) { /* if 0, it's a null field */
+ for (;;) {
+ i++;
+ if (i > nfields)
+ growfldtab(i);
+ if (freeable(fldtab[i]))
+ xfree(fldtab[i]->sval);
+ fldtab[i]->sval = fr;
+ fldtab[i]->tval = FLD | STR | DONTFREE;
+ while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */
+ *fr++ = *r++;
+ *fr++ = 0;
+ if (*r++ == 0)
+ break;
+ }
+ *fr = 0;
+ }
+ if (i > nfields)
+ FATAL("record `%.30s...' has too many fields; can't happen", r);
+ cleanfld(i+1, lastfld); /* clean out junk from previous record */
+ lastfld = i;
+ donefld = 1;
+ for (j = 1; j <= lastfld; j++) {
+ p = fldtab[j];
+ if(is_number(p->sval)) {
+ p->fval = atof(p->sval);
+ p->tval |= NUM;
+ }
+ }
+ setfval(nfloc, (Awkfloat) lastfld);
+ if (dbg) {
+ for (j = 0; j <= lastfld; j++) {
+ p = fldtab[j];
+ printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
+ }
+ }
+}
+
+void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
+{ /* nvals remain intact */
+ Cell *p;
+ int i;
+
+ for (i = n1; i <= n2; i++) {
+ p = fldtab[i];
+ if (freeable(p))
+ xfree(p->sval);
+ p->sval = "";
+ p->tval = FLD | STR | DONTFREE;
+ }
+}
+
+void newfld(int n) /* add field n after end of existing lastfld */
+{
+ if (n > nfields)
+ growfldtab(n);
+ cleanfld(lastfld+1, n);
+ lastfld = n;
+ setfval(nfloc, (Awkfloat) n);
+}
+
+Cell *fieldadr(int n) /* get nth field */
+{
+ if (n < 0)
+ FATAL("trying to access field %d", n);
+ if (n > nfields) /* fields after NF are empty */
+ growfldtab(n); /* but does not increase NF */
+ return(fldtab[n]);
+}
+
+void growfldtab(int n) /* make new fields up to at least $n */
+{
+ int nf = 2 * nfields;
+
+ if (n > nf)
+ nf = n;
+ fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
+ if (fldtab == NULL)
+ FATAL("out of space creating %d fields", nf);
+ makefields(nfields+1, nf);
+ nfields = nf;
+}
+
+int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */
+{
+ /* this relies on having fields[] the same length as $0 */
+ /* the fields are all stored in this one array with \0's */
+ char *fr;
+ void *p;
+ int i, n;
+
+ n = strlen(rec);
+ if (n > fieldssize) {
+ xfree(fields);
+ if ((fields = (char *) malloc(n+1)) == NULL)
+ FATAL("out of space for fields in refldbld %d", n);
+ fieldssize = n;
+ }
+ fr = fields;
+ *fr = '\0';
+ if (*rec == '\0')
+ return 0;
+ p = compre(fs);
+ dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
+ for (i = 1; ; i++) {
+ if (i > nfields)
+ growfldtab(i);
+ if (freeable(fldtab[i]))
+ xfree(fldtab[i]->sval);
+ fldtab[i]->tval = FLD | STR | DONTFREE;
+ fldtab[i]->sval = fr;
+ dprintf( ("refldbld: i=%d\n", i) );
+ if (nematch(p, rec, rec)) {
+ dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
+ strncpy(fr, rec, patbeg-rec);
+ fr += patbeg - rec + 1;
+ *(fr-1) = '\0';
+ rec = patbeg + patlen;
+ } else {
+ dprintf( ("no match %s\n", rec) );
+ strcpy(fr, rec);
+ break;
+ }
+ }
+ return i;
+}
+
+void recbld(void) /* create $0 from $1..$NF if necessary */
+{
+ int i;
+ char *r, *p;
+
+ if (donerec == 1)
+ return;
+ r = record;
+ for (i = 1; i <= *NF; i++) {
+ p = getsval(fldtab[i]);
+ if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
+ FATAL("created $0 `%.30s...' too long", record);
+ while ((*r = *p++) != 0)
+ r++;
+ if (i < *NF) {
+ if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
+ FATAL("created $0 `%.30s...' too long", record);
+ for (p = *OFS; (*r = *p++) != 0; )
+ r++;
+ }
+ }
+ if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
+ FATAL("built giant record `%.30s...'", record);
+ *r = '\0';
+ dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
+
+ if (freeable(fldtab[0]))
+ xfree(fldtab[0]->sval);
+ fldtab[0]->tval = REC | STR | DONTFREE;
+ fldtab[0]->sval = record;
+
+ dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
+ dprintf( ("recbld = |%s|\n", record) );
+ donerec = 1;
+}
+
+int errorflag = 0;
+
+void yyerror(char *s)
+{
+ SYNTAX(s);
+}
+
+void SYNTAX(char *fmt, ...)
+{
+ extern char *cmdname, *curfname;
+ static int been_here = 0;
+ va_list varg;
+
+ if (been_here++ > 2)
+ return;
+ fprintf(stderr, "%s: ", cmdname);
+ va_start(varg, fmt);
+ vfprintf(stderr, fmt, varg);
+ va_end(varg);
+ if(compile_time == 1 && cursource() != NULL)
+ fprintf(stderr, " at %s:%d", cursource(), lineno);
+ else
+ fprintf(stderr, " at line %d", lineno);
+ if (curfname != NULL)
+ fprintf(stderr, " in function %s", curfname);
+ fprintf(stderr, "\n");
+ errorflag = 2;
+ eprint();
+}
+
+void fpecatch(int n)
+{
+ FATAL("floating point exception %d", n);
+}
+
+extern int bracecnt, brackcnt, parencnt;
+
+void bracecheck(void)
+{
+ int c;
+ static int beenhere = 0;
+
+ if (beenhere++)
+ return;
+ while ((c = input()) != EOF && c != '\0')
+ bclass(c);
+ bcheck2(bracecnt, '{', '}');
+ bcheck2(brackcnt, '[', ']');
+ bcheck2(parencnt, '(', ')');
+}
+
+void bcheck2(int n, int c1, int c2)
+{
+ if (n == 1)
+ fprintf(stderr, "\tmissing %c\n", c2);
+ else if (n > 1)
+ fprintf(stderr, "\t%d missing %c's\n", n, c2);
+ else if (n == -1)
+ fprintf(stderr, "\textra %c\n", c2);
+ else if (n < -1)
+ fprintf(stderr, "\t%d extra %c's\n", -n, c2);
+}
+
+void FATAL(char *fmt, ...)
+{
+ extern char *cmdname;
+ va_list varg;
+
+ fflush(stdout);
+ fprintf(stderr, "%s: ", cmdname);
+ va_start(varg, fmt);
+ vfprintf(stderr, fmt, varg);
+ va_end(varg);
+ error();
+ if (dbg > 1) /* core dump if serious debugging on */
+ abort();
+ exit(2);
+}
+
+void WARNING(char *fmt, ...)
+{
+ extern char *cmdname;
+ va_list varg;
+
+ fflush(stdout);
+ fprintf(stderr, "%s: ", cmdname);
+ va_start(varg, fmt);
+ vfprintf(stderr, fmt, varg);
+ va_end(varg);
+ error();
+}
+
+void error()
+{
+ extern Node *curnode;
+ int line;
+
+ fprintf(stderr, "\n");
+ if (compile_time != 2 && NR && *NR > 0) {
+ if (strcmp(*FILENAME, "-") != 0)
+ fprintf(stderr, " input record %s:%d", *FILENAME, (int) (*FNR));
+ else
+ fprintf(stderr, " input record number %d", (int) (*FNR));
+ fprintf(stderr, "\n");
+ }
+ if (compile_time != 2 && curnode)
+ line = curnode->lineno;
+ else if (compile_time != 2 && lineno)
+ line = lineno;
+ else
+ line = -1;
+ if (compile_time == 1 && cursource() != NULL){
+ if(line >= 0)
+ fprintf(stderr, " source %s:%d", cursource(), line);
+ else
+ fprintf(stderr, " source file %s", cursource());
+ }else if(line >= 0)
+ fprintf(stderr, " source line %d", line);
+ fprintf(stderr, "\n");
+ eprint();
+}
+
+void eprint(void) /* try to print context around error */
+{
+ char *p, *q;
+ int c;
+ static int been_here = 0;
+ extern char ebuf[], *ep;
+
+ if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
+ return;
+ p = ep - 1;
+ if (p > ebuf && *p == '\n')
+ p--;
+ for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
+ ;
+ while (*p == '\n')
+ p++;
+ fprintf(stderr, " context is\n\t");
+ for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
+ ;
+ for ( ; p < q; p++)
+ if (*p)
+ putc(*p, stderr);
+ fprintf(stderr, " >>> ");
+ for ( ; p < ep; p++)
+ if (*p)
+ putc(*p, stderr);
+ fprintf(stderr, " <<< ");
+ if (*ep)
+ while ((c = input()) != '\n' && c != '\0' && c != EOF) {
+ putc(c, stderr);
+ bclass(c);
+ }
+ putc('\n', stderr);
+ ep = ebuf;
+}
+
+void bclass(int c)
+{
+ switch (c) {
+ case '{': bracecnt++; break;
+ case '}': bracecnt--; break;
+ case '[': brackcnt++; break;
+ case ']': brackcnt--; break;
+ case '(': parencnt++; break;
+ case ')': parencnt--; break;
+ }
+}
+
+double errcheck(double x, char *s)
+{
+
+ if (errno == EDOM) {
+ errno = 0;
+ WARNING("%s argument out of domain", s);
+ x = 1;
+ } else if (errno == ERANGE) {
+ errno = 0;
+ WARNING("%s result out of range", s);
+ x = 1;
+ }
+ return x;
+}
+
+int isclvar(char *s) /* is s of form var=something ? */
+{
+ char *os = s;
+
+ if (!isalpha(*s) && *s != '_')
+ return 0;
+ for ( ; *s; s++)
+ if (!(isalnum(*s) || *s == '_'))
+ break;
+ return *s == '=' && s > os && *(s+1) != '=';
+}
+
+/* strtod is supposed to be a proper test of what's a valid number */
+
+#include <math.h>
+int is_number(char *s)
+{
+ double r;
+ char *ep;
+
+ /*
+ * fast could-it-be-a-number check before calling strtod,
+ * which takes a surprisingly long time to reject non-numbers.
+ */
+ switch (*s) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ case '-':
+ case '+':
+ case '.':
+ case 'n': /* nans */
+ case 'N':
+ case 'i': /* infs */
+ case 'I':
+ break;
+ default:
+ return 0; /* can't be a number */
+ }
+
+ errno = 0;
+ r = strtod(s, &ep);
+ if (ep == s || r == HUGE_VAL || errno == ERANGE)
+ return 0;
+ while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+ ep++;
+ if (*ep == '\0')
+ return 1;
+ else
+ return 0;
+}
+
diff --git a/bin/9base/awk/main.c b/bin/9base/awk/main.c
new file mode 100644
index 00000000..ea20f63e
--- /dev/null
+++ b/bin/9base/awk/main.c
@@ -0,0 +1,198 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+char *version = "version 19990602";
+
+#define DEBUG
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "awk.h"
+#include "y.tab.h"
+
+extern char **environ;
+extern int nfields;
+
+int dbg = 0;
+char *cmdname; /* gets argv[0] for error messages */
+extern FILE *yyin; /* lex input file */
+char *lexprog; /* points to program argument if it exists */
+extern int errorflag; /* non-zero if any syntax errors; set by yyerror */
+int compile_time = 2; /* for error printing: */
+ /* 2 = cmdline, 1 = compile, 0 = running */
+
+char *pfile[20]; /* program filenames from -f's */
+int npfile = 0; /* number of filenames */
+int curpfile = 0; /* current filename */
+
+int safe = 0; /* 1 => "safe" mode */
+
+int main(int argc, char *argv[])
+{
+ char *fs = NULL, *marg;
+ int temp;
+
+ cmdname = argv[0];
+ if (argc == 1) {
+ fprintf(stderr, "Usage: %s [-F fieldsep] [-mf n] [-mr n] [-v var=value] [-f programfile | 'program'] [file ...]\n", cmdname);
+ exit(1);
+ }
+ signal(SIGFPE, fpecatch);
+ yyin = NULL;
+ symtab = makesymtab(NSYMTAB);
+ while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
+ if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
+ argc--;
+ argv++;
+ break;
+ }
+ switch (argv[1][1]) {
+ case 's':
+ if (strcmp(argv[1], "-safe") == 0)
+ safe = 1;
+ break;
+ case 'f': /* next argument is program filename */
+ argc--;
+ argv++;
+ if (argc <= 1)
+ FATAL("no program filename");
+ pfile[npfile++] = argv[1];
+ break;
+ case 'F': /* set field separator */
+ if (argv[1][2] != 0) { /* arg is -Fsomething */
+ if (argv[1][2] == 't' && argv[1][3] == 0) /* wart: t=>\t */
+ fs = "\t";
+ else if (argv[1][2] != 0)
+ fs = &argv[1][2];
+ } else { /* arg is -F something */
+ argc--; argv++;
+ if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0) /* wart: t=>\t */
+ fs = "\t";
+ else if (argc > 1 && argv[1][0] != 0)
+ fs = &argv[1][0];
+ }
+ if (fs == NULL || *fs == '\0')
+ WARNING("field separator FS is empty");
+ break;
+ case 'v': /* -v a=1 to be done NOW. one -v for each */
+ if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1]))
+ setclvar(argv[1]);
+ break;
+ case 'm': /* more memory: -mr=record, -mf=fields */
+ /* no longer needed */
+ marg = argv[1];
+ if (argv[1][3])
+ temp = atoi(&argv[1][3]);
+ else {
+ argv++; argc--;
+ temp = atoi(&argv[1][0]);
+ }
+ switch (marg[2]) {
+ case 'r': recsize = temp; break;
+ case 'f': nfields = temp; break;
+ default: FATAL("unknown option %s\n", marg);
+ }
+ break;
+ case 'd':
+ dbg = atoi(&argv[1][2]);
+ if (dbg == 0)
+ dbg = 1;
+ printf("awk %s\n", version);
+ break;
+ case 'V': /* added for exptools "standard" */
+ printf("awk %s\n", version);
+ exit(0);
+ break;
+ default:
+ WARNING("unknown option %s ignored", argv[1]);
+ break;
+ }
+ argc--;
+ argv++;
+ }
+ /* argv[1] is now the first argument */
+ if (npfile == 0) { /* no -f; first argument is program */
+ if (argc <= 1) {
+ if (dbg)
+ exit(0);
+ FATAL("no program given");
+ }
+ dprintf( ("program = |%s|\n", argv[1]) );
+ lexprog = argv[1];
+ argc--;
+ argv++;
+ }
+ recinit(recsize);
+ syminit();
+ compile_time = 1;
+ argv[0] = cmdname; /* put prog name at front of arglist */
+ dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) );
+ arginit(argc, argv);
+ if (!safe)
+ envinit(environ);
+ yyparse();
+ if (fs)
+ *FS = qstring(fs, '\0');
+ dprintf( ("errorflag=%d\n", errorflag) );
+ if (errorflag == 0) {
+ compile_time = 0;
+ run(winner);
+ } else
+ bracecheck();
+ return(errorflag);
+}
+
+int pgetc(void) /* get 1 character from awk program */
+{
+ int c;
+
+ for (;;) {
+ if (yyin == NULL) {
+ if (curpfile >= npfile)
+ return EOF;
+ if (strcmp(pfile[curpfile], "-") == 0)
+ yyin = stdin;
+ else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
+ FATAL("can't open file %s", pfile[curpfile]);
+ lineno = 1;
+ }
+ if ((c = getc(yyin)) != EOF)
+ return c;
+ if (yyin != stdin)
+ fclose(yyin);
+ yyin = NULL;
+ curpfile++;
+ }
+}
+
+char *cursource(void) /* current source file name */
+{
+ if (npfile > 0)
+ return pfile[curpfile];
+ else
+ return NULL;
+}
+
diff --git a/bin/9base/awk/maketab.c b/bin/9base/awk/maketab.c
new file mode 100644
index 00000000..50908ce9
--- /dev/null
+++ b/bin/9base/awk/maketab.c
@@ -0,0 +1,169 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+/*
+ * this program makes the table to link function names
+ * and type indices that is used by execute() in run.c.
+ * it finds the indices in y.tab.h, produced by yacc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "y.tab.h"
+
+struct xx
+{ int token;
+ char *name;
+ char *pname;
+} proc[] = {
+ { PROGRAM, "program", NULL },
+ { BOR, "boolop", " || " },
+ { AND, "boolop", " && " },
+ { NOT, "boolop", " !" },
+ { NE, "relop", " != " },
+ { EQ, "relop", " == " },
+ { LE, "relop", " <= " },
+ { LT, "relop", " < " },
+ { GE, "relop", " >= " },
+ { GT, "relop", " > " },
+ { ARRAY, "array", NULL },
+ { INDIRECT, "indirect", "$(" },
+ { SUBSTR, "substr", "substr" },
+ { SUB, "sub", "sub" },
+ { GSUB, "gsub", "gsub" },
+ { INDEX, "sindex", "sindex" },
+ { SPRINTF, "awksprintf", "sprintf " },
+ { ADD, "arith", " + " },
+ { MINUS, "arith", " - " },
+ { MULT, "arith", " * " },
+ { DIVIDE, "arith", " / " },
+ { MOD, "arith", " % " },
+ { UMINUS, "arith", " -" },
+ { POWER, "arith", " **" },
+ { PREINCR, "incrdecr", "++" },
+ { POSTINCR, "incrdecr", "++" },
+ { PREDECR, "incrdecr", "--" },
+ { POSTDECR, "incrdecr", "--" },
+ { CAT, "cat", " " },
+ { PASTAT, "pastat", NULL },
+ { PASTAT2, "dopa2", NULL },
+ { MATCH, "matchop", " ~ " },
+ { NOTMATCH, "matchop", " !~ " },
+ { MATCHFCN, "matchop", "matchop" },
+ { INTEST, "intest", "intest" },
+ { PRINTF, "awkprintf", "printf" },
+ { PRINT, "printstat", "print" },
+ { CLOSE, "closefile", "closefile" },
+ { DELETE, "awkdelete", "awkdelete" },
+ { SPLIT, "split", "split" },
+ { ASSIGN, "assign", " = " },
+ { ADDEQ, "assign", " += " },
+ { SUBEQ, "assign", " -= " },
+ { MULTEQ, "assign", " *= " },
+ { DIVEQ, "assign", " /= " },
+ { MODEQ, "assign", " %= " },
+ { POWEQ, "assign", " ^= " },
+ { CONDEXPR, "condexpr", " ?: " },
+ { IF, "ifstat", "if(" },
+ { WHILE, "whilestat", "while(" },
+ { FOR, "forstat", "for(" },
+ { DO, "dostat", "do" },
+ { IN, "instat", "instat" },
+ { NEXT, "jump", "next" },
+ { NEXTFILE, "jump", "nextfile" },
+ { EXIT, "jump", "exit" },
+ { BREAK, "jump", "break" },
+ { CONTINUE, "jump", "continue" },
+ { RETURN, "jump", "ret" },
+ { BLTIN, "bltin", "bltin" },
+ { CALL, "call", "call" },
+ { ARG, "arg", "arg" },
+ { VARNF, "getnf", "NF" },
+ { GETLINE, "getline", "getline" },
+ { 0, "", "" },
+};
+
+#define SIZE (LASTTOKEN - FIRSTTOKEN + 1)
+char *table[SIZE];
+char *names[SIZE];
+
+int main(int argc, char *argv[])
+{
+ struct xx *p;
+ int i, n, tok;
+ char c;
+ FILE *fp;
+ char buf[200], name[200], def[200];
+
+ printf("#include <stdio.h>\n");
+ printf("#include \"awk.h\"\n");
+ printf("#include \"y.tab.h\"\n\n");
+ for (i = SIZE; --i >= 0; )
+ names[i] = "";
+
+ if ((fp = fopen("y.tab.h", "r")) == NULL) {
+ fprintf(stderr, "maketab can't open y.tab.h!\n");
+ exit(1);
+ }
+ printf("static char *printname[%d] = {\n", SIZE);
+ i = 0;
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok);
+ if (c != '#' || (n != 4 && strcmp(def,"define") != 0)) /* not a valid #define */
+ continue;
+ if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
+ fprintf(stderr, "maketab funny token %d %s\n", tok, buf);
+ exit(1);
+ }
+ names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1);
+ strcpy(names[tok-FIRSTTOKEN], name);
+ printf("\t(char *) \"%s\",\t/* %d */\n", name, tok);
+ i++;
+ }
+ printf("};\n\n");
+
+ for (p=proc; p->token!=0; p++)
+ table[p->token-FIRSTTOKEN] = p->name;
+ printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
+ for (i=0; i<SIZE; i++)
+ if (table[i]==0)
+ printf("\tnullproc,\t/* %s */\n", names[i]);
+ else
+ printf("\t%s,\t/* %s */\n", table[i], names[i]);
+ printf("};\n\n");
+
+ printf("char *tokname(int n)\n"); /* print a tokname() function */
+ printf("{\n");
+ printf(" static char buf[100];\n\n");
+ printf(" if (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
+ printf(" sprintf(buf, \"token %%d\", n);\n");
+ printf(" return buf;\n");
+ printf(" }\n");
+ printf(" return printname[n-FIRSTTOKEN];\n");
+ printf("}\n");
+ return 0;
+}
+
diff --git a/bin/9base/awk/parse.c b/bin/9base/awk/parse.c
new file mode 100644
index 00000000..d4c88324
--- /dev/null
+++ b/bin/9base/awk/parse.c
@@ -0,0 +1,272 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "y.tab.h"
+
+Node *nodealloc(int n)
+{
+ Node *x;
+
+ x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *));
+ if (x == NULL)
+ FATAL("out of space in nodealloc");
+ x->nnext = NULL;
+ x->lineno = lineno;
+ return(x);
+}
+
+Node *exptostat(Node *a)
+{
+ a->ntype = NSTAT;
+ return(a);
+}
+
+Node *node1(int a, Node *b)
+{
+ Node *x;
+
+ x = nodealloc(1);
+ x->nobj = a;
+ x->narg[0]=b;
+ return(x);
+}
+
+Node *node2(int a, Node *b, Node *c)
+{
+ Node *x;
+
+ x = nodealloc(2);
+ x->nobj = a;
+ x->narg[0] = b;
+ x->narg[1] = c;
+ return(x);
+}
+
+Node *node3(int a, Node *b, Node *c, Node *d)
+{
+ Node *x;
+
+ x = nodealloc(3);
+ x->nobj = a;
+ x->narg[0] = b;
+ x->narg[1] = c;
+ x->narg[2] = d;
+ return(x);
+}
+
+Node *node4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+ Node *x;
+
+ x = nodealloc(4);
+ x->nobj = a;
+ x->narg[0] = b;
+ x->narg[1] = c;
+ x->narg[2] = d;
+ x->narg[3] = e;
+ return(x);
+}
+
+Node *stat1(int a, Node *b)
+{
+ Node *x;
+
+ x = node1(a,b);
+ x->ntype = NSTAT;
+ return(x);
+}
+
+Node *stat2(int a, Node *b, Node *c)
+{
+ Node *x;
+
+ x = node2(a,b,c);
+ x->ntype = NSTAT;
+ return(x);
+}
+
+Node *stat3(int a, Node *b, Node *c, Node *d)
+{
+ Node *x;
+
+ x = node3(a,b,c,d);
+ x->ntype = NSTAT;
+ return(x);
+}
+
+Node *stat4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+ Node *x;
+
+ x = node4(a,b,c,d,e);
+ x->ntype = NSTAT;
+ return(x);
+}
+
+Node *op1(int a, Node *b)
+{
+ Node *x;
+
+ x = node1(a,b);
+ x->ntype = NEXPR;
+ return(x);
+}
+
+Node *op2(int a, Node *b, Node *c)
+{
+ Node *x;
+
+ x = node2(a,b,c);
+ x->ntype = NEXPR;
+ return(x);
+}
+
+Node *op3(int a, Node *b, Node *c, Node *d)
+{
+ Node *x;
+
+ x = node3(a,b,c,d);
+ x->ntype = NEXPR;
+ return(x);
+}
+
+Node *op4(int a, Node *b, Node *c, Node *d, Node *e)
+{
+ Node *x;
+
+ x = node4(a,b,c,d,e);
+ x->ntype = NEXPR;
+ return(x);
+}
+
+Node *celltonode(Cell *a, int b)
+{
+ Node *x;
+
+ a->ctype = OCELL;
+ a->csub = b;
+ x = node1(0, (Node *) a);
+ x->ntype = NVALUE;
+ return(x);
+}
+
+Node *rectonode(void) /* make $0 into a Node */
+{
+ extern Cell *literal0;
+ return op1(INDIRECT, celltonode(literal0, CUNK));
+}
+
+Node *makearr(Node *p)
+{
+ Cell *cp;
+
+ if (isvalue(p)) {
+ cp = (Cell *) (p->narg[0]);
+ if (isfcn(cp))
+ SYNTAX( "%s is a function, not an array", cp->nval );
+ else if (!isarr(cp)) {
+ xfree(cp->sval);
+ cp->sval = (char *) makesymtab(NSYMTAB);
+ cp->tval = ARR;
+ }
+ }
+ return p;
+}
+
+#define PA2NUM 50 /* max number of pat,pat patterns allowed */
+int paircnt; /* number of them in use */
+int pairstack[PA2NUM]; /* state of each pat,pat */
+
+Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */
+{
+ Node *x;
+
+ x = node4(PASTAT2, a, b, c, itonp(paircnt));
+ if (paircnt++ >= PA2NUM)
+ SYNTAX( "limited to %d pat,pat statements", PA2NUM );
+ x->ntype = NSTAT;
+ return(x);
+}
+
+Node *linkum(Node *a, Node *b)
+{
+ Node *c;
+
+ if (errorflag) /* don't link things that are wrong */
+ return a;
+ if (a == NULL)
+ return(b);
+ else if (b == NULL)
+ return(a);
+ for (c = a; c->nnext != NULL; c = c->nnext)
+ ;
+ c->nnext = b;
+ return(a);
+}
+
+void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */
+{ /* body of function, arglist */
+ Node *p;
+ int n;
+
+ if (isarr(v)) {
+ SYNTAX( "`%s' is an array name and a function name", v->nval );
+ return;
+ }
+ v->tval = FCN;
+ v->sval = (char *) st;
+ n = 0; /* count arguments */
+ for (p = vl; p; p = p->nnext)
+ n++;
+ v->fval = n;
+ dprintf( ("defining func %s (%d args)\n", v->nval, n) );
+}
+
+int isarg(char *s) /* is s in argument list for current function? */
+{ /* return -1 if not, otherwise arg # */
+ extern Node *arglist;
+ Node *p = arglist;
+ int n;
+
+ for (n = 0; p != 0; p = p->nnext, n++)
+ if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
+ return n;
+ return -1;
+}
+
+int ptoi(void *p) /* convert pointer to integer */
+{
+ return (int) (long) p; /* swearing that p fits, of course */
+}
+
+Node *itonp(int i) /* and vice versa */
+{
+ return (Node *) (long) i;
+}
+
diff --git a/bin/9base/awk/proctab.c b/bin/9base/awk/proctab.c
new file mode 100644
index 00000000..46001757
--- /dev/null
+++ b/bin/9base/awk/proctab.c
@@ -0,0 +1,206 @@
+#include <stdio.h>
+#include <math.h>
+#include "awk.h"
+#include "y.tab.h"
+
+static char *printname[92] = {
+ (char *) "FIRSTTOKEN", /* 57346 */
+ (char *) "PROGRAM", /* 57347 */
+ (char *) "PASTAT", /* 57348 */
+ (char *) "PASTAT2", /* 57349 */
+ (char *) "XBEGIN", /* 57350 */
+ (char *) "XEND", /* 57351 */
+ (char *) "NL", /* 57352 */
+ (char *) "ARRAY", /* 57353 */
+ (char *) "MATCH", /* 57354 */
+ (char *) "NOTMATCH", /* 57355 */
+ (char *) "MATCHOP", /* 57356 */
+ (char *) "FINAL", /* 57357 */
+ (char *) "DOT", /* 57358 */
+ (char *) "ALL", /* 57359 */
+ (char *) "CCL", /* 57360 */
+ (char *) "NCCL", /* 57361 */
+ (char *) "CHAR", /* 57362 */
+ (char *) "OR", /* 57363 */
+ (char *) "STAR", /* 57364 */
+ (char *) "QUEST", /* 57365 */
+ (char *) "PLUS", /* 57366 */
+ (char *) "AND", /* 57367 */
+ (char *) "BOR", /* 57368 */
+ (char *) "APPEND", /* 57369 */
+ (char *) "EQ", /* 57370 */
+ (char *) "GE", /* 57371 */
+ (char *) "GT", /* 57372 */
+ (char *) "LE", /* 57373 */
+ (char *) "LT", /* 57374 */
+ (char *) "NE", /* 57375 */
+ (char *) "IN", /* 57376 */
+ (char *) "ARG", /* 57377 */
+ (char *) "BLTIN", /* 57378 */
+ (char *) "BREAK", /* 57379 */
+ (char *) "CLOSE", /* 57380 */
+ (char *) "CONTINUE", /* 57381 */
+ (char *) "DELETE", /* 57382 */
+ (char *) "DO", /* 57383 */
+ (char *) "EXIT", /* 57384 */
+ (char *) "FOR", /* 57385 */
+ (char *) "FUNC", /* 57386 */
+ (char *) "SUB", /* 57387 */
+ (char *) "GSUB", /* 57388 */
+ (char *) "IF", /* 57389 */
+ (char *) "INDEX", /* 57390 */
+ (char *) "LSUBSTR", /* 57391 */
+ (char *) "MATCHFCN", /* 57392 */
+ (char *) "NEXT", /* 57393 */
+ (char *) "NEXTFILE", /* 57394 */
+ (char *) "ADD", /* 57395 */
+ (char *) "MINUS", /* 57396 */
+ (char *) "MULT", /* 57397 */
+ (char *) "DIVIDE", /* 57398 */
+ (char *) "MOD", /* 57399 */
+ (char *) "ASSIGN", /* 57400 */
+ (char *) "ASGNOP", /* 57401 */
+ (char *) "ADDEQ", /* 57402 */
+ (char *) "SUBEQ", /* 57403 */
+ (char *) "MULTEQ", /* 57404 */
+ (char *) "DIVEQ", /* 57405 */
+ (char *) "MODEQ", /* 57406 */
+ (char *) "POWEQ", /* 57407 */
+ (char *) "PRINT", /* 57408 */
+ (char *) "PRINTF", /* 57409 */
+ (char *) "SPRINTF", /* 57410 */
+ (char *) "ELSE", /* 57411 */
+ (char *) "INTEST", /* 57412 */
+ (char *) "CONDEXPR", /* 57413 */
+ (char *) "POSTINCR", /* 57414 */
+ (char *) "PREINCR", /* 57415 */
+ (char *) "POSTDECR", /* 57416 */
+ (char *) "PREDECR", /* 57417 */
+ (char *) "VAR", /* 57418 */
+ (char *) "IVAR", /* 57419 */
+ (char *) "VARNF", /* 57420 */
+ (char *) "CALL", /* 57421 */
+ (char *) "NUMBER", /* 57422 */
+ (char *) "STRING", /* 57423 */
+ (char *) "REGEXPR", /* 57424 */
+ (char *) "GETLINE", /* 57425 */
+ (char *) "RETURN", /* 57426 */
+ (char *) "SPLIT", /* 57427 */
+ (char *) "SUBSTR", /* 57428 */
+ (char *) "WHILE", /* 57429 */
+ (char *) "CAT", /* 57430 */
+ (char *) "NOT", /* 57431 */
+ (char *) "UMINUS", /* 57432 */
+ (char *) "POWER", /* 57433 */
+ (char *) "DECR", /* 57434 */
+ (char *) "INCR", /* 57435 */
+ (char *) "INDIRECT", /* 57436 */
+ (char *) "LASTTOKEN", /* 57437 */
+};
+
+
+Cell *(*proctab[92])(Node **, int) = {
+ nullproc, /* FIRSTTOKEN */
+ program, /* PROGRAM */
+ pastat, /* PASTAT */
+ dopa2, /* PASTAT2 */
+ nullproc, /* XBEGIN */
+ nullproc, /* XEND */
+ nullproc, /* NL */
+ array, /* ARRAY */
+ matchop, /* MATCH */
+ matchop, /* NOTMATCH */
+ nullproc, /* MATCHOP */
+ nullproc, /* FINAL */
+ nullproc, /* DOT */
+ nullproc, /* ALL */
+ nullproc, /* CCL */
+ nullproc, /* NCCL */
+ nullproc, /* CHAR */
+ nullproc, /* OR */
+ nullproc, /* STAR */
+ nullproc, /* QUEST */
+ nullproc, /* PLUS */
+ boolop, /* AND */
+ boolop, /* BOR */
+ nullproc, /* APPEND */
+ relop, /* EQ */
+ relop, /* GE */
+ relop, /* GT */
+ relop, /* LE */
+ relop, /* LT */
+ relop, /* NE */
+ instat, /* IN */
+ arg, /* ARG */
+ bltin, /* BLTIN */
+ jump, /* BREAK */
+ closefile, /* CLOSE */
+ jump, /* CONTINUE */
+ awkdelete, /* DELETE */
+ dostat, /* DO */
+ jump, /* EXIT */
+ forstat, /* FOR */
+ nullproc, /* FUNC */
+ sub, /* SUB */
+ gsub, /* GSUB */
+ ifstat, /* IF */
+ sindex, /* INDEX */
+ nullproc, /* LSUBSTR */
+ matchop, /* MATCHFCN */
+ jump, /* NEXT */
+ jump, /* NEXTFILE */
+ arith, /* ADD */
+ arith, /* MINUS */
+ arith, /* MULT */
+ arith, /* DIVIDE */
+ arith, /* MOD */
+ assign, /* ASSIGN */
+ nullproc, /* ASGNOP */
+ assign, /* ADDEQ */
+ assign, /* SUBEQ */
+ assign, /* MULTEQ */
+ assign, /* DIVEQ */
+ assign, /* MODEQ */
+ assign, /* POWEQ */
+ printstat, /* PRINT */
+ awkprintf, /* PRINTF */
+ awksprintf, /* SPRINTF */
+ nullproc, /* ELSE */
+ intest, /* INTEST */
+ condexpr, /* CONDEXPR */
+ incrdecr, /* POSTINCR */
+ incrdecr, /* PREINCR */
+ incrdecr, /* POSTDECR */
+ incrdecr, /* PREDECR */
+ nullproc, /* VAR */
+ nullproc, /* IVAR */
+ getnf, /* VARNF */
+ call, /* CALL */
+ nullproc, /* NUMBER */
+ nullproc, /* STRING */
+ nullproc, /* REGEXPR */
+ getline, /* GETLINE */
+ jump, /* RETURN */
+ split, /* SPLIT */
+ substr, /* SUBSTR */
+ whilestat, /* WHILE */
+ cat, /* CAT */
+ boolop, /* NOT */
+ arith, /* UMINUS */
+ arith, /* POWER */
+ nullproc, /* DECR */
+ nullproc, /* INCR */
+ indirect, /* INDIRECT */
+ nullproc, /* LASTTOKEN */
+};
+
+char *tokname(int n)
+{
+ static char buf[100];
+
+ if (n < FIRSTTOKEN || n > LASTTOKEN) {
+ sprintf(buf, "token %d", n);
+ return buf;
+ }
+ return printname[n-FIRSTTOKEN];
+}
diff --git a/bin/9base/awk/proto.h b/bin/9base/awk/proto.h
new file mode 100644
index 00000000..f124f4e4
--- /dev/null
+++ b/bin/9base/awk/proto.h
@@ -0,0 +1,180 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define getline p9getline
+
+extern int yywrap(void);
+extern void setfname(Cell *);
+extern int constnode(Node *);
+extern char *strnode(Node *);
+extern Node *notnull(Node *);
+extern int yyparse(void);
+
+extern int yylex(void);
+extern void startreg(void);
+extern int input(void);
+extern void unput(int);
+extern void unputstr(char *);
+extern int yylook(void);
+extern int yyback(int *, int);
+extern int yyinput(void);
+
+extern void *compre(char *);
+extern int hexstr(char **);
+extern void quoted(char **, char **, char *);
+extern int match(void *, char *, char *);
+extern int pmatch(void *, char *, char *);
+extern int nematch(void *, char *, char *);
+extern int countposn(char *, int);
+extern void overflow(void);
+
+extern int pgetc(void);
+extern char *cursource(void);
+
+extern Node *nodealloc(int);
+extern Node *exptostat(Node *);
+extern Node *node1(int, Node *);
+extern Node *node2(int, Node *, Node *);
+extern Node *node3(int, Node *, Node *, Node *);
+extern Node *node4(int, Node *, Node *, Node *, Node *);
+extern Node *stat3(int, Node *, Node *, Node *);
+extern Node *op2(int, Node *, Node *);
+extern Node *op1(int, Node *);
+extern Node *stat1(int, Node *);
+extern Node *op3(int, Node *, Node *, Node *);
+extern Node *op4(int, Node *, Node *, Node *, Node *);
+extern Node *stat2(int, Node *, Node *);
+extern Node *stat4(int, Node *, Node *, Node *, Node *);
+extern Node *celltonode(Cell *, int);
+extern Node *rectonode(void);
+extern Node *makearr(Node *);
+extern Node *pa2stat(Node *, Node *, Node *);
+extern Node *linkum(Node *, Node *);
+extern void defn(Cell *, Node *, Node *);
+extern int isarg(char *);
+extern char *tokname(int);
+extern Cell *(*proctab[])(Node **, int);
+extern int ptoi(void *);
+extern Node *itonp(int);
+
+extern void syminit(void);
+extern void arginit(int, char **);
+extern void envinit(char **);
+extern Array *makesymtab(int);
+extern void freesymtab(Cell *);
+extern void freeelem(Cell *, char *);
+extern Cell *setsymtab(char *, char *, double, unsigned int, Array *);
+extern int hash(char *, int);
+extern void rehash(Array *);
+extern Cell *lookup(char *, Array *);
+extern double setfval(Cell *, double);
+extern void funnyvar(Cell *, char *);
+extern char *setsval(Cell *, char *);
+extern double getfval(Cell *);
+extern char *getsval(Cell *);
+extern char *tostring(char *);
+extern char *qstring(char *, int);
+
+extern void recinit(unsigned int);
+extern void initgetrec(void);
+extern void makefields(int, int);
+extern void growfldtab(int n);
+extern int getrec(char **, int *, int);
+extern void nextfile(void);
+extern int readrec(char **buf, int *bufsize, FILE *inf);
+extern char *getargv(int);
+extern void setclvar(char *);
+extern void fldbld(void);
+extern void cleanfld(int, int);
+extern void newfld(int);
+extern int refldbld(char *, char *);
+extern void recbld(void);
+extern Cell *fieldadr(int);
+extern void yyerror(char *);
+extern void fpecatch(int);
+extern void bracecheck(void);
+extern void bcheck2(int, int, int);
+extern void SYNTAX(char *, ...);
+extern void FATAL(char *, ...);
+extern void WARNING(char *, ...);
+extern void error(void);
+extern void eprint(void);
+extern void bclass(int);
+extern double errcheck(double, char *);
+extern int isclvar(char *);
+extern int is_number(char *);
+
+extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, char *what);
+extern void run(Node *);
+extern Cell *execute(Node *);
+extern Cell *program(Node **, int);
+extern Cell *call(Node **, int);
+extern Cell *copycell(Cell *);
+extern Cell *arg(Node **, int);
+extern Cell *jump(Node **, int);
+extern Cell *getline(Node **, int);
+extern Cell *getnf(Node **, int);
+extern Cell *array(Node **, int);
+extern Cell *awkdelete(Node **, int);
+extern Cell *intest(Node **, int);
+extern Cell *matchop(Node **, int);
+extern Cell *boolop(Node **, int);
+extern Cell *relop(Node **, int);
+extern void tfree(Cell *);
+extern Cell *gettemp(void);
+extern Cell *field(Node **, int);
+extern Cell *indirect(Node **, int);
+extern Cell *substr(Node **, int);
+extern Cell *sindex(Node **, int);
+extern int format(char **, int *, char *, Node *);
+extern Cell *awksprintf(Node **, int);
+extern Cell *awkprintf(Node **, int);
+extern Cell *arith(Node **, int);
+extern double ipow(double, int);
+extern Cell *incrdecr(Node **, int);
+extern Cell *assign(Node **, int);
+extern Cell *cat(Node **, int);
+extern Cell *pastat(Node **, int);
+extern Cell *dopa2(Node **, int);
+extern Cell *split(Node **, int);
+extern Cell *condexpr(Node **, int);
+extern Cell *ifstat(Node **, int);
+extern Cell *whilestat(Node **, int);
+extern Cell *dostat(Node **, int);
+extern Cell *forstat(Node **, int);
+extern Cell *instat(Node **, int);
+extern Cell *bltin(Node **, int);
+extern Cell *printstat(Node **, int);
+extern Cell *nullproc(Node **, int);
+extern FILE *redirect(int, Node *);
+extern FILE *openfile(int, char *);
+extern char *filename(FILE *);
+extern Cell *closefile(Node **, int);
+extern void closeall(void);
+extern Cell *sub(Node **, int);
+extern Cell *gsub(Node **, int);
+
+extern FILE *popen(const char *, const char *);
+extern int pclose(FILE *);
+
diff --git a/bin/9base/awk/re.c b/bin/9base/awk/re.c
new file mode 100644
index 00000000..a15d2f4d
--- /dev/null
+++ b/bin/9base/awk/re.c
@@ -0,0 +1,325 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+
+#define DEBUG
+#include <stdio.h>
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <regexp.h>
+#include "awk.h"
+#include "y.tab.h"
+
+ /* This file provides the interface between the main body of
+ * awk and the pattern matching package. It preprocesses
+ * patterns prior to compilation to provide awk-like semantics
+ * to character sequences not supported by the pattern package.
+ * The following conversions are performed:
+ *
+ * "()" -> "[]"
+ * "[-" -> "[\-"
+ * "[^-" -> "[^\-"
+ * "-]" -> "\-]"
+ * "[]" -> "[]*"
+ * "\xdddd" -> "\z" where 'z' is the UTF sequence
+ * for the hex value
+ * "\ddd" -> "\o" where 'o' is a char octal value
+ * "\b" -> "\B" where 'B' is backspace
+ * "\t" -> "\T" where 'T' is tab
+ * "\f" -> "\F" where 'F' is form feed
+ * "\n" -> "\N" where 'N' is newline
+ * "\r" -> "\r" where 'C' is cr
+ */
+
+#define MAXRE 512
+
+static char re[MAXRE]; /* copy buffer */
+
+char *patbeg;
+int patlen; /* number of chars in pattern */
+
+#define NPATS 20 /* number of slots in pattern cache */
+
+static struct pat_list /* dynamic pattern cache */
+{
+ char *re;
+ int use;
+ Reprog *program;
+} pattern[NPATS];
+
+static int npats; /* cache fill level */
+
+ /* Compile a pattern */
+void
+*compre(char *pat)
+{
+ int i, j, inclass;
+ char c, *p, *s;
+ Reprog *program;
+
+ if (!compile_time) { /* search cache for dynamic pattern */
+ for (i = 0; i < npats; i++)
+ if (!strcmp(pat, pattern[i].re)) {
+ pattern[i].use++;
+ return((void *) pattern[i].program);
+ }
+ }
+ /* Preprocess Pattern for compilation */
+ p = re;
+ s = pat;
+ inclass = 0;
+ while (c = *s++) {
+ if (c == '\\') {
+ quoted(&s, &p, re+MAXRE);
+ continue;
+ }
+ else if (!inclass && c == '(' && *s == ')') {
+ if (p < re+MAXRE-2) { /* '()' -> '[]*' */
+ *p++ = '[';
+ *p++ = ']';
+ c = '*';
+ s++;
+ }
+ else overflow();
+ }
+ else if (c == '['){ /* '[-' -> '[\-' */
+ inclass = 1;
+ if (*s == '-') {
+ if (p < re+MAXRE-2) {
+ *p++ = '[';
+ *p++ = '\\';
+ c = *s++;
+ }
+ else overflow();
+ } /* '[^-' -> '[^\-'*/
+ else if (*s == '^' && s[1] == '-'){
+ if (p < re+MAXRE-3) {
+ *p++ = '[';
+ *p++ = *s++;
+ *p++ = '\\';
+ c = *s++;
+ }
+ else overflow();
+ }
+ else if (*s == '['){ /* skip '[[' */
+ if (p < re+MAXRE-1)
+ *p++ = c;
+ else overflow();
+ c = *s++;
+ }
+ else if (*s == '^' && s[1] == '[') { /* skip '[^['*/
+ if (p < re+MAXRE-2) {
+ *p++ = c;
+ *p++ = *s++;
+ c = *s++;
+ }
+ else overflow();
+ }
+ else if (*s == ']') { /* '[]' -> '[]*' */
+ if (p < re+MAXRE-2) {
+ *p++ = c;
+ *p++ = *s++;
+ c = '*';
+ inclass = 0;
+ }
+ else overflow();
+ }
+ }
+ else if (c == '-' && *s == ']') { /* '-]' -> '\-]' */
+ if (p < re+MAXRE-1)
+ *p++ = '\\';
+ else overflow();
+ }
+ else if (c == ']')
+ inclass = 0;
+ if (p < re+MAXRE-1)
+ *p++ = c;
+ else overflow();
+ }
+ *p = 0;
+ program = regcomp(re); /* compile pattern */
+ if (!compile_time) {
+ if (npats < NPATS) /* Room in cache */
+ i = npats++;
+ else { /* Throw out least used */
+ int use = pattern[0].use;
+ i = 0;
+ for (j = 1; j < NPATS; j++) {
+ if (pattern[j].use < use) {
+ use = pattern[j].use;
+ i = j;
+ }
+ }
+ xfree(pattern[i].program);
+ xfree(pattern[i].re);
+ }
+ pattern[i].re = tostring(pat);
+ pattern[i].program = program;
+ pattern[i].use = 1;
+ }
+ return((void *) program);
+}
+
+ /* T/F match indication - matched string not exported */
+int
+match(void *p, char *s, char *start)
+{
+ return regexec((Reprog *) p, (char *) s, 0, 0);
+}
+
+ /* match and delimit the matched string */
+int
+pmatch(void *p, char *s, char *start)
+{
+ Resub m;
+
+ m.s.sp = start;
+ m.e.ep = 0;
+ if (regexec((Reprog *) p, (char *) s, &m, 1)) {
+ patbeg = m.s.sp;
+ patlen = m.e.ep-m.s.sp;
+ return 1;
+ }
+ patlen = -1;
+ patbeg = start;
+ return 0;
+}
+
+ /* perform a non-empty match */
+int
+nematch(void *p, char *s, char *start)
+{
+ if (pmatch(p, s, start) == 1 && patlen > 0)
+ return 1;
+ patlen = -1;
+ patbeg = start;
+ return 0;
+}
+/* in the parsing of regular expressions, metacharacters like . have */
+/* to be seen literally; \056 is not a metacharacter. */
+
+int
+hexstr(char **pp) /* find and eval hex string at pp, return new p */
+{
+ char c;
+ int n = 0;
+ int i;
+
+ for (i = 0, c = (*pp)[i]; i < 4 && isxdigit(c); i++, c = (*pp)[i]) {
+ if (isdigit(c))
+ n = 16 * n + c - '0';
+ else if ('a' <= c && c <= 'f')
+ n = 16 * n + c - 'a' + 10;
+ else if ('A' <= c && c <= 'F')
+ n = 16 * n + c - 'A' + 10;
+ }
+ *pp += i;
+ return n;
+}
+
+ /* look for awk-specific escape sequences */
+
+#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */
+
+void
+quoted(char **s, char **to, char *end) /* handle escaped sequence */
+{
+ char *p = *s;
+ char *t = *to;
+ wchar_t c;
+
+ switch(c = *p++) {
+ case 't':
+ c = '\t';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ default:
+ if (t < end-1) /* all else must be escaped */
+ *t++ = '\\';
+ if (c == 'x') { /* hexadecimal goo follows */
+ c = hexstr(&p);
+ if (t < end-MB_CUR_MAX)
+ t += wctomb(t, c);
+ else overflow();
+ *to = t;
+ *s = p;
+ return;
+ } else if (isoctdigit(c)) { /* \d \dd \ddd */
+ c -= '0';
+ if (isoctdigit(*p)) {
+ c = 8 * c + *p++ - '0';
+ if (isoctdigit(*p))
+ c = 8 * c + *p++ - '0';
+ }
+ }
+ break;
+ }
+ if (t < end-1)
+ *t++ = c;
+ *s = p;
+ *to = t;
+}
+ /* count rune positions */
+int
+countposn(char *s, int n)
+{
+ int i, j;
+ char *end;
+
+ for (i = 0, end = s+n; *s && s < end; i++){
+ j = mblen(s, n);
+ if(j <= 0)
+ j = 1;
+ s += j;
+ }
+ return(i);
+}
+
+ /* pattern package error handler */
+
+void
+regerror(char *s)
+{
+ FATAL("%s", s);
+}
+
+void
+overflow(void)
+{
+ FATAL("%s", "regular expression too big");
+}
+
diff --git a/bin/9base/awk/run.c b/bin/9base/awk/run.c
new file mode 100644
index 00000000..b99ef58a
--- /dev/null
+++ b/bin/9base/awk/run.c
@@ -0,0 +1,1899 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "awk.h"
+#include "y.tab.h"
+
+#define tempfree(x) if (istemp(x)) tfree(x); else
+
+/*
+#undef tempfree
+
+void tempfree(Cell *p) {
+ if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
+ WARNING("bad csub %d in Cell %d %s",
+ p->csub, p->ctype, p->sval);
+ }
+ if (istemp(p))
+ tfree(p);
+}
+*/
+
+#ifdef _NFILE
+#ifndef FOPEN_MAX
+#define FOPEN_MAX _NFILE
+#endif
+#endif
+
+#ifndef FOPEN_MAX
+#define FOPEN_MAX 40 /* max number of open files */
+#endif
+
+#ifndef RAND_MAX
+#define RAND_MAX 32767 /* all that ansi guarantees */
+#endif
+
+jmp_buf env;
+extern int pairstack[];
+
+Node *winner = NULL; /* root of parse tree */
+Cell *tmps; /* free temporary cells for execution */
+
+static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM };
+Cell *True = &truecell;
+static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM };
+Cell *False = &falsecell;
+static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM };
+Cell *jbreak = &breakcell;
+static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM };
+Cell *jcont = &contcell;
+static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM };
+Cell *jnext = &nextcell;
+static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM };
+Cell *jnextfile = &nextfilecell;
+static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM };
+Cell *jexit = &exitcell;
+static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM };
+Cell *jret = &retcell;
+static Cell tempcell ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE };
+
+Node *curnode = NULL; /* the node being executed, for debugging */
+
+/* buffer memory management */
+int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
+ char *whatrtn)
+/* pbuf: address of pointer to buffer being managed
+ * psiz: address of buffer size variable
+ * minlen: minimum length of buffer needed
+ * quantum: buffer size quantum
+ * pbptr: address of movable pointer into buffer, or 0 if none
+ * whatrtn: name of the calling routine if failure should cause fatal error
+ *
+ * return 0 for realloc failure, !=0 for success
+ */
+{
+ if (minlen > *psiz) {
+ char *tbuf;
+ int rminlen = quantum ? minlen % quantum : 0;
+ int boff = pbptr ? *pbptr - *pbuf : 0;
+ /* round up to next multiple of quantum */
+ if (rminlen)
+ minlen += quantum - rminlen;
+ tbuf = (char *) realloc(*pbuf, minlen);
+ if (tbuf == NULL) {
+ if (whatrtn)
+ FATAL("out of memory in %s", whatrtn);
+ return 0;
+ }
+ *pbuf = tbuf;
+ *psiz = minlen;
+ if (pbptr)
+ *pbptr = tbuf + boff;
+ }
+ return 1;
+}
+
+void run(Node *a) /* execution of parse tree starts here */
+{
+ extern void stdinit(void);
+
+ stdinit();
+ execute(a);
+ closeall();
+}
+
+Cell *execute(Node *u) /* execute a node of the parse tree */
+{
+ int nobj;
+ Cell *(*proc)(Node **, int);
+ Cell *x;
+ Node *a;
+
+ if (u == NULL)
+ return(True);
+ for (a = u; ; a = a->nnext) {
+ curnode = a;
+ if (isvalue(a)) {
+ x = (Cell *) (a->narg[0]);
+ if (isfld(x) && !donefld)
+ fldbld();
+ else if (isrec(x) && !donerec)
+ recbld();
+ return(x);
+ }
+ nobj = a->nobj;
+ if (notlegal(nobj)) /* probably a Cell* but too risky to print */
+ FATAL("illegal statement");
+ proc = proctab[nobj-FIRSTTOKEN];
+ x = (*proc)(a->narg, nobj);
+ if (isfld(x) && !donefld)
+ fldbld();
+ else if (isrec(x) && !donerec)
+ recbld();
+ if (isexpr(a))
+ return(x);
+ if (isjump(x))
+ return(x);
+ if (a->nnext == NULL)
+ return(x);
+ tempfree(x);
+ }
+}
+
+
+Cell *program(Node **a, int n) /* execute an awk program */
+{ /* a[0] = BEGIN, a[1] = body, a[2] = END */
+ Cell *x;
+
+ if (setjmp(env) != 0)
+ goto ex;
+ if (a[0]) { /* BEGIN */
+ x = execute(a[0]);
+ if (isexit(x))
+ return(True);
+ if (isjump(x))
+ FATAL("illegal break, continue, next or nextfile from BEGIN");
+ tempfree(x);
+ }
+ if (a[1] || a[2])
+ while (getrec(&record, &recsize, 1) > 0) {
+ x = execute(a[1]);
+ if (isexit(x))
+ break;
+ tempfree(x);
+ }
+ ex:
+ if (setjmp(env) != 0) /* handles exit within END */
+ goto ex1;
+ if (a[2]) { /* END */
+ x = execute(a[2]);
+ if (isbreak(x) || isnext(x) || iscont(x))
+ FATAL("illegal break, continue, next or nextfile from END");
+ tempfree(x);
+ }
+ ex1:
+ return(True);
+}
+
+struct Frame { /* stack frame for awk function calls */
+ int nargs; /* number of arguments in this call */
+ Cell *fcncell; /* pointer to Cell for function */
+ Cell **args; /* pointer to array of arguments after execute */
+ Cell *retval; /* return value */
+};
+
+#define NARGS 50 /* max args in a call */
+
+struct Frame *frame = NULL; /* base of stack frames; dynamically allocated */
+int nframe = 0; /* number of frames allocated */
+struct Frame *fp = NULL; /* frame pointer. bottom level unused */
+
+Cell *call(Node **a, int n) /* function call. very kludgy and fragile */
+{
+ static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE };
+ int i, ncall, ndef;
+ Node *x;
+ Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */
+ Cell *y, *z, *fcn;
+ char *s;
+
+ fcn = execute(a[0]); /* the function itself */
+ s = fcn->nval;
+ if (!isfcn(fcn))
+ FATAL("calling undefined function %s", s);
+ if (frame == NULL) {
+ fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame));
+ if (frame == NULL)
+ FATAL("out of space for stack frames calling %s", s);
+ }
+ for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */
+ ncall++;
+ ndef = (int) fcn->fval; /* args in defn */
+ dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) );
+ if (ncall > ndef)
+ WARNING("function %s called with %d args, uses only %d",
+ s, ncall, ndef);
+ if (ncall + ndef > NARGS)
+ FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
+ for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) { /* get call args */
+ dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) );
+ y = execute(x);
+ oargs[i] = y;
+ dprintf( ("args[%d]: %s %f <%s>, t=%o\n",
+ i, y->nval, y->fval, isarr(y) ? "(array)" : y->sval, y->tval) );
+ if (isfcn(y))
+ FATAL("can't use function %s as argument in %s", y->nval, s);
+ if (isarr(y))
+ args[i] = y; /* arrays by ref */
+ else
+ args[i] = copycell(y);
+ tempfree(y);
+ }
+ for ( ; i < ndef; i++) { /* add null args for ones not provided */
+ args[i] = gettemp();
+ *args[i] = newcopycell;
+ }
+ fp++; /* now ok to up frame */
+ if (fp >= frame + nframe) {
+ int dfp = fp - frame; /* old index */
+ frame = (struct Frame *)
+ realloc((char *) frame, (nframe += 100) * sizeof(struct Frame));
+ if (frame == NULL)
+ FATAL("out of space for stack frames in %s", s);
+ fp = frame + dfp;
+ }
+ fp->fcncell = fcn;
+ fp->args = args;
+ fp->nargs = ndef; /* number defined with (excess are locals) */
+ fp->retval = gettemp();
+
+ dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+ y = execute((Node *)(fcn->sval)); /* execute body */
+ dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+
+ for (i = 0; i < ndef; i++) {
+ Cell *t = fp->args[i];
+ if (isarr(t)) {
+ if (t->csub == CCOPY) {
+ if (i >= ncall) {
+ freesymtab(t);
+ t->csub = CTEMP;
+ tempfree(t);
+ } else {
+ oargs[i]->tval = t->tval;
+ oargs[i]->tval &= ~(STR|NUM|DONTFREE);
+ oargs[i]->sval = t->sval;
+ tempfree(t);
+ }
+ }
+ } else if (t != y) { /* kludge to prevent freeing twice */
+ t->csub = CTEMP;
+ tempfree(t);
+ }
+ }
+ tempfree(fcn);
+ if (isexit(y) || isnext(y) || isnextfile(y))
+ return y;
+ tempfree(y); /* this can free twice! */
+ z = fp->retval; /* return value */
+ dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) );
+ fp--;
+ return(z);
+}
+
+Cell *copycell(Cell *x) /* make a copy of a cell in a temp */
+{
+ Cell *y;
+
+ y = gettemp();
+ y->csub = CCOPY; /* prevents freeing until call is over */
+ y->nval = x->nval; /* BUG? */
+ y->sval = x->sval ? tostring(x->sval) : NULL;
+ y->fval = x->fval;
+ y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */
+ /* is DONTFREE right? */
+ return y;
+}
+
+Cell *arg(Node **a, int n) /* nth argument of a function */
+{
+
+ n = ptoi(a[0]); /* argument number, counting from 0 */
+ dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) );
+ if (n+1 > fp->nargs)
+ FATAL("argument #%d of function %s was not supplied",
+ n+1, fp->fcncell->nval);
+ return fp->args[n];
+}
+
+Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */
+{
+ Cell *y;
+
+ switch (n) {
+ case EXIT:
+ if (a[0] != NULL) {
+ y = execute(a[0]);
+ errorflag = (int) getfval(y);
+ tempfree(y);
+ }
+ longjmp(env, 1);
+ case RETURN:
+ if (a[0] != NULL) {
+ y = execute(a[0]);
+ if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+ setsval(fp->retval, getsval(y));
+ fp->retval->fval = getfval(y);
+ fp->retval->tval |= NUM;
+ }
+ else if (y->tval & STR)
+ setsval(fp->retval, getsval(y));
+ else if (y->tval & NUM)
+ setfval(fp->retval, getfval(y));
+ else /* can't happen */
+ FATAL("bad type variable %d", y->tval);
+ tempfree(y);
+ }
+ return(jret);
+ case NEXT:
+ return(jnext);
+ case NEXTFILE:
+ nextfile();
+ return(jnextfile);
+ case BREAK:
+ return(jbreak);
+ case CONTINUE:
+ return(jcont);
+ default: /* can't happen */
+ FATAL("illegal jump type %d", n);
+ }
+ return 0; /* not reached */
+}
+
+Cell *getline(Node **a, int n) /* get next line from specific input */
+{ /* a[0] is variable, a[1] is operator, a[2] is filename */
+ Cell *r, *x;
+ extern Cell **fldtab;
+ FILE *fp;
+ char *buf;
+ int bufsize = recsize;
+ int mode;
+
+ if ((buf = (char *) malloc(bufsize)) == NULL)
+ FATAL("out of memory in getline");
+
+ fflush(stdout); /* in case someone is waiting for a prompt */
+ r = gettemp();
+ if (a[1] != NULL) { /* getline < file */
+ x = execute(a[2]); /* filename */
+ mode = ptoi(a[1]);
+ if (mode == '|') /* input pipe */
+ mode = LE; /* arbitrary flag */
+ fp = openfile(mode, getsval(x));
+ tempfree(x);
+ if (fp == NULL)
+ n = -1;
+ else
+ n = readrec(&buf, &bufsize, fp);
+ if (n <= 0) {
+ ;
+ } else if (a[0] != NULL) { /* getline var <file */
+ x = execute(a[0]);
+ setsval(x, buf);
+ tempfree(x);
+ } else { /* getline <file */
+ setsval(fldtab[0], buf);
+ if (is_number(fldtab[0]->sval)) {
+ fldtab[0]->fval = atof(fldtab[0]->sval);
+ fldtab[0]->tval |= NUM;
+ }
+ }
+ } else { /* bare getline; use current input */
+ if (a[0] == NULL) /* getline */
+ n = getrec(&record, &recsize, 1);
+ else { /* getline var */
+ n = getrec(&buf, &bufsize, 0);
+ x = execute(a[0]);
+ setsval(x, buf);
+ tempfree(x);
+ }
+ }
+ setfval(r, (Awkfloat) n);
+ free(buf);
+ return r;
+}
+
+Cell *getnf(Node **a, int n) /* get NF */
+{
+ if (donefld == 0)
+ fldbld();
+ return (Cell *) a[0];
+}
+
+Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
+{
+ Cell *x, *y, *z;
+ char *s;
+ Node *np;
+ char *buf;
+ int bufsz = recsize;
+ int nsub = strlen(*SUBSEP);
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in array");
+
+ x = execute(a[0]); /* Cell* for symbol table */
+ buf[0] = 0;
+ for (np = a[1]; np; np = np->nnext) {
+ y = execute(np); /* subscript */
+ s = getsval(y);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory for %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ if (np->nnext)
+ strcat(buf, *SUBSEP);
+ tempfree(y);
+ }
+ if (!isarr(x)) {
+ dprintf( ("making %s into an array\n", x->nval) );
+ if (freeable(x))
+ xfree(x->sval);
+ x->tval &= ~(STR|NUM|DONTFREE);
+ x->tval |= ARR;
+ x->sval = (char *) makesymtab(NSYMTAB);
+ }
+ z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
+ z->ctype = OCELL;
+ z->csub = CVAR;
+ tempfree(x);
+ free(buf);
+ return(z);
+}
+
+Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
+{
+ Cell *x, *y;
+ Node *np;
+ char *s;
+ int nsub = strlen(*SUBSEP);
+
+ x = execute(a[0]); /* Cell* for symbol table */
+ if (!isarr(x))
+ return True;
+ if (a[1] == 0) { /* delete the elements, not the table */
+ freesymtab(x);
+ x->tval &= ~STR;
+ x->tval |= ARR;
+ x->sval = (char *) makesymtab(NSYMTAB);
+ } else {
+ int bufsz = recsize;
+ char *buf;
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in adelete");
+ buf[0] = 0;
+ for (np = a[1]; np; np = np->nnext) {
+ y = execute(np); /* subscript */
+ s = getsval(y);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ if (np->nnext)
+ strcat(buf, *SUBSEP);
+ tempfree(y);
+ }
+ freeelem(x, buf);
+ free(buf);
+ }
+ tempfree(x);
+ return True;
+}
+
+Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */
+{
+ Cell *x, *ap, *k;
+ Node *p;
+ char *buf;
+ char *s;
+ int bufsz = recsize;
+ int nsub = strlen(*SUBSEP);
+
+ ap = execute(a[1]); /* array name */
+ if (!isarr(ap)) {
+ dprintf( ("making %s into an array\n", ap->nval) );
+ if (freeable(ap))
+ xfree(ap->sval);
+ ap->tval &= ~(STR|NUM|DONTFREE);
+ ap->tval |= ARR;
+ ap->sval = (char *) makesymtab(NSYMTAB);
+ }
+ if ((buf = (char *) malloc(bufsz)) == NULL) {
+ FATAL("out of memory in intest");
+ }
+ buf[0] = 0;
+ for (p = a[0]; p; p = p->nnext) {
+ x = execute(p); /* expr */
+ s = getsval(x);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ tempfree(x);
+ if (p->nnext)
+ strcat(buf, *SUBSEP);
+ }
+ k = lookup(buf, (Array *) ap->sval);
+ tempfree(ap);
+ free(buf);
+ if (k == NULL)
+ return(False);
+ else
+ return(True);
+}
+
+
+Cell *matchop(Node **a, int n) /* ~ and match() */
+{
+ Cell *x, *y;
+ char *s, *t;
+ int i;
+ void *p;
+
+ x = execute(a[1]); /* a[1] = target text */
+ s = getsval(x);
+ if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */
+ p = (void *) a[2];
+ else {
+ y = execute(a[2]); /* a[2] = regular expr */
+ t = getsval(y);
+ p = compre(t);
+ tempfree(y);
+ }
+ if (n == MATCHFCN)
+ i = pmatch(p, s, s);
+ else
+ i = match(p, s, s);
+ tempfree(x);
+ if (n == MATCHFCN) {
+ int start = countposn(s, patbeg-s)+1;
+ if (patlen < 0)
+ start = 0;
+ setfval(rstartloc, (Awkfloat) start);
+ setfval(rlengthloc, (Awkfloat) countposn(patbeg, patlen));
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = start;
+ return x;
+ } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
+ return(True);
+ else
+ return(False);
+}
+
+
+Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */
+{
+ Cell *x, *y;
+ int i;
+
+ x = execute(a[0]);
+ i = istrue(x);
+ tempfree(x);
+ switch (n) {
+ case BOR:
+ if (i) return(True);
+ y = execute(a[1]);
+ i = istrue(y);
+ tempfree(y);
+ if (i) return(True);
+ else return(False);
+ case AND:
+ if ( !i ) return(False);
+ y = execute(a[1]);
+ i = istrue(y);
+ tempfree(y);
+ if (i) return(True);
+ else return(False);
+ case NOT:
+ if (i) return(False);
+ else return(True);
+ default: /* can't happen */
+ FATAL("unknown boolean operator %d", n);
+ }
+ return 0; /*NOTREACHED*/
+}
+
+Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */
+{
+ int i;
+ Cell *x, *y;
+ Awkfloat j;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ if (x->tval&NUM && y->tval&NUM) {
+ j = x->fval - y->fval;
+ i = j<0? -1: (j>0? 1: 0);
+ } else {
+ i = strcmp(getsval(x), getsval(y));
+ }
+ tempfree(x);
+ tempfree(y);
+ switch (n) {
+ case LT: if (i<0) return(True);
+ else return(False);
+ case LE: if (i<=0) return(True);
+ else return(False);
+ case NE: if (i!=0) return(True);
+ else return(False);
+ case EQ: if (i == 0) return(True);
+ else return(False);
+ case GE: if (i>=0) return(True);
+ else return(False);
+ case GT: if (i>0) return(True);
+ else return(False);
+ default: /* can't happen */
+ FATAL("unknown relational operator %d", n);
+ }
+ return 0; /*NOTREACHED*/
+}
+
+void tfree(Cell *a) /* free a tempcell */
+{
+ if (freeable(a)) {
+ dprintf( ("freeing %s %s %o\n", a->nval, a->sval, a->tval) );
+ xfree(a->sval);
+ }
+ if (a == tmps)
+ FATAL("tempcell list is curdled");
+ a->cnext = tmps;
+ tmps = a;
+}
+
+Cell *gettemp(void) /* get a tempcell */
+{ int i;
+ Cell *x;
+
+ if (!tmps) {
+ tmps = (Cell *) calloc(100, sizeof(Cell));
+ if (!tmps)
+ FATAL("out of space for temporaries");
+ for(i = 1; i < 100; i++)
+ tmps[i-1].cnext = &tmps[i];
+ tmps[i-1].cnext = 0;
+ }
+ x = tmps;
+ tmps = x->cnext;
+ *x = tempcell;
+ return(x);
+}
+
+Cell *indirect(Node **a, int n) /* $( a[0] ) */
+{
+ Cell *x;
+ int m;
+ char *s;
+
+ x = execute(a[0]);
+ m = (int) getfval(x);
+ if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */
+ FATAL("illegal field $(%s), name \"%s\"", s, x->nval);
+ /* BUG: can x->nval ever be null??? */
+ tempfree(x);
+ x = fieldadr(m);
+ x->ctype = OCELL; /* BUG? why are these needed? */
+ x->csub = CFLD;
+ return(x);
+}
+
+Cell *substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */
+{
+ int k, m, n;
+ char *s, *p;
+ int temp;
+ Cell *x, *y, *z = 0;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ if (a[2] != 0)
+ z = execute(a[2]);
+ s = getsval(x);
+ k = countposn(s, strlen(s)) + 1;
+ if (k <= 1) {
+ tempfree(x);
+ tempfree(y);
+ if (a[2] != 0)
+ tempfree(z);
+ x = gettemp();
+ setsval(x, "");
+ return(x);
+ }
+ m = (int) getfval(y);
+ if (m <= 0)
+ m = 1;
+ else if (m > k)
+ m = k;
+ tempfree(y);
+ if (a[2] != 0) {
+ n = (int) getfval(z);
+ tempfree(z);
+ } else
+ n = k - 1;
+ if (n < 0)
+ n = 0;
+ else if (n > k - m)
+ n = k - m;
+ dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) );
+ y = gettemp();
+ while (*s && --m)
+ s += mblen(s, k);
+ for (p = s; *p && n--; p += mblen(p, k))
+ ;
+ temp = *p; /* with thanks to John Linderman */
+ *p = '\0';
+ setsval(y, s);
+ *p = temp;
+ tempfree(x);
+ return(y);
+}
+
+Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */
+{
+ Cell *x, *y, *z;
+ char *s1, *s2, *p1, *p2, *q;
+ Awkfloat v = 0.0;
+
+ x = execute(a[0]);
+ s1 = getsval(x);
+ y = execute(a[1]);
+ s2 = getsval(y);
+
+ z = gettemp();
+ for (p1 = s1; *p1 != '\0'; p1++) {
+ for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++)
+ ;
+ if (*p2 == '\0') {
+ v = (Awkfloat) countposn(s1, p1-s1) + 1; /* origin 1 */
+ break;
+ }
+ }
+ tempfree(x);
+ tempfree(y);
+ setfval(z, v);
+ return(z);
+}
+
+#define MAXNUMSIZE 50
+
+int format(char **pbuf, int *pbufsize, char *s, Node *a) /* printf-like conversions */
+{
+ char *fmt;
+ char *p, *t, *os;
+ Cell *x;
+ int flag = 0, n;
+ int fmtwd; /* format width */
+ int fmtsz = recsize;
+ char *buf = *pbuf;
+ int bufsize = *pbufsize;
+
+ os = s;
+ p = buf;
+ if ((fmt = (char *) malloc(fmtsz)) == NULL)
+ FATAL("out of memory in format()");
+ while (*s) {
+ adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format");
+ if (*s != '%') {
+ *p++ = *s++;
+ continue;
+ }
+ if (*(s+1) == '%') {
+ *p++ = '%';
+ s += 2;
+ continue;
+ }
+ /* have to be real careful in case this is a huge number, eg, %100000d */
+ fmtwd = atoi(s+1);
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+ for (t = fmt; (*t++ = *s) != '\0'; s++) {
+ if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, 0))
+ FATAL("format item %.30s... ran format() out of memory", os);
+ if (isalpha(*s) && *s != 'l' && *s != 'h' && *s != 'L')
+ break; /* the ansi panoply */
+ if (*s == '*') {
+ x = execute(a);
+ a = a->nnext;
+ sprintf(t-1, "%d", fmtwd=(int) getfval(x));
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+ t = fmt + strlen(fmt);
+ tempfree(x);
+ }
+ }
+ *t = '\0';
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+
+ switch (*s) {
+ case 'f': case 'e': case 'g': case 'E': case 'G':
+ flag = 1;
+ break;
+ case 'd': case 'i':
+ flag = 2;
+ if(*(s-1) == 'l') break;
+ *(t-1) = 'l';
+ *t = 'd';
+ *++t = '\0';
+ break;
+ case 'o': case 'x': case 'X': case 'u':
+ flag = *(s-1) == 'l' ? 2 : 3;
+ break;
+ case 's':
+ flag = 4;
+ break;
+ case 'c':
+ flag = 5;
+ break;
+ default:
+ WARNING("weird printf conversion %s", fmt);
+ flag = 0;
+ break;
+ }
+ if (a == NULL)
+ FATAL("not enough args in printf(%s)", os);
+ x = execute(a);
+ a = a->nnext;
+ n = MAXNUMSIZE;
+ if (fmtwd > n)
+ n = fmtwd;
+ adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format");
+ switch (flag) {
+ case 0: sprintf(p, "%s", fmt); /* unknown, so dump it too */
+ t = getsval(x);
+ n = strlen(t);
+ if (fmtwd > n)
+ n = fmtwd;
+ adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format");
+ p += strlen(p);
+ sprintf(p, "%s", t);
+ break;
+ case 1: sprintf(p, fmt, getfval(x)); break;
+ case 2: sprintf(p, fmt, (long) getfval(x)); break;
+ case 3: sprintf(p, fmt, (int) getfval(x)); break;
+ case 4:
+ t = getsval(x);
+ n = strlen(t);
+ if (fmtwd > n)
+ n = fmtwd;
+ if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, 0))
+ FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t);
+ sprintf(p, fmt, t);
+ break;
+ case 5:
+ if (isnum(x)) {
+ if (getfval(x))
+ sprintf(p, fmt, (int) getfval(x));
+ else{
+ *p++ = '\0';
+ *p = '\0';
+ }
+ } else
+ sprintf(p, fmt, getsval(x)[0]);
+ break;
+ }
+ tempfree(x);
+ p += strlen(p);
+ s++;
+ }
+ *p = '\0';
+ free(fmt);
+ for ( ; a; a = a->nnext) /* evaluate any remaining args */
+ execute(a);
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ return p - buf;
+}
+
+Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */
+{
+ Cell *x;
+ Node *y;
+ char *buf;
+ int bufsz=3*recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in awksprintf");
+ y = a[0]->nnext;
+ x = execute(a[0]);
+ if (format(&buf, &bufsz, getsval(x), y) == -1)
+ FATAL("sprintf string %.30s... too long. can't happen.", buf);
+ tempfree(x);
+ x = gettemp();
+ x->sval = buf;
+ x->tval = STR;
+ return(x);
+}
+
+Cell *awkprintf(Node **a, int n) /* printf */
+{ /* a[0] is list of args, starting with format string */
+ /* a[1] is redirection operator, a[2] is redirection file */
+ FILE *fp;
+ Cell *x;
+ Node *y;
+ char *buf;
+ int len;
+ int bufsz=3*recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in awkprintf");
+ y = a[0]->nnext;
+ x = execute(a[0]);
+ if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
+ FATAL("printf string %.30s... too long. can't happen.", buf);
+ tempfree(x);
+ if (a[1] == NULL) {
+ /* fputs(buf, stdout); */
+ fwrite(buf, len, 1, stdout);
+ if (ferror(stdout))
+ FATAL("write error on stdout");
+ } else {
+ fp = redirect(ptoi(a[1]), a[2]);
+ /* fputs(buf, fp); */
+ fwrite(buf, len, 1, fp);
+ fflush(fp);
+ if (ferror(fp))
+ FATAL("write error on %s", filename(fp));
+ }
+ free(buf);
+ return(True);
+}
+
+Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
+{
+ Awkfloat i, j = 0;
+ double v;
+ Cell *x, *y, *z;
+
+ x = execute(a[0]);
+ i = getfval(x);
+ tempfree(x);
+ if (n != UMINUS) {
+ y = execute(a[1]);
+ j = getfval(y);
+ tempfree(y);
+ }
+ z = gettemp();
+ switch (n) {
+ case ADD:
+ i += j;
+ break;
+ case MINUS:
+ i -= j;
+ break;
+ case MULT:
+ i *= j;
+ break;
+ case DIVIDE:
+ if (j == 0)
+ FATAL("division by zero");
+ i /= j;
+ break;
+ case MOD:
+ if (j == 0)
+ FATAL("division by zero in mod");
+ modf(i/j, &v);
+ i = i - j * v;
+ break;
+ case UMINUS:
+ i = -i;
+ break;
+ case POWER:
+ if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
+ i = ipow(i, (int) j);
+ else
+ i = errcheck(pow(i, j), "pow");
+ break;
+ default: /* can't happen */
+ FATAL("illegal arithmetic operator %d", n);
+ }
+ setfval(z, i);
+ return(z);
+}
+
+double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */
+{
+ double v;
+
+ if (n <= 0)
+ return 1;
+ v = ipow(x, n/2);
+ if (n % 2 == 0)
+ return v * v;
+ else
+ return x * v * v;
+}
+
+Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */
+{
+ Cell *x, *z;
+ int k;
+ Awkfloat xf;
+
+ x = execute(a[0]);
+ xf = getfval(x);
+ k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
+ if (n == PREINCR || n == PREDECR) {
+ setfval(x, xf + k);
+ return(x);
+ }
+ z = gettemp();
+ setfval(z, xf);
+ setfval(x, xf + k);
+ tempfree(x);
+ return(z);
+}
+
+Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */
+{ /* this is subtle; don't muck with it. */
+ Cell *x, *y;
+ Awkfloat xf, yf;
+ double v;
+
+ y = execute(a[1]);
+ x = execute(a[0]);
+ if (n == ASSIGN) { /* ordinary assignment */
+ if (x == y && !(x->tval & (FLD|REC))) /* self-assignment: */
+ ; /* leave alone unless it's a field */
+ else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+ setsval(x, getsval(y));
+ x->fval = getfval(y);
+ x->tval |= NUM;
+ }
+ else if (isstr(y))
+ setsval(x, getsval(y));
+ else if (isnum(y))
+ setfval(x, getfval(y));
+ else
+ funnyvar(y, "read value of");
+ tempfree(y);
+ return(x);
+ }
+ xf = getfval(x);
+ yf = getfval(y);
+ switch (n) {
+ case ADDEQ:
+ xf += yf;
+ break;
+ case SUBEQ:
+ xf -= yf;
+ break;
+ case MULTEQ:
+ xf *= yf;
+ break;
+ case DIVEQ:
+ if (yf == 0)
+ FATAL("division by zero in /=");
+ xf /= yf;
+ break;
+ case MODEQ:
+ if (yf == 0)
+ FATAL("division by zero in %%=");
+ modf(xf/yf, &v);
+ xf = xf - yf * v;
+ break;
+ case POWEQ:
+ if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */
+ xf = ipow(xf, (int) yf);
+ else
+ xf = errcheck(pow(xf, yf), "pow");
+ break;
+ default:
+ FATAL("illegal assignment operator %d", n);
+ break;
+ }
+ tempfree(y);
+ setfval(x, xf);
+ return(x);
+}
+
+Cell *cat(Node **a, int q) /* a[0] cat a[1] */
+{
+ Cell *x, *y, *z;
+ int n1, n2;
+ char *s;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ getsval(x);
+ getsval(y);
+ n1 = strlen(x->sval);
+ n2 = strlen(y->sval);
+ s = (char *) malloc(n1 + n2 + 1);
+ if (s == NULL)
+ FATAL("out of space concatenating %.15s... and %.15s...",
+ x->sval, y->sval);
+ strcpy(s, x->sval);
+ strcpy(s+n1, y->sval);
+ tempfree(y);
+ z = gettemp();
+ z->sval = s;
+ z->tval = STR;
+ tempfree(x);
+ return(z);
+}
+
+Cell *pastat(Node **a, int n) /* a[0] { a[1] } */
+{
+ Cell *x;
+
+ if (a[0] == 0)
+ x = execute(a[1]);
+ else {
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ }
+ }
+ return x;
+}
+
+Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */
+{
+ Cell *x;
+ int pair;
+
+ pair = ptoi(a[3]);
+ if (pairstack[pair] == 0) {
+ x = execute(a[0]);
+ if (istrue(x))
+ pairstack[pair] = 1;
+ tempfree(x);
+ }
+ if (pairstack[pair] == 1) {
+ x = execute(a[1]);
+ if (istrue(x))
+ pairstack[pair] = 0;
+ tempfree(x);
+ x = execute(a[2]);
+ return(x);
+ }
+ return(False);
+}
+
+Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */
+{
+ Cell *x = 0, *y, *ap;
+ char *s;
+ int sep;
+ char *t, temp, num[50], *fs = 0;
+ int n, arg3type;
+
+ y = execute(a[0]); /* source string */
+ s = getsval(y);
+ arg3type = ptoi(a[3]);
+ if (a[2] == 0) /* fs string */
+ fs = *FS;
+ else if (arg3type == STRING) { /* split(str,arr,"string") */
+ x = execute(a[2]);
+ fs = getsval(x);
+ } else if (arg3type == REGEXPR)
+ fs = "(regexpr)"; /* split(str,arr,/regexpr/) */
+ else
+ FATAL("illegal type of split");
+ sep = *fs;
+ ap = execute(a[1]); /* array name */
+ freesymtab(ap);
+ dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, ap->nval, fs) );
+ ap->tval &= ~STR;
+ ap->tval |= ARR;
+ ap->sval = (char *) makesymtab(NSYMTAB);
+
+ n = 0;
+ if ((*s != '\0' && strlen(fs) > 1) || arg3type == REGEXPR) { /* reg expr */
+ void *p;
+ if (arg3type == REGEXPR) { /* it's ready already */
+ p = (void *) a[2];
+ } else {
+ p = compre(fs);
+ }
+ t = s;
+ if (nematch(p,s,t)) {
+ do {
+ n++;
+ sprintf(num, "%d", n);
+ temp = *patbeg;
+ *patbeg = '\0';
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *patbeg = temp;
+ t = patbeg + patlen;
+ if (t[-1] == 0 || *t == 0) {
+ n++;
+ sprintf(num, "%d", n);
+ setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
+ goto spdone;
+ }
+ } while (nematch(p,s,t));
+ }
+ n++;
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ spdone:
+ p = NULL;
+ } else if (sep == ' ') {
+ for (n = 0; ; ) {
+ while (*s == ' ' || *s == '\t' || *s == '\n')
+ s++;
+ if (*s == 0)
+ break;
+ n++;
+ t = s;
+ do
+ s++;
+ while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0');
+ temp = *s;
+ *s = '\0';
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *s = temp;
+ if (*s != 0)
+ s++;
+ }
+ } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */
+ for (n = 0; *s != 0; s++) {
+ char buf[2];
+ n++;
+ sprintf(num, "%d", n);
+ buf[0] = *s;
+ buf[1] = 0;
+ if (isdigit(buf[0]))
+ setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
+ }
+ } else if (*s != 0) {
+ for (;;) {
+ n++;
+ t = s;
+ while (*s != sep && *s != '\n' && *s != '\0')
+ s++;
+ temp = *s;
+ *s = '\0';
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *s = temp;
+ if (*s++ == 0)
+ break;
+ }
+ }
+ tempfree(ap);
+ tempfree(y);
+ if (a[2] != 0 && arg3type == STRING)
+ tempfree(x);
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = n;
+ return(x);
+}
+
+Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ } else {
+ tempfree(x);
+ x = execute(a[2]);
+ }
+ return(x);
+}
+
+Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ } else if (a[2] != 0) {
+ tempfree(x);
+ x = execute(a[2]);
+ }
+ return(x);
+}
+
+Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */
+{
+ Cell *x;
+
+ for (;;) {
+ x = execute(a[0]);
+ if (!istrue(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[1]);
+ if (isbreak(x)) {
+ x = True;
+ return(x);
+ }
+ if (isnext(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ }
+}
+
+Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */
+{
+ Cell *x;
+
+ for (;;) {
+ x = execute(a[0]);
+ if (isbreak(x))
+ return True;
+ if (isnext(x) || isnextfile(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[1]);
+ if (!istrue(x))
+ return(x);
+ tempfree(x);
+ }
+}
+
+Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ tempfree(x);
+ for (;;) {
+ if (a[1]!=0) {
+ x = execute(a[1]);
+ if (!istrue(x)) return(x);
+ else tempfree(x);
+ }
+ x = execute(a[3]);
+ if (isbreak(x)) /* turn off break */
+ return True;
+ if (isnext(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[2]);
+ tempfree(x);
+ }
+}
+
+Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */
+{
+ Cell *x, *vp, *arrayp, *cp, *ncp;
+ Array *tp;
+ int i;
+
+ vp = execute(a[0]);
+ arrayp = execute(a[1]);
+ if (!isarr(arrayp)) {
+ return True;
+ }
+ tp = (Array *) arrayp->sval;
+ tempfree(arrayp);
+ for (i = 0; i < tp->size; i++) { /* this routine knows too much */
+ for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
+ setsval(vp, cp->nval);
+ ncp = cp->cnext;
+ x = execute(a[2]);
+ if (isbreak(x)) {
+ tempfree(vp);
+ return True;
+ }
+ if (isnext(x) || isexit(x) || isret(x)) {
+ tempfree(vp);
+ return(x);
+ }
+ tempfree(x);
+ }
+ }
+ return True;
+}
+
+Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */
+{
+ Cell *x, *y;
+ Awkfloat u;
+ int t;
+ wchar_t wc;
+ char *p, *buf;
+ char mbc[50];
+ Node *nextarg;
+ FILE *fp;
+
+ t = ptoi(a[0]);
+ x = execute(a[1]);
+ nextarg = a[1]->nnext;
+ switch (t) {
+ case FLENGTH:
+ p = getsval(x);
+ u = (Awkfloat) countposn(p, strlen(p)); break;
+ case FLOG:
+ u = errcheck(log(getfval(x)), "log"); break;
+ case FINT:
+ modf(getfval(x), &u); break;
+ case FEXP:
+ u = errcheck(exp(getfval(x)), "exp"); break;
+ case FSQRT:
+ u = errcheck(sqrt(getfval(x)), "sqrt"); break;
+ case FSIN:
+ u = sin(getfval(x)); break;
+ case FCOS:
+ u = cos(getfval(x)); break;
+ case FATAN:
+ if (nextarg == 0) {
+ WARNING("atan2 requires two arguments; returning 1.0");
+ u = 1.0;
+ } else {
+ y = execute(a[1]->nnext);
+ u = atan2(getfval(x), getfval(y));
+ tempfree(y);
+ nextarg = nextarg->nnext;
+ }
+ break;
+ case FSYSTEM:
+ fflush(stdout); /* in case something is buffered already */
+ u = (Awkfloat) system(getsval(x)) / 256; /* 256 is unix-dep */
+ break;
+ case FRAND:
+ /* in principle, rand() returns something in 0..RAND_MAX */
+ u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
+ break;
+ case FSRAND:
+ if (isrec(x)) /* no argument provided */
+ u = time((time_t *)0);
+ else
+ u = getfval(x);
+ srand((unsigned int) u);
+ break;
+ case FTOUPPER:
+ case FTOLOWER:
+ buf = tostring(getsval(x));
+ if (t == FTOUPPER) {
+ for (p = buf; *p; p++)
+ if (islower(*p))
+ *p = toupper(*p);
+ } else {
+ for (p = buf; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+ }
+ tempfree(x);
+ x = gettemp();
+ setsval(x, buf);
+ free(buf);
+ return x;
+ case FFLUSH:
+ if ((fp = openfile(FFLUSH, getsval(x))) == NULL)
+ u = EOF;
+ else
+ u = fflush(fp);
+ break;
+ case FUTF:
+ wc = (int)getfval(x);
+ mbc[wctomb(mbc, wc)] = 0;
+ tempfree(x);
+ x = gettemp();
+ setsval(x, mbc);
+ return x;
+ default: /* can't happen */
+ FATAL("illegal function type %d", t);
+ break;
+ }
+ tempfree(x);
+ x = gettemp();
+ setfval(x, u);
+ if (nextarg != 0) {
+ WARNING("warning: function has too many arguments");
+ for ( ; nextarg; nextarg = nextarg->nnext)
+ execute(nextarg);
+ }
+ return(x);
+}
+
+Cell *printstat(Node **a, int n) /* print a[0] */
+{
+ int r;
+ Node *x;
+ Cell *y;
+ FILE *fp;
+
+ if (a[1] == 0) /* a[1] is redirection operator, a[2] is file */
+ fp = stdout;
+ else
+ fp = redirect(ptoi(a[1]), a[2]);
+ for (x = a[0]; x != NULL; x = x->nnext) {
+ y = execute(x);
+ fputs(getsval(y), fp);
+ tempfree(y);
+ if (x->nnext == NULL)
+ r = fputs(*ORS, fp);
+ else
+ r = fputs(*OFS, fp);
+ if (r == EOF)
+ FATAL("write error on %s", filename(fp));
+ }
+ if (a[1] != 0)
+ if (fflush(fp) == EOF)
+ FATAL("write error on %s", filename(fp));
+ return(True);
+}
+
+Cell *nullproc(Node **a, int n)
+{
+ n = n;
+ a = a;
+ return 0;
+}
+
+
+FILE *redirect(int a, Node *b) /* set up all i/o redirections */
+{
+ FILE *fp;
+ Cell *x;
+ char *fname;
+
+ x = execute(b);
+ fname = getsval(x);
+ fp = openfile(a, fname);
+ if (fp == NULL)
+ FATAL("can't open file %s", fname);
+ tempfree(x);
+ return fp;
+}
+
+struct files {
+ FILE *fp;
+ char *fname;
+ int mode; /* '|', 'a', 'w' => LE/LT, GT */
+} files[FOPEN_MAX] ={
+ { NULL, "/dev/stdin", LT }, /* watch out: don't free this! */
+ { NULL, "/dev/stdout", GT },
+ { NULL, "/dev/stderr", GT }
+};
+
+void stdinit(void) /* in case stdin, etc., are not constants */
+{
+ files[0].fp = stdin;
+ files[1].fp = stdout;
+ files[2].fp = stderr;
+}
+
+FILE *openfile(int a, char *us)
+{
+ char *s = us;
+ int i, m;
+ FILE *fp = 0;
+
+ if (*s == '\0')
+ FATAL("null file name in print or getline");
+ for (i=0; i < FOPEN_MAX; i++)
+ if (files[i].fname && strcmp(s, files[i].fname) == 0) {
+ if (a == files[i].mode || (a==APPEND && files[i].mode==GT))
+ return files[i].fp;
+ if (a == FFLUSH)
+ return files[i].fp;
+ }
+ if (a == FFLUSH) /* didn't find it, so don't create it! */
+ return NULL;
+
+ for (i=0; i < FOPEN_MAX; i++)
+ if (files[i].fp == 0)
+ break;
+ if (i >= FOPEN_MAX)
+ FATAL("%s makes too many open files", s);
+ fflush(stdout); /* force a semblance of order */
+ m = a;
+ if (a == GT) {
+ fp = fopen(s, "w");
+ } else if (a == APPEND) {
+ fp = fopen(s, "a");
+ m = GT; /* so can mix > and >> */
+ } else if (a == '|') { /* output pipe */
+ fp = popen(s, "w");
+ } else if (a == LE) { /* input pipe */
+ fp = popen(s, "r");
+ } else if (a == LT) { /* getline <file */
+ fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r"); /* "-" is stdin */
+ } else /* can't happen */
+ FATAL("illegal redirection %d", a);
+ if (fp != NULL) {
+ files[i].fname = tostring(s);
+ files[i].fp = fp;
+ files[i].mode = m;
+ }
+ return fp;
+}
+
+char *filename(FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (fp == files[i].fp)
+ return files[i].fname;
+ return "???";
+}
+
+Cell *closefile(Node **a, int n)
+{
+ Cell *x;
+ int i, stat;
+
+ n = n;
+ x = execute(a[0]);
+ getsval(x);
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
+ if (ferror(files[i].fp))
+ WARNING( "i/o error occurred on %s", files[i].fname );
+ if (files[i].mode == '|' || files[i].mode == LE)
+ stat = pclose(files[i].fp);
+ else
+ stat = fclose(files[i].fp);
+ if (stat == EOF)
+ WARNING( "i/o error occurred closing %s", files[i].fname );
+ if (i > 2) /* don't do /dev/std... */
+ xfree(files[i].fname);
+ files[i].fname = NULL; /* watch out for ref thru this */
+ files[i].fp = NULL;
+ }
+ tempfree(x);
+ return(True);
+}
+
+void closeall(void)
+{
+ int i, stat;
+
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (files[i].fp) {
+ if (ferror(files[i].fp))
+ WARNING( "i/o error occurred on %s", files[i].fname );
+ if (files[i].mode == '|' || files[i].mode == LE)
+ stat = pclose(files[i].fp);
+ else
+ stat = fclose(files[i].fp);
+ if (stat == EOF)
+ WARNING( "i/o error occurred while closing %s", files[i].fname );
+ }
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr);
+
+Cell *sub(Node **a, int nnn) /* substitute command */
+{
+ char *sptr, *pb, *q;
+ Cell *x, *y, *result;
+ char *t, *buf;
+ void *p;
+ int bufsz = recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in sub");
+ x = execute(a[3]); /* target string */
+ t = getsval(x);
+ if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */
+ p = (void *) a[1]; /* regular expression */
+ else {
+ y = execute(a[1]);
+ p = compre(getsval(y));
+ tempfree(y);
+ }
+ y = execute(a[2]); /* replacement string */
+ result = False;
+ if (pmatch(p, t, t)) {
+ sptr = t;
+ adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub");
+ pb = buf;
+ while (sptr < patbeg)
+ *pb++ = *sptr++;
+ sptr = getsval(y);
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ *pb = '\0';
+ if (pb > buf + bufsz)
+ FATAL("sub result1 %.30s too big; can't happen", buf);
+ sptr = patbeg + patlen;
+ if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
+ adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub");
+ while ((*pb++ = *sptr++) != 0)
+ ;
+ }
+ if (pb > buf + bufsz)
+ FATAL("sub result2 %.30s too big; can't happen", buf);
+ setsval(x, buf); /* BUG: should be able to avoid copy */
+ result = True;;
+ }
+ tempfree(x);
+ tempfree(y);
+ free(buf);
+ return result;
+}
+
+Cell *gsub(Node **a, int nnn) /* global substitute */
+{
+ Cell *x, *y;
+ char *rptr, *sptr, *t, *pb, *c;
+ char *buf;
+ void *p;
+ int mflag, num;
+ int bufsz = recsize;
+
+ if ((buf = (char *)malloc(bufsz)) == NULL)
+ FATAL("out of memory in gsub");
+ mflag = 0; /* if mflag == 0, can replace empty string */
+ num = 0;
+ x = execute(a[3]); /* target string */
+ c = t = getsval(x);
+ if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */
+ p = (void *) a[1]; /* regular expression */
+ else {
+ y = execute(a[1]);
+ p = compre(getsval(y));
+ tempfree(y);
+ }
+ y = execute(a[2]); /* replacement string */
+ if (pmatch(p, t, c)) {
+ pb = buf;
+ rptr = getsval(y);
+ do {
+ if (patlen == 0 && *patbeg != 0) { /* matched empty string */
+ if (mflag == 0) { /* can replace empty */
+ num++;
+ sptr = rptr;
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ char *q;
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ }
+ if (*c == 0) /* at end */
+ goto done;
+ adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub");
+ *pb++ = *c++;
+ if (pb > buf + bufsz) /* BUG: not sure of this test */
+ FATAL("gsub result0 %.30s too big; can't happen", buf);
+ mflag = 0;
+ }
+ else { /* matched nonempty string */
+ num++;
+ sptr = c;
+ adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub");
+ while (sptr < patbeg)
+ *pb++ = *sptr++;
+ sptr = rptr;
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ char *q;
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ c = patbeg + patlen;
+ if ((c[-1] == 0) || (*c == 0))
+ goto done;
+ if (pb > buf + bufsz)
+ FATAL("gsub result1 %.30s too big; can't happen", buf);
+ mflag = 1;
+ }
+ } while (pmatch(p, t, c));
+ sptr = c;
+ adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub");
+ while ((*pb++ = *sptr++) != 0)
+ ;
+ done: if (pb > buf + bufsz)
+ FATAL("gsub result2 %.30s too big; can't happen", buf);
+ *pb = '\0';
+ setsval(x, buf); /* BUG: should be able to avoid copy + free */
+ }
+ tempfree(x);
+ tempfree(y);
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = num;
+ free(buf);
+ return(x);
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr) /* handle \\& variations */
+{ /* sptr[0] == '\\' */
+ char *pb = *pb_ptr, *sptr = *sptr_ptr;
+
+ if (sptr[1] == '\\') {
+ if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
+ *pb++ = '\\';
+ *pb++ = '&';
+ sptr += 4;
+ } else if (sptr[2] == '&') { /* \\& -> \ + matched */
+ *pb++ = '\\';
+ sptr += 2;
+ } else { /* \\x -> \\x */
+ *pb++ = *sptr++;
+ *pb++ = *sptr++;
+ }
+ } else if (sptr[1] == '&') { /* literal & */
+ sptr++;
+ *pb++ = *sptr++;
+ } else /* literal \ */
+ *pb++ = *sptr++;
+
+ *pb_ptr = pb;
+ *sptr_ptr = sptr;
+}
+
diff --git a/bin/9base/awk/tran.c b/bin/9base/awk/tran.c
new file mode 100644
index 00000000..272a7fdc
--- /dev/null
+++ b/bin/9base/awk/tran.c
@@ -0,0 +1,435 @@
+/****************************************************************
+Copyright (C) Lucent Technologies 1997
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+****************************************************************/
+
+#define DEBUG
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "awk.h"
+#include "y.tab.h"
+
+#define FULLTAB 2 /* rehash when table gets this x full */
+#define GROWTAB 4 /* grow table by this factor */
+
+Array *symtab; /* main symbol table */
+
+char **FS; /* initial field sep */
+char **RS; /* initial record sep */
+char **OFS; /* output field sep */
+char **ORS; /* output record sep */
+char **OFMT; /* output format for numbers */
+char **CONVFMT; /* format for conversions in getsval */
+Awkfloat *NF; /* number of fields in current record */
+Awkfloat *NR; /* number of current record */
+Awkfloat *FNR; /* number of current record in current file */
+char **FILENAME; /* current filename argument */
+Awkfloat *ARGC; /* number of arguments from command line */
+char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
+Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
+Awkfloat *RLENGTH; /* length of same */
+
+Cell *nrloc; /* NR */
+Cell *nfloc; /* NF */
+Cell *fnrloc; /* FNR */
+Array *ARGVtab; /* symbol table containing ARGV[...] */
+Array *ENVtab; /* symbol table containing ENVIRON[...] */
+Cell *rstartloc; /* RSTART */
+Cell *rlengthloc; /* RLENGTH */
+Cell *symtabloc; /* SYMTAB */
+
+Cell *nullloc; /* a guaranteed empty cell */
+Node *nullnode; /* zero&null, converted into a node for comparisons */
+Cell *literal0;
+
+extern Cell **fldtab;
+
+void syminit(void) /* initialize symbol table with builtin vars */
+{
+ literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
+ /* this is used for if(x)... tests: */
+ nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
+ nullnode = celltonode(nullloc, CCON);
+
+ FS = &setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab)->sval;
+ RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+ OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
+ ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+ OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+ CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
+ FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
+ nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
+ NF = &nfloc->fval;
+ nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
+ NR = &nrloc->fval;
+ fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
+ FNR = &fnrloc->fval;
+ SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
+ rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
+ RSTART = &rstartloc->fval;
+ rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
+ RLENGTH = &rlengthloc->fval;
+ symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
+ symtabloc->sval = (char *) symtab;
+}
+
+void arginit(int ac, char **av) /* set up ARGV and ARGC */
+{
+ Cell *cp;
+ int i;
+ char temp[50];
+
+ ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
+ cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
+ ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
+ cp->sval = (char *) ARGVtab;
+ for (i = 0; i < ac; i++) {
+ sprintf(temp, "%d", i);
+ if (is_number(*av))
+ setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
+ else
+ setsymtab(temp, *av, 0.0, STR, ARGVtab);
+ av++;
+ }
+}
+
+void envinit(char **envp) /* set up ENVIRON variable */
+{
+ Cell *cp;
+ char *p;
+
+ cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
+ ENVtab = makesymtab(NSYMTAB);
+ cp->sval = (char *) ENVtab;
+ for ( ; *envp; envp++) {
+ if ((p = strchr(*envp, '=')) == NULL)
+ continue;
+ *p++ = 0; /* split into two strings at = */
+ if (is_number(p))
+ setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
+ else
+ setsymtab(*envp, p, 0.0, STR, ENVtab);
+ p[-1] = '='; /* restore in case env is passed down to a shell */
+ }
+}
+
+Array *makesymtab(int n) /* make a new symbol table */
+{
+ Array *ap;
+ Cell **tp;
+
+ ap = (Array *) malloc(sizeof(Array));
+ tp = (Cell **) calloc(n, sizeof(Cell *));
+ if (ap == NULL || tp == NULL)
+ FATAL("out of space in makesymtab");
+ ap->nelem = 0;
+ ap->size = n;
+ ap->tab = tp;
+ return(ap);
+}
+
+void freesymtab(Cell *ap) /* free a symbol table */
+{
+ Cell *cp, *temp;
+ Array *tp;
+ int i;
+
+ if (!isarr(ap))
+ return;
+ tp = (Array *) ap->sval;
+ if (tp == NULL)
+ return;
+ for (i = 0; i < tp->size; i++) {
+ for (cp = tp->tab[i]; cp != NULL; cp = temp) {
+ xfree(cp->nval);
+ if (freeable(cp))
+ xfree(cp->sval);
+ temp = cp->cnext; /* avoids freeing then using */
+ free(cp);
+ }
+ tp->tab[i] = 0;
+ }
+ free(tp->tab);
+ free(tp);
+}
+
+void freeelem(Cell *ap, char *s) /* free elem s from ap (i.e., ap["s"] */
+{
+ Array *tp;
+ Cell *p, *prev = NULL;
+ int h;
+
+ tp = (Array *) ap->sval;
+ h = hash(s, tp->size);
+ for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
+ if (strcmp(s, p->nval) == 0) {
+ if (prev == NULL) /* 1st one */
+ tp->tab[h] = p->cnext;
+ else /* middle somewhere */
+ prev->cnext = p->cnext;
+ if (freeable(p))
+ xfree(p->sval);
+ free(p->nval);
+ free(p);
+ tp->nelem--;
+ return;
+ }
+}
+
+Cell *setsymtab(char *n, char *s, Awkfloat f, unsigned t, Array *tp)
+{
+ int h;
+ Cell *p;
+
+ if (n != NULL && (p = lookup(n, tp)) != NULL) {
+ dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
+ p, p->nval, p->sval, p->fval, p->tval) );
+ return(p);
+ }
+ p = (Cell *) malloc(sizeof(Cell));
+ if (p == NULL)
+ FATAL("out of space for symbol table at %s", n);
+ p->nval = tostring(n);
+ p->sval = s ? tostring(s) : tostring("");
+ p->fval = f;
+ p->tval = t;
+ p->csub = CUNK;
+ p->ctype = OCELL;
+ tp->nelem++;
+ if (tp->nelem > FULLTAB * tp->size)
+ rehash(tp);
+ h = hash(n, tp->size);
+ p->cnext = tp->tab[h];
+ tp->tab[h] = p;
+ dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
+ p, p->nval, p->sval, p->fval, p->tval) );
+ return(p);
+}
+
+int hash(char *s, int n) /* form hash value for string s */
+{
+ unsigned hashval;
+
+ for (hashval = 0; *s != '\0'; s++)
+ hashval = (*s + 31 * hashval);
+ return hashval % n;
+}
+
+void rehash(Array *tp) /* rehash items in small table into big one */
+{
+ int i, nh, nsz;
+ Cell *cp, *op, **np;
+
+ nsz = GROWTAB * tp->size;
+ np = (Cell **) calloc(nsz, sizeof(Cell *));
+ if (np == NULL) /* can't do it, but can keep running. */
+ return; /* someone else will run out later. */
+ for (i = 0; i < tp->size; i++) {
+ for (cp = tp->tab[i]; cp; cp = op) {
+ op = cp->cnext;
+ nh = hash(cp->nval, nsz);
+ cp->cnext = np[nh];
+ np[nh] = cp;
+ }
+ }
+ free(tp->tab);
+ tp->tab = np;
+ tp->size = nsz;
+}
+
+Cell *lookup(char *s, Array *tp) /* look for s in tp */
+{
+ Cell *p;
+ int h;
+
+ h = hash(s, tp->size);
+ for (p = tp->tab[h]; p != NULL; p = p->cnext)
+ if (strcmp(s, p->nval) == 0)
+ return(p); /* found it */
+ return(NULL); /* not found */
+}
+
+Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
+{
+ int fldno;
+
+ if ((vp->tval & (NUM | STR)) == 0)
+ funnyvar(vp, "assign to");
+ if (isfld(vp)) {
+ donerec = 0; /* mark $0 invalid */
+ fldno = atoi(vp->nval);
+ if (fldno > *NF)
+ newfld(fldno);
+ dprintf( ("setting field %d to %g\n", fldno, f) );
+ } else if (isrec(vp)) {
+ donefld = 0; /* mark $1... invalid */
+ donerec = 1;
+ }
+ if (freeable(vp))
+ xfree(vp->sval); /* free any previous string */
+ vp->tval &= ~STR; /* mark string invalid */
+ vp->tval |= NUM; /* mark number ok */
+ dprintf( ("setfval %p: %s = %g, t=%o\n", vp, vp->nval, f, vp->tval) );
+ return vp->fval = f;
+}
+
+void funnyvar(Cell *vp, char *rw)
+{
+ if (isarr(vp))
+ FATAL("can't %s %s; it's an array name.", rw, vp->nval);
+ if (vp->tval & FCN)
+ FATAL("can't %s %s; it's a function.", rw, vp->nval);
+ WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
+ vp, vp->nval, vp->sval, vp->fval, vp->tval);
+}
+
+char *setsval(Cell *vp, char *s) /* set string val of a Cell */
+{
+ char *t;
+ int fldno;
+
+ dprintf( ("starting setsval %p: %s = \"%s\", t=%o\n", vp, vp->nval, s, vp->tval) );
+ if ((vp->tval & (NUM | STR)) == 0)
+ funnyvar(vp, "assign to");
+ if (isfld(vp)) {
+ donerec = 0; /* mark $0 invalid */
+ fldno = atoi(vp->nval);
+ if (fldno > *NF)
+ newfld(fldno);
+ dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
+ } else if (isrec(vp)) {
+ donefld = 0; /* mark $1... invalid */
+ donerec = 1;
+ }
+ t = tostring(s); /* in case it's self-assign */
+ vp->tval &= ~NUM;
+ vp->tval |= STR;
+ if (freeable(vp))
+ xfree(vp->sval);
+ vp->tval &= ~DONTFREE;
+ dprintf( ("setsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, t,t, vp->tval) );
+ return(vp->sval = t);
+}
+
+Awkfloat getfval(Cell *vp) /* get float val of a Cell */
+{
+ if ((vp->tval & (NUM | STR)) == 0)
+ funnyvar(vp, "read value of");
+ if (isfld(vp) && donefld == 0)
+ fldbld();
+ else if (isrec(vp) && donerec == 0)
+ recbld();
+ if (!isnum(vp)) { /* not a number */
+ vp->fval = atof(vp->sval); /* best guess */
+ if (is_number(vp->sval) && !(vp->tval&CON))
+ vp->tval |= NUM; /* make NUM only sparingly */
+ }
+ dprintf( ("getfval %p: %s = %g, t=%o\n", vp, vp->nval, vp->fval, vp->tval) );
+ return(vp->fval);
+}
+
+char *getsval(Cell *vp) /* get string val of a Cell */
+{
+ char s[100]; /* BUG: unchecked */
+ double dtemp;
+
+ if ((vp->tval & (NUM | STR)) == 0)
+ funnyvar(vp, "read value of");
+ if (isfld(vp) && donefld == 0)
+ fldbld();
+ else if (isrec(vp) && donerec == 0)
+ recbld();
+ if (isstr(vp) == 0) {
+ if (freeable(vp))
+ xfree(vp->sval);
+ if (modf(vp->fval, &dtemp) == 0) /* it's integral */
+ sprintf(s, "%.30g", vp->fval);
+ else
+ sprintf(s, *CONVFMT, vp->fval);
+ vp->sval = tostring(s);
+ vp->tval &= ~DONTFREE;
+ vp->tval |= STR;
+ }
+ dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, vp->sval, vp->sval, vp->tval) );
+ return(vp->sval);
+}
+
+char *tostring(char *s) /* make a copy of string s */
+{
+ char *p;
+
+ p = (char *) malloc(strlen(s)+1);
+ if (p == NULL)
+ FATAL("out of space in tostring on %s", s);
+ strcpy(p, s);
+ return(p);
+}
+
+char *qstring(char *s, int delim) /* collect string up to next delim */
+{
+ char *os = s;
+ int c, n;
+ char *buf, *bp;
+
+ if ((buf = (char *) malloc(strlen(s)+3)) == NULL)
+ FATAL( "out of space in qstring(%s)", s);
+ for (bp = buf; (c = *s) != delim; s++) {
+ if (c == '\n')
+ SYNTAX( "newline in string %.20s...", os );
+ else if (c != '\\')
+ *bp++ = c;
+ else { /* \something */
+ c = *++s;
+ if (c == 0) { /* \ at end */
+ *bp++ = '\\';
+ break; /* for loop */
+ }
+ switch (c) {
+ case '\\': *bp++ = '\\'; break;
+ case 'n': *bp++ = '\n'; break;
+ case 't': *bp++ = '\t'; break;
+ case 'b': *bp++ = '\b'; break;
+ case 'f': *bp++ = '\f'; break;
+ case 'r': *bp++ = '\r'; break;
+ default:
+ if (!isdigit(c)) {
+ *bp++ = c;
+ break;
+ }
+ n = c - '0';
+ if (isdigit(s[1])) {
+ n = 8 * n + *++s - '0';
+ if (isdigit(s[1]))
+ n = 8 * n + *++s - '0';
+ }
+ *bp++ = n;
+ break;
+ }
+ }
+ }
+ *bp++ = 0;
+ return buf;
+}
+
diff --git a/bin/9base/basename/Makefile b/bin/9base/basename/Makefile
new file mode 100644
index 00000000..738339a2
--- /dev/null
+++ b/bin/9base/basename/Makefile
@@ -0,0 +1,10 @@
+# basename - basename unix port from plan9
+# Depends on ../lib9
+
+TARG = basename
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/basename/basename.1 b/bin/9base/basename/basename.1
new file mode 100644
index 00000000..ba31a487
--- /dev/null
+++ b/bin/9base/basename/basename.1
@@ -0,0 +1,35 @@
+.TH BASENAME 1
+.SH NAME
+basename \- strip file name affixes
+.SH SYNOPSIS
+.B basename
+[
+.B -d
+]
+.I string
+[
+.I suffix
+]
+.SH DESCRIPTION
+.PP
+.I Basename
+deletes any prefix ending in slash
+.RB ( / )
+and the
+.IR suffix ,
+if present in
+.IR string ,
+from
+.IR string ,
+and prints the result on the standard output.
+.PP
+The
+.B -d
+option instead prints the directory component,
+that is,
+.I string
+up to but not including the final slash.
+If the string contains no slash,
+a period and newline are printed.
+.SH SOURCE
+.B \*9/src/cmd/basename.c
diff --git a/bin/9base/basename/basename.c b/bin/9base/basename/basename.c
new file mode 100644
index 00000000..7b274b22
--- /dev/null
+++ b/bin/9base/basename/basename.c
@@ -0,0 +1,41 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+ char *pr;
+ int n, dflag;
+
+ dflag = 0;
+ if(argc>1 && strcmp(argv[1], "-d") == 0){
+ --argc;
+ ++argv;
+ dflag = 1;
+ }
+ if(argc < 2 || argc > 3){
+ fprint(2, "usage: basename [-d] string [suffix]\n");
+ exits("usage");
+ }
+ pr = utfrrune(argv[1], '/');
+ if(dflag){
+ if(pr){
+ *pr = 0;
+ print("%s\n", argv[1]);
+ exits(0);
+ }
+ print(".\n");
+ exits(0);
+ }
+ if(pr)
+ pr++;
+ else
+ pr = argv[1];
+ if(argc==3){
+ n = strlen(pr)-strlen(argv[2]);
+ if(n >= 0 && !strcmp(pr+n, argv[2]))
+ pr[n] = 0;
+ }
+ print("%s\n", pr);
+ exits(0);
+}
diff --git a/bin/9base/bc/Makefile b/bin/9base/bc/Makefile
new file mode 100644
index 00000000..8eed9d56
--- /dev/null
+++ b/bin/9base/bc/Makefile
@@ -0,0 +1,19 @@
+# bc - bc unix port from plan9
+# Depends on ../lib9
+
+TARG = bc
+OFILES = y.tab.o
+YFILES = bc.y
+MANFILES = bc.1
+
+include ../yacc.mk
+
+# Solaris
+#LDFLAGS += -dy -lxnet
+
+pre-uninstall:
+ rm -f ${DESTDIR}${PREFIX}/lib/bclib
+
+post-install:
+ @mkdir -p ${DESTDIR}${PREFIX}/lib
+ @cp bclib ${DESTDIR}${PREFIX}/lib/
diff --git a/bin/9base/bc/bc.1 b/bin/9base/bc/bc.1
new file mode 100644
index 00000000..57194340
--- /dev/null
+++ b/bin/9base/bc/bc.1
@@ -0,0 +1,292 @@
+.TH BC 1
+.SH NAME
+bc \- arbitrary-precision arithmetic language
+.SH SYNOPSIS
+.B bc
+[
+.B -c
+]
+[
+.B -l
+]
+[
+.B -s
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Bc
+is an interactive processor for a language that resembles
+C but provides arithmetic on numbers of arbitrary length with up
+to 100 digits right of the decimal point.
+It takes input from any files given, then reads
+the standard input.
+The
+.B -l
+argument stands for the name
+of an arbitrary precision math library.
+The
+.B -s
+argument suppresses the automatic display
+of calculation results; all output is via the
+.B print
+command.
+.PP
+The following syntax for
+.I bc
+programs is like that of C;
+.I L
+means letter
+.BR a - z ,
+.I E
+means expression,
+.I S
+means statement.
+.TF length(E)
+.TP
+Lexical
+.RS
+.HP
+comments are enclosed in
+.B /* */
+.HP
+newlines end statements
+.RE
+.TP
+Names
+.IP
+simple variables:
+.I L
+.br
+array elements:
+.IB L [ E ]
+.br
+The words
+.BR ibase ,
+.BR obase ,
+and
+.B scale
+.TP
+Other operands
+.IP
+arbitrarily long numbers with optional sign and decimal point.
+.RS
+.TP
+.BI ( E )
+.TP
+.BI sqrt( E )
+.TP
+.BI length( E )
+number of significant decimal digits
+.TP
+.BI scale( E )
+number of digits right of decimal point
+.TP
+.IB L ( E , ... ,\fIE\fP)
+function call
+.RE
+.TP
+Operators
+.RS
+.HP
+.B "+ - * / % ^\ "
+.RB ( %
+is remainder;
+.B ^
+is power)
+.HP
+.B "++ --\ "
+.TP
+.B "== <= >= != < >"
+.TP
+.B "= += -= *= /= %= ^="
+.RE
+.TP
+Statements
+.RS
+.br
+.I E
+.br
+.B {
+.I S
+.B ;
+\&...
+.B ;
+.I S
+.B }
+.br
+.B "print"
+.I E
+.br
+.B "if ("
+.I E
+.B )
+.I S
+.br
+.B "while ("
+.I E
+.B )
+.I S
+.br
+.B "for ("
+.I E
+.B ;
+.I E
+.B ;
+.I E
+.B ")"
+.I S
+.br
+null statement
+.br
+.B break
+.br
+.B quit
+.br
+\fL"\fRtext\fL"\fR
+.RE
+.TP
+Function definitions
+.RS
+.br
+.B define
+.I L
+.B (
+.I L
+.B ,
+\&...
+.B ,
+.I L
+.B ){
+.PD0
+.br
+.B auto
+.I L
+.B ,
+\&...
+.B ,
+.I L
+.br
+.I S
+.B ;
+\&...
+.B ;
+.I S
+.br
+.B return
+.I E
+.LP
+.B }
+.RE
+.TP
+Functions in
+.B -l
+math library
+.RS
+.TP
+.BI s( x )
+sine
+.TP
+.BI c( x )
+cosine
+.TP
+.BI e( x )
+exponential
+.TP
+.BI l( x )
+log
+.TP
+.BI a( x )
+arctangent
+.TP
+.BI j( "n, x" )
+Bessel function
+.RE
+.PP
+.DT
+All function arguments are passed by value.
+.PD
+.PP
+The value of an expression at the top level is printed
+unless the main operator is an assignment or the
+.B -s
+command line argument is given.
+Text in quotes, which may include newlines, is always printed.
+Either semicolons or newlines may separate statements.
+Assignment to
+.B scale
+influences the number of digits to be retained on arithmetic
+operations in the manner of
+.IR dc (1).
+Assignments to
+.B ibase
+or
+.B obase
+set the input and output number radix respectively.
+.PP
+The same letter may be used as an array, a function,
+and a simple variable simultaneously.
+All variables are global to the program.
+Automatic variables are pushed down during function calls.
+In a declaration of an array as a function argument
+or automatic variable
+empty square brackets must follow the array name.
+.PP
+.I Bc
+is actually a preprocessor for
+.IR dc (1),
+which it invokes automatically, unless the
+.B -c
+(compile only)
+option is present.
+In this case the
+.I dc
+input is sent to the standard output instead.
+.SH EXAMPLE
+Define a function to compute an approximate value of
+the exponential.
+Use it to print 10 values.
+(The exponential function in the library gives better answers.)
+.PP
+.EX
+scale = 20
+define e(x) {
+ auto a, b, c, i, s
+ a = 1
+ b = 1
+ s = 1
+ for(i=1; 1; i++) {
+ a *= x
+ b *= i
+ c = a/b
+ if(c == 0) return s
+ s += c
+ }
+}
+for(i=1; i<=10; i++) print e(i)
+.EE
+.SH FILES
+.B \*9/lib/bclib
+mathematical library
+.SH SOURCE
+.B \*9/src/cmd/bc.y
+.SH "SEE ALSO"
+.IR dc (1),
+.IR hoc (1)
+.SH BUGS
+No
+.LR && ,
+.LR || ,
+or
+.L !
+operators.
+.PP
+A
+.L for
+statement must have all three
+.LR E s.
+.PP
+A
+.L quit
+is interpreted when read, not when executed.
diff --git a/bin/9base/bc/bc.y b/bin/9base/bc/bc.y
new file mode 100644
index 00000000..91ffcf03
--- /dev/null
+++ b/bin/9base/bc/bc.y
@@ -0,0 +1,988 @@
+%{
+ #include <u.h>
+ #include <libc.h>
+ #include <bio.h>
+
+ #define bsp_max 5000
+
+ Biobuf *in;
+ Biobuf bstdin;
+ Biobuf bstdout;
+ char cary[1000];
+ char* cp = { cary };
+ char string[1000];
+ char* str = { string };
+ int crs = 128;
+ int rcrs = 128; /* reset crs */
+ int bindx = 0;
+ int lev = 0;
+ int ln;
+ char* ttp;
+ char* ss = "";
+ int bstack[10] = { 0 };
+ char* numb[15] =
+ {
+ " 0", " 1", " 2", " 3", " 4", " 5",
+ " 6", " 7", " 8", " 9", " 10", " 11",
+ " 12", " 13", " 14"
+ };
+ char* pre;
+ char* post;
+
+ long peekc = -1;
+ int sargc;
+ int ifile;
+ char** sargv;
+
+ char *funtab[] =
+ {
+ "<1>","<2>","<3>","<4>","<5>",
+ "<6>","<7>","<8>","<9>","<10>",
+ "<11>","<12>","<13>","<14>","<15>",
+ "<16>","<17>","<18>","<19>","<20>",
+ "<21>","<22>","<23>","<24>","<25>",
+ "<26>"
+ };
+ char *atab[] =
+ {
+ "<221>","<222>","<223>","<224>","<225>",
+ "<226>","<227>","<228>","<229>","<230>",
+ "<231>","<232>","<233>","<234>","<235>",
+ "<236>","<237>","<238>","<239>","<240>",
+ "<241>","<242>","<243>","<244>","<245>",
+ "<246>"
+ };
+ char* letr[26] =
+ {
+ "a","b","c","d","e","f","g","h","i","j",
+ "k","l","m","n","o","p","q","r","s","t",
+ "u","v","w","x","y","z"
+ };
+ char* dot = { "." };
+ char* bspace[bsp_max];
+ char** bsp_nxt = bspace;
+ int bdebug = 0;
+ int lflag;
+ int cflag;
+ int sflag;
+
+ char* bundle(int, ...);
+ void conout(char*, char*);
+ int cpeek(int, int, int);
+ int getch(void);
+ char* geta(char*);
+ char* getf(char*);
+ void getout(void);
+ void output(char*);
+ void pp(char*);
+ void routput(char*);
+ void tp(char*);
+ void yyerror(char*, ...);
+ int yyparse(void);
+
+ typedef void* pointer;
+ #pragma varargck type "lx" pointer
+
+%}
+%union
+{
+ char* cptr;
+ int cc;
+}
+
+%type <cptr> pstat stat stat1 def slist dlets e ase nase
+%type <cptr> slist re fprefix cargs eora cons constant lora
+%type <cptr> crs
+
+%token <cptr> LETTER EQOP _AUTO DOT
+%token <cc> DIGIT SQRT LENGTH _IF FFF EQ
+%token <cc> _PRINT _WHILE _FOR NE LE GE INCR DECR
+%token <cc> _RETURN _BREAK _DEFINE BASE OBASE SCALE
+%token <cc> QSTR ERROR
+
+%right '=' EQOP
+%left '+' '-'
+%left '*' '/' '%'
+%right '^'
+%left UMINUS
+
+%%
+start:
+ start stuff
+| stuff
+
+stuff:
+ pstat tail
+ {
+ output($1);
+ }
+| def dargs ')' '{' dlist slist '}'
+ {
+ ttp = bundle(6, pre, $6, post , "0", numb[lev], "Q");
+ conout(ttp, (char*)$1);
+ rcrs = crs;
+ output("");
+ lev = bindx = 0;
+ }
+
+dlist:
+ tail
+| dlist _AUTO dlets tail
+
+stat:
+ stat1
+| nase
+ {
+ if(sflag)
+ bundle(2, $1, "s.");
+ }
+
+pstat:
+ stat1
+ {
+ if(sflag)
+ bundle(2, $1, "0");
+ }
+| nase
+ {
+ if(!sflag)
+ bundle(2, $1, "ps.");
+ }
+
+stat1:
+ {
+ bundle(1, "");
+ }
+| ase
+ {
+ bundle(2, $1, "s.");
+ }
+| SCALE '=' e
+ {
+ bundle(2, $3, "k");
+ }
+| SCALE EQOP e
+ {
+ bundle(4, "K", $3, $2, "k");
+ }
+| BASE '=' e
+ {
+ bundle(2, $3, "i");
+ }
+| BASE EQOP e
+ {
+ bundle(4, "I", $3, $2, "i");
+ }
+| OBASE '=' e
+ {
+ bundle(2, $3, "o");
+ }
+| OBASE EQOP e
+ {
+ bundle(4, "O", $3, $2, "o");
+ }
+| QSTR
+ {
+ bundle(3, "[", $1, "]P");
+ }
+| _BREAK
+ {
+ bundle(2, numb[lev-bstack[bindx-1]], "Q");
+ }
+| _PRINT e
+ {
+ bundle(2, $2, "ps.");
+ }
+| _RETURN e
+ {
+ bundle(4, $2, post, numb[lev], "Q");
+ }
+| _RETURN
+ {
+ bundle(4, "0", post, numb[lev], "Q");
+ }
+| '{' slist '}'
+ {
+ $$ = $2;
+ }
+| FFF
+ {
+ bundle(1, "fY");
+ }
+| _IF crs BLEV '(' re ')' stat
+ {
+ conout($7, $2);
+ bundle(3, $5, $2, " ");
+ }
+| _WHILE crs '(' re ')' stat BLEV
+ {
+ bundle(3, $6, $4, $2);
+ conout($$, $2);
+ bundle(3, $4, $2, " ");
+ }
+| fprefix crs re ';' e ')' stat BLEV
+ {
+ bundle(5, $7, $5, "s.", $3, $2);
+ conout($$, $2);
+ bundle(5, $1, "s.", $3, $2, " ");
+ }
+| '~' LETTER '=' e
+ {
+ bundle(3, $4, "S", $2);
+ }
+
+fprefix:
+ _FOR '(' e ';'
+ {
+ $$ = $3;
+ }
+
+BLEV:
+ =
+ {
+ --bindx;
+ }
+
+slist:
+ stat
+| slist tail stat
+ {
+ bundle(2, $1, $3);
+ }
+
+tail:
+ '\n'
+ {
+ ln++;
+ }
+| ';'
+
+re:
+ e EQ e
+ {
+ $$ = bundle(3, $1, $3, "=");
+ }
+| e '<' e
+ {
+ bundle(3, $1, $3, ">");
+ }
+| e '>' e
+ {
+ bundle(3, $1, $3, "<");
+ }
+| e NE e
+ {
+ bundle(3, $1, $3, "!=");
+ }
+| e GE e
+ {
+ bundle(3, $1, $3, "!>");
+ }
+| e LE e
+ {
+ bundle(3, $1, $3, "!<");
+ }
+| e
+ {
+ bundle(2, $1, " 0!=");
+ }
+
+nase:
+ '(' e ')'
+ {
+ $$ = $2;
+ }
+| cons
+ {
+ bundle(3, " ", $1, " ");
+ }
+| DOT cons
+ {
+ bundle(3, " .", $2, " ");
+ }
+| cons DOT cons
+ {
+ bundle(5, " ", $1, ".", $3, " ");
+ }
+| cons DOT
+ {
+ bundle(4, " ", $1, ".", " ");
+ }
+| DOT
+ {
+ $<cptr>$ = "l.";
+ }
+| LETTER '[' e ']'
+ {
+ bundle(3, $3, ";", geta($1));
+ }
+| LETTER INCR
+ {
+ bundle(4, "l", $1, "d1+s", $1);
+ }
+| INCR LETTER
+ {
+ bundle(4, "l", $2, "1+ds", $2);
+ }
+| DECR LETTER
+ {
+ bundle(4, "l", $2, "1-ds", $2);
+ }
+| LETTER DECR
+ {
+ bundle(4, "l", $1, "d1-s", $1);
+ }
+| LETTER '[' e ']' INCR
+ {
+ bundle(7, $3, ";", geta($1), "d1+" ,$3, ":" ,geta($1));
+ }
+| INCR LETTER '[' e ']'
+ {
+ bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2));
+ }
+| LETTER '[' e ']' DECR
+ {
+ bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1));
+ }
+| DECR LETTER '[' e ']'
+ {
+ bundle(7, $4, ";", geta($2), "1-d", $4, ":" ,geta($2));
+ }
+| SCALE INCR
+ {
+ bundle(1, "Kd1+k");
+ }
+| INCR SCALE
+ {
+ bundle(1, "K1+dk");
+ }
+| SCALE DECR
+ {
+ bundle(1, "Kd1-k");
+ }
+| DECR SCALE
+ {
+ bundle(1, "K1-dk");
+ }
+| BASE INCR
+ {
+ bundle(1, "Id1+i");
+ }
+| INCR BASE
+ {
+ bundle(1, "I1+di");
+ }
+| BASE DECR
+ {
+ bundle(1, "Id1-i");
+ }
+| DECR BASE
+ {
+ bundle(1, "I1-di");
+ }
+| OBASE INCR
+ {
+ bundle(1, "Od1+o");
+ }
+| INCR OBASE
+ {
+ bundle(1, "O1+do");
+ }
+| OBASE DECR
+ {
+ bundle(1, "Od1-o");
+ }
+| DECR OBASE
+ {
+ bundle(1, "O1-do");
+ }
+| LETTER '(' cargs ')'
+ {
+ bundle(4, $3, "l", getf($1), "x");
+ }
+| LETTER '(' ')'
+ {
+ bundle(3, "l", getf($1), "x");
+ }
+| LETTER = {
+ bundle(2, "l", $1);
+ }
+| LENGTH '(' e ')'
+ {
+ bundle(2, $3, "Z");
+ }
+| SCALE '(' e ')'
+ {
+ bundle(2, $3, "X");
+ }
+| '?'
+ {
+ bundle(1, "?");
+ }
+| SQRT '(' e ')'
+ {
+ bundle(2, $3, "v");
+ }
+| '~' LETTER
+ {
+ bundle(2, "L", $2);
+ }
+| SCALE
+ {
+ bundle(1, "K");
+ }
+| BASE
+ {
+ bundle(1, "I");
+ }
+| OBASE
+ {
+ bundle(1, "O");
+ }
+| '-' e
+ {
+ bundle(3, " 0", $2, "-");
+ }
+| e '+' e
+ {
+ bundle(3, $1, $3, "+");
+ }
+| e '-' e
+ {
+ bundle(3, $1, $3, "-");
+ }
+| e '*' e
+ {
+ bundle(3, $1, $3, "*");
+ }
+| e '/' e
+ {
+ bundle(3, $1, $3, "/");
+ }
+| e '%' e
+ {
+ bundle(3, $1, $3, "%%");
+ }
+| e '^' e
+ {
+ bundle(3, $1, $3, "^");
+ }
+
+ase:
+ LETTER '=' e
+ {
+ bundle(3, $3, "ds", $1);
+ }
+| LETTER '[' e ']' '=' e
+ {
+ bundle(5, $6, "d", $3, ":", geta($1));
+ }
+| LETTER EQOP e
+ {
+ bundle(6, "l", $1, $3, $2, "ds", $1);
+ }
+| LETTER '[' e ']' EQOP e
+ {
+ bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":", geta($1));
+ }
+
+e:
+ ase
+| nase
+
+cargs:
+ eora
+| cargs ',' eora
+ {
+ bundle(2, $1, $3);
+ }
+
+eora:
+ e
+| LETTER '[' ']'
+ {
+ bundle(2, "l", geta($1));
+ }
+
+cons:
+ constant
+ {
+ *cp++ = 0;
+ }
+
+constant:
+ '_'
+ {
+ $<cptr>$ = cp;
+ *cp++ = '_';
+ }
+| DIGIT
+ {
+ $<cptr>$ = cp;
+ *cp++ = $1;
+ }
+| constant DIGIT
+ {
+ *cp++ = $2;
+ }
+
+crs:
+ =
+ {
+ $$ = cp;
+ *cp++ = '<';
+ *cp++ = crs/100+'0';
+ *cp++ = (crs%100)/10+'0';
+ *cp++ = crs%10+'0';
+ *cp++ = '>';
+ *cp++ = '\0';
+ if(crs++ >= 220) {
+ yyerror("program too big");
+ getout();
+ }
+ bstack[bindx++] = lev++;
+ }
+
+def:
+ _DEFINE LETTER '('
+ {
+ $$ = getf($2);
+ pre = (char*)"";
+ post = (char*)"";
+ lev = 1;
+ bindx = 0;
+ bstack[bindx] = 0;
+ }
+
+dargs:
+| lora
+ {
+ pp((char*)$1);
+ }
+| dargs ',' lora
+ {
+ pp((char*)$3);
+ }
+
+dlets:
+ lora
+ {
+ tp((char*)$1);
+ }
+| dlets ',' lora
+ {
+ tp((char*)$3);
+ }
+
+lora:
+ LETTER
+ {
+ $<cptr>$=$1;
+ }
+| LETTER '[' ']'
+ {
+ $$ = geta($1);
+ }
+
+%%
+
+int
+yylex(void)
+{
+ int c, ch;
+
+restart:
+ c = getch();
+ peekc = -1;
+ while(c == ' ' || c == '\t')
+ c = getch();
+ if(c == '\\') {
+ getch();
+ goto restart;
+ }
+ if(c >= 'a' && c <= 'z') {
+ /* look ahead to look for reserved words */
+ peekc = getch();
+ if(peekc >= 'a' && peekc <= 'z') { /* must be reserved word */
+ if(c=='p' && peekc=='r') {
+ c = _PRINT;
+ goto skip;
+ }
+ if(c=='i' && peekc=='f') {
+ c = _IF;
+ goto skip;
+ }
+ if(c=='w' && peekc=='h') {
+ c = _WHILE;
+ goto skip;
+ }
+ if(c=='f' && peekc=='o') {
+ c = _FOR;
+ goto skip;
+ }
+ if(c=='s' && peekc=='q') {
+ c = SQRT;
+ goto skip;
+ }
+ if(c=='r' && peekc=='e') {
+ c = _RETURN;
+ goto skip;
+ }
+ if(c=='b' && peekc=='r') {
+ c = _BREAK;
+ goto skip;
+ }
+ if(c=='d' && peekc=='e') {
+ c = _DEFINE;
+ goto skip;
+ }
+ if(c=='s' && peekc=='c') {
+ c = SCALE;
+ goto skip;
+ }
+ if(c=='b' && peekc=='a') {
+ c = BASE;
+ goto skip;
+ }
+ if(c=='i' && peekc=='b') {
+ c = BASE;
+ goto skip;
+ }
+ if(c=='o' && peekc=='b') {
+ c = OBASE;
+ goto skip;
+ }
+ if(c=='d' && peekc=='i') {
+ c = FFF;
+ goto skip;
+ }
+ if(c=='a' && peekc=='u') {
+ c = _AUTO;
+ goto skip;
+ }
+ if(c=='l' && peekc=='e') {
+ c = LENGTH;
+ goto skip;
+ }
+ if(c=='q' && peekc=='u')
+ getout();
+ /* could not be found */
+ return ERROR;
+
+ skip: /* skip over rest of word */
+ peekc = -1;
+ for(;;) {
+ ch = getch();
+ if(ch < 'a' || ch > 'z')
+ break;
+ }
+ peekc = ch;
+ return c;
+ }
+
+ /* usual case; just one single letter */
+ yylval.cptr = letr[c-'a'];
+ return LETTER;
+ }
+ if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
+ yylval.cc = c;
+ return DIGIT;
+ }
+ switch(c) {
+ case '.':
+ return DOT;
+ case '*':
+ yylval.cptr = "*";
+ return cpeek('=', EQOP, c);
+ case '%':
+ yylval.cptr = "%%";
+ return cpeek('=', EQOP, c);
+ case '^':
+ yylval.cptr = "^";
+ return cpeek('=', EQOP, c);
+ case '+':
+ ch = cpeek('=', EQOP, c);
+ if(ch == EQOP) {
+ yylval.cptr = "+";
+ return ch;
+ }
+ return cpeek('+', INCR, c);
+ case '-':
+ ch = cpeek('=', EQOP, c);
+ if(ch == EQOP) {
+ yylval.cptr = "-";
+ return ch;
+ }
+ return cpeek('-', DECR, c);
+ case '=':
+ return cpeek('=', EQ, '=');
+ case '<':
+ return cpeek('=', LE, '<');
+ case '>':
+ return cpeek('=', GE, '>');
+ case '!':
+ return cpeek('=', NE, '!');
+ case '/':
+ ch = cpeek('=', EQOP, c);
+ if(ch == EQOP) {
+ yylval.cptr = "/";
+ return ch;
+ }
+ if(peekc == '*') {
+ peekc = -1;
+ for(;;) {
+ ch = getch();
+ if(ch == '*') {
+ peekc = getch();
+ if(peekc == '/') {
+ peekc = -1;
+ goto restart;
+ }
+ }
+ }
+ }
+ return c;
+ case '"':
+ yylval.cptr = str;
+ while((c=getch()) != '"'){
+ *str++ = c;
+ if(str >= &string[999]){
+ yyerror("string space exceeded");
+ getout();
+ }
+ }
+ *str++ = 0;
+ return QSTR;
+ default:
+ return c;
+ }
+}
+
+int
+cpeek(int c, int yes, int no)
+{
+
+ peekc = getch();
+ if(peekc == c) {
+ peekc = -1;
+ return yes;
+ }
+ return no;
+}
+
+int
+getch(void)
+{
+ long ch;
+
+loop:
+ ch = peekc;
+ if(ch < 0){
+ if(in == 0)
+ ch = -1;
+ else
+ ch = Bgetc(in);
+ }
+ peekc = -1;
+ if(ch >= 0)
+ return ch;
+
+ ifile++;
+ if(ifile >= sargc) {
+ if(ifile >= sargc+1)
+ getout();
+ in = &bstdin;
+ Binit(in, 0, OREAD);
+ ln = 0;
+ goto loop;
+ }
+ if(in)
+ Bterm(in);
+ if((in = Bopen(sargv[ifile], OREAD)) != 0){
+ ln = 0;
+ ss = sargv[ifile];
+ goto loop;
+ }
+ fprint(2, "open %s: %r\n", sargv[ifile]);
+ yyerror("cannot open input file");
+ return 0; /* shut up ken */
+}
+
+char*
+bundle(int a, ...)
+{
+ int i;
+ char **q;
+ va_list arg;
+
+ i = a;
+ va_start(arg, a);
+ q = bsp_nxt;
+ if(bdebug)
+ fprint(2, "bundle %d elements at %lx\n", i, q);
+ while(i-- > 0) {
+ if(bsp_nxt >= &bspace[bsp_max])
+ yyerror("bundling space exceeded");
+ *bsp_nxt++ = va_arg(arg, char*);
+ }
+ *bsp_nxt++ = 0;
+ va_end(arg);
+ yyval.cptr = (char*)q;
+ return (char*)q;
+}
+
+void
+routput(char *p)
+{
+ char **pp;
+
+ if(bdebug)
+ fprint(2, "routput(%lx)\n", p);
+ if((char**)p >= &bspace[0] && (char**)p < &bspace[bsp_max]) {
+ /* part of a bundle */
+ pp = (char**)p;
+ while(*pp != 0)
+ routput(*pp++);
+ } else
+ Bprint(&bstdout, p); /* character string */
+}
+
+void
+output(char *p)
+{
+ routput(p);
+ bsp_nxt = &bspace[0];
+ Bprint(&bstdout, "\n");
+ Bflush(&bstdout);
+ cp = cary;
+ crs = rcrs;
+}
+
+void
+conout(char *p, char *s)
+{
+ Bprint(&bstdout, "[");
+ routput(p);
+ Bprint(&bstdout, "]s%s\n", s);
+ Bflush(&bstdout);
+ lev--;
+}
+
+void
+yyerror(char *s, ...)
+{
+ if(ifile > sargc)
+ ss = "teletype";
+ Bprint(&bstdout, "c[%s:%d, %s]pc\n", s, ln+1, ss);
+ Bflush(&bstdout);
+ cp = cary;
+ crs = rcrs;
+ bindx = 0;
+ lev = 0;
+ bsp_nxt = &bspace[0];
+}
+
+void
+pp(char *s)
+{
+ /* puts the relevant stuff on pre and post for the letter s */
+ bundle(3, "S", s, pre);
+ pre = yyval.cptr;
+ bundle(4, post, "L", s, "s.");
+ post = yyval.cptr;
+}
+
+void
+tp(char *s)
+{
+ /* same as pp, but for temps */
+ bundle(3, "0S", s, pre);
+ pre = yyval.cptr;
+ bundle(4, post, "L", s, "s.");
+ post = yyval.cptr;
+}
+
+void
+yyinit(int argc, char **argv)
+{
+ Binit(&bstdout, 1, OWRITE);
+ sargv = argv;
+ sargc = argc;
+ if(sargc == 0) {
+ in = &bstdin;
+ Binit(in, 0, OREAD);
+ } else if((in = Bopen(sargv[0], OREAD)) == 0)
+ yyerror("cannot open input file");
+ ifile = 0;
+ ln = 0;
+ ss = sargv[0];
+}
+
+void
+getout(void)
+{
+ Bprint(&bstdout, "q");
+ Bflush(&bstdout);
+ exits(0);
+}
+
+char*
+getf(char *p)
+{
+ return funtab[*p - 'a'];
+}
+
+char*
+geta(char *p)
+{
+ return atab[*p - 'a'];
+}
+
+void
+main(int argc, char **argv)
+{
+ int p[2];
+
+ ARGBEGIN{
+ case 'd':
+ bdebug++;
+ break;
+ case 'c':
+ cflag++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 's':
+ sflag++;
+ break;
+ default:
+ fprint(2, "Usage: bc [-l] [-c] [file ...]\n");
+ exits("usage");
+ }ARGEND
+
+ if(lflag) {
+ argc++;
+ argv--;
+ *argv = unsharp("#9/lib/bclib");
+ }
+ if(cflag) {
+ yyinit(argc, argv);
+ for(;;)
+ yyparse();
+ exits(0);
+ }
+ pipe(p);
+ if(fork() == 0) {
+ dup(p[1], 1);
+ close(p[0]);
+ close(p[1]);
+ yyinit(argc, argv);
+ for(;;)
+ yyparse();
+ }
+ dup(p[0], 0);
+ close(p[0]);
+ close(p[1]);
+ execl(unsharp("#9/bin/dc"), "dc", nil);
+}
diff --git a/bin/9base/bc/bclib b/bin/9base/bc/bclib
new file mode 100644
index 00000000..97b1c92e
--- /dev/null
+++ b/bin/9base/bc/bclib
@@ -0,0 +1,246 @@
+scale = 50
+define e(x) {
+ auto a, b, c, d, e, g, w, y, t, r
+
+ r = ibase
+ ibase = A
+
+ t = scale
+ scale = t + .434*x + 1
+
+ w = 0
+ if(x<0) {
+ x = -x
+ w = 1
+ }
+ y = 0
+ while(x>2) {
+ x /= 2
+ y++
+ }
+
+ a = 1
+ b = 1
+ c = b
+ d = 1
+ e = 1
+ for(a=1; 1; a++) {
+ b *= x
+ c = c*a+b
+ d *= a
+ g = c/d
+ if(g == e) {
+ g = g/1
+ while(y--) {
+ g *= g
+ }
+ scale = t
+ if(w==1) {
+ ibase = r
+ return 1/g
+ }
+ ibase = r
+ return g/1
+ }
+ e = g
+ }
+}
+
+define l(x) {
+ auto a, b, c, d, e, f, g, u, s, t, r, z
+
+ r = ibase
+ ibase = A
+ if(x <= 0) {
+ z = 1-10^scale
+ ibase = r
+ return z
+ }
+ t = scale
+
+ f = 1
+ scale += scale(x) - length(x) + 1
+ s = scale
+ while(x > 2) {
+ s += (length(x)-scale(x))/2 + 1
+ if(s>0) {
+ scale = s
+ }
+ x = sqrt(x)
+ f *= 2
+ }
+ while(x < .5) {
+ s += (length(x)-scale(x))/2 + 1
+ if(s>0) {
+ scale = s
+ }
+ x = sqrt(x)
+ f *= 2
+ }
+
+ scale = t + length(f) - scale(f) + 1
+ u = (x-1)/(x+1)
+
+ scale += 1.1*length(t) - 1.1*scale(t)
+ s = u*u
+ b = 2*f
+ c = b
+ d = 1
+ e = 1
+ for(a=3; 1; a=a+2){
+ b *= s
+ c = c*a + d*b
+ d *= a
+ g = c/d
+ if(g==e) {
+ scale = t
+ ibase = r
+ return u*c/d
+ }
+ e = g
+ }
+}
+
+define s(x) {
+ auto a, b, c, s, t, y, p, n, i, r
+
+ r = ibase
+ ibase = A
+ t = scale
+ y = x/.7853
+ s = t + length(y) - scale(y)
+ if(s<t) {
+ s = t
+ }
+ scale = s
+ p = a(1)
+
+ scale = 0
+ if(x>=0) {
+ n = (x/(2*p)+1)/2
+ }
+ if(x<0) {
+ n = (x/(2*p)-1)/2
+ }
+ x -= 4*n*p
+ if(n%2 != 0) {
+ x = -x
+ }
+
+ scale = t + length(1.2*t) - scale(1.2*t)
+ y = -x*x
+ a = x
+ b = 1
+ s = x
+ for(i=3; 1; i+=2) {
+ a *= y
+ b *= i*(i-1)
+ c = a/b
+ if(c==0){
+ scale = t
+ ibase = r
+ return s/1
+ }
+ s += c
+ }
+}
+
+define c(x) {
+ auto t, r
+
+ r = ibase
+ ibase = A
+ t = scale
+ scale = scale+1
+ x = s(x + 2*a(1))
+ scale = t
+ ibase = r
+ return x/1
+}
+
+define a(x) {
+ auto a, b, c, d, e, f, g, s, t, r, z
+
+ r = ibase
+ ibase = A
+ if(x==0) {
+ return 0
+ }
+ if(x==1) {
+ z = .7853981633974483096156608458198757210492923498437764/1
+ ibase = r
+ if(scale<52) {
+ return z
+ }
+ }
+ t = scale
+ f = 1
+ while(x > .5) {
+ scale++
+ x = -(1 - sqrt(1.+x*x))/x
+ f *= 2
+ }
+ while(x < -.5) {
+ scale++
+ x = -(1 - sqrt(1.+x*x))/x
+ f *= 2
+ }
+ s = -x*x
+ b = f
+ c = f
+ d = 1
+ e = 1
+ for(a=3; 1; a+=2) {
+ b *= s
+ c = c*a + d*b
+ d *= a
+ g = c/d
+ if(g==e) {
+ scale = t
+ ibase = r
+ return x*c/d
+ }
+ e = g
+ }
+}
+
+define j(n,x) {
+ auto a,b,c,d,e,g,i,s,k,t,r
+
+ r = ibase
+ ibase = A
+
+ t = scale
+ k = 1.36*x + 1.16*t - n
+ k = length(k) - scale(k)
+ if(k>0) {
+ scale += k
+ }
+
+ s = -x*x/4
+ if(n<0) {
+ n = -n
+ x = -x
+ }
+ a = 1
+ c = 1
+ for(i=1; i<=n; i++) {
+ a *= x
+ c *= 2*i
+ }
+ b = a
+ d = 1
+ e = 1
+ for(i=1; 1; i++) {
+ a *= s
+ b = b*i*(n+i) + a
+ c *= i*(n+i)
+ g = b/c
+ if(g==e) {
+ scale = t
+ ibase = r
+ return g/1
+ }
+ e = g
+ }
+}
diff --git a/bin/9base/cal/Makefile b/bin/9base/cal/Makefile
new file mode 100644
index 00000000..50163119
--- /dev/null
+++ b/bin/9base/cal/Makefile
@@ -0,0 +1,10 @@
+# cal - cal unix port from plan9
+# Depends on ../lib9
+
+TARG = cal
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/cal/cal.1 b/bin/9base/cal/cal.1
new file mode 100644
index 00000000..2ccb24f8
--- /dev/null
+++ b/bin/9base/cal/cal.1
@@ -0,0 +1,46 @@
+.TH CAL 1
+.SH NAME
+cal \- print calendar
+.SH SYNOPSIS
+.B cal
+[
+.I month
+]
+[
+.I year
+]
+.SH DESCRIPTION
+.I Cal
+prints a calendar.
+.I Month
+is either a number from 1 to 12,
+a lower case month name,
+or a lower case three-letter prefix of a month name.
+.I Year
+can be between 1
+and 9999.
+If either
+.I month
+or
+.I year
+is omitted, the current month or year is used.
+If only one argument is given, and it is a number larger than 12,
+a calendar for all twelve months of the given year is produced;
+otherwise a calendar for just one month is printed.
+The calendar
+produced is that for England and her colonies.
+.PP
+Try
+.EX
+ cal sep 1752
+.EE
+.SH SOURCE
+.B \*9/src/cmd/cal.c
+.SH BUGS
+The year is always considered to start in January even though this
+is historically naive.
+.PP
+Beware that
+.L "cal 90"
+refers to the early Christian era,
+not the 20th century.
diff --git a/bin/9base/cal/cal.c b/bin/9base/cal/cal.c
new file mode 100644
index 00000000..36dd9dd8
--- /dev/null
+++ b/bin/9base/cal/cal.c
@@ -0,0 +1,313 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+char dayw[] =
+{
+ " S M Tu W Th F S"
+};
+char *smon[] =
+{
+ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December",
+};
+char mon[] =
+{
+ 0,
+ 31, 29, 31, 30,
+ 31, 30, 31, 31,
+ 30, 31, 30, 31,
+};
+char string[432];
+Biobuf bout;
+
+void main(int argc, char *argv[]);
+int number(char *str);
+void pstr(char *str, int n);
+void cal(int m, int y, char *p, int w);
+int jan1(int yr);
+int curmo(void);
+int curyr(void);
+
+void
+main(int argc, char *argv[])
+{
+ int y, i, j, m;
+
+ if(argc > 3) {
+ fprint(2, "usage: cal [month] [year]\n");
+ exits("usage");
+ }
+ Binit(&bout, 1, OWRITE);
+
+/*
+ * no arg, print current month
+ */
+ if(argc == 1) {
+ m = curmo();
+ y = curyr();
+ goto xshort;
+ }
+
+/*
+ * one arg
+ * if looks like a month, print month
+ * else print year
+ */
+ if(argc == 2) {
+ y = number(argv[1]);
+ if(y < 0)
+ y = -y;
+ if(y >= 1 && y <= 12) {
+ m = y;
+ y = curyr();
+ goto xshort;
+ }
+ goto xlong;
+ }
+
+/*
+ * two arg, month and year
+ */
+ m = number(argv[1]);
+ if(m < 0)
+ m = -m;
+ y = number(argv[2]);
+ goto xshort;
+
+/*
+ * print out just month
+ */
+xshort:
+ if(m < 1 || m > 12)
+ goto badarg;
+ if(y < 1 || y > 9999)
+ goto badarg;
+ Bprint(&bout, " %s %d\n", smon[m-1], y);
+ Bprint(&bout, "%s\n", dayw);
+ cal(m, y, string, 24);
+ for(i=0; i<6*24; i+=24)
+ pstr(string+i, 24);
+ exits(0);
+
+/*
+ * print out complete year
+ */
+xlong:
+ y = number(argv[1]);
+ if(y<1 || y>9999)
+ goto badarg;
+ Bprint(&bout, "\n\n\n");
+ Bprint(&bout, " %d\n", y);
+ Bprint(&bout, "\n");
+ for(i=0; i<12; i+=3) {
+ for(j=0; j<6*72; j++)
+ string[j] = '\0';
+ Bprint(&bout, " %.3s", smon[i]);
+ Bprint(&bout, " %.3s", smon[i+1]);
+ Bprint(&bout, " %.3s\n", smon[i+2]);
+ Bprint(&bout, "%s %s %s\n", dayw, dayw, dayw);
+ cal(i+1, y, string, 72);
+ cal(i+2, y, string+23, 72);
+ cal(i+3, y, string+46, 72);
+ for(j=0; j<6*72; j+=72)
+ pstr(string+j, 72);
+ }
+ Bprint(&bout, "\n\n\n");
+ exits(0);
+
+badarg:
+ Bprint(&bout, "cal: bad argument\n");
+}
+
+struct
+{
+ char* word;
+ int val;
+} dict[] =
+{
+ "jan", 1,
+ "january", 1,
+ "feb", 2,
+ "february", 2,
+ "mar", 3,
+ "march", 3,
+ "apr", 4,
+ "april", 4,
+ "may", 5,
+ "jun", 6,
+ "june", 6,
+ "jul", 7,
+ "july", 7,
+ "aug", 8,
+ "august", 8,
+ "sep", 9,
+ "sept", 9,
+ "september", 9,
+ "oct", 10,
+ "october", 10,
+ "nov", 11,
+ "november", 11,
+ "dec", 12,
+ "december", 12,
+ 0
+};
+
+/*
+ * convert to a number.
+ * if its a dictionary word,
+ * return negative number
+ */
+int
+number(char *str)
+{
+ int n, c;
+ char *s;
+
+ for(n=0; s=dict[n].word; n++)
+ if(strcmp(s, str) == 0)
+ return -dict[n].val;
+ n = 0;
+ s = str;
+ while(c = *s++) {
+ if(c<'0' || c>'9')
+ return 0;
+ n = n*10 + c-'0';
+ }
+ return n;
+}
+
+void
+pstr(char *str, int n)
+{
+ int i;
+ char *s;
+
+ s = str;
+ i = n;
+ while(i--)
+ if(*s++ == '\0')
+ s[-1] = ' ';
+ i = n+1;
+ while(i--)
+ if(*--s != ' ')
+ break;
+ s[1] = '\0';
+ Bprint(&bout, "%s\n", str);
+}
+
+void
+cal(int m, int y, char *p, int w)
+{
+ int d, i;
+ char *s;
+
+ s = p;
+ d = jan1(y);
+ mon[2] = 29;
+ mon[9] = 30;
+
+ switch((jan1(y+1)+7-d)%7) {
+
+ /*
+ * non-leap year
+ */
+ case 1:
+ mon[2] = 28;
+ break;
+
+ /*
+ * 1752
+ */
+ default:
+ mon[9] = 19;
+ break;
+
+ /*
+ * leap year
+ */
+ case 2:
+ ;
+ }
+ for(i=1; i<m; i++)
+ d += mon[i];
+ d %= 7;
+ s += 3*d;
+ for(i=1; i<=mon[m]; i++) {
+ if(i==3 && mon[m]==19) {
+ i += 11;
+ mon[m] += 11;
+ }
+ if(i > 9)
+ *s = i/10+'0';
+ s++;
+ *s++ = i%10+'0';
+ s++;
+ if(++d == 7) {
+ d = 0;
+ s = p+w;
+ p = s;
+ }
+ }
+}
+
+/*
+ * return day of the week
+ * of jan 1 of given year
+ */
+int
+jan1(int yr)
+{
+ int y, d;
+
+/*
+ * normal gregorian calendar
+ * one extra day per four years
+ */
+
+ y = yr;
+ d = 4+y+(y+3)/4;
+
+/*
+ * julian calendar
+ * regular gregorian
+ * less three days per 400
+ */
+
+ if(y > 1800) {
+ d -= (y-1701)/100;
+ d += (y-1601)/400;
+ }
+
+/*
+ * great calendar changeover instant
+ */
+
+ if(y > 1752)
+ d += 3;
+
+ return d%7;
+}
+
+/*
+ * system dependent
+ * get current month and year
+ */
+int
+curmo(void)
+{
+ Tm *tm;
+
+ tm = localtime(time(0));
+ return tm->mon+1;
+}
+
+int
+curyr(void)
+{
+ Tm *tm;
+
+ tm = localtime(time(0));
+ return tm->year+1900;
+}
diff --git a/bin/9base/cat/Makefile b/bin/9base/cat/Makefile
new file mode 100644
index 00000000..5bc2f7a0
--- /dev/null
+++ b/bin/9base/cat/Makefile
@@ -0,0 +1,10 @@
+# cat - cat unix port from plan9
+# Depends on ../lib9
+
+TARG = cat
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/cat/cat.1 b/bin/9base/cat/cat.1
new file mode 100644
index 00000000..0738206a
--- /dev/null
+++ b/bin/9base/cat/cat.1
@@ -0,0 +1,108 @@
+.TH CAT 1
+.SH NAME
+cat, read, nobs \- catenate files
+.SH SYNOPSIS
+.B cat
+[
+.I file ...
+]
+.br
+.B read
+[
+.B -m
+] [
+.B -n
+.I nline
+] [
+.I file ...
+]
+.br
+.B nobs
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Cat
+reads each
+.I file
+in sequence and writes it on the standard output.
+Thus
+.IP
+.L
+cat file
+.LP
+prints a file and
+.IP
+.L
+cat file1 file2 >file3
+.LP
+concatenates the first two files and places the result
+on the third.
+.PP
+If no
+.I file
+is given,
+.I cat
+reads from the standard input.
+Output is buffered in blocks matching the input.
+.PP
+.I Read
+copies to standard output exactly one line from the named
+.IR file ,
+default standard input.
+It is useful in interactive
+.IR rc (1)
+scripts.
+.PP
+The
+.B -m
+flag causes it to continue reading and writing multiple lines until end of file;
+.B -n
+causes it to read no more than
+.I nline
+lines.
+.PP
+.I Read
+always executes a single
+.B write
+for each line of input, which can be helpful when
+preparing input to programs that expect line-at-a-time data.
+It never reads any more data from the input than it prints to the output.
+.PP
+.I Nobs
+copies the named files to
+standard output except that it removes all backspace
+characters and the characters that precede them.
+It is useful to use as
+.B $PAGER
+with the Unix version of
+.IR man (1)
+when run inside a
+.I win
+(see
+.IR acme (1))
+window.
+.SH SOURCE
+.B \*9/src/cmd/cat.c
+.br
+.B \*9/src/cmd/read.c
+.br
+.B \*9/bin/nobs
+.SH SEE ALSO
+.IR cp (1)
+.SH DIAGNOSTICS
+.I Read
+exits with status
+.B eof
+on end of file or, in the
+.B -n
+case, if it doesn't read
+.I nlines
+lines.
+.SH BUGS
+Beware of
+.L "cat a b >a"
+and
+.LR "cat a b >b" ,
+which
+destroy input files before reading them.
diff --git a/bin/9base/cat/cat.c b/bin/9base/cat/cat.c
new file mode 100644
index 00000000..ac588897
--- /dev/null
+++ b/bin/9base/cat/cat.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+
+void
+cat(int f, char *s)
+{
+ char buf[8192];
+ long n;
+
+ while((n=read(f, buf, (long)sizeof buf))>0)
+ if(write(1, buf, n)!=n)
+ sysfatal("write error copying %s: %r", s);
+ if(n < 0)
+ sysfatal("error reading %s: %r", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int f, i;
+
+ argv0 = "cat";
+ if(argc == 1)
+ cat(0, "<stdin>");
+ else for(i=1; i<argc; i++){
+ f = open(argv[i], OREAD);
+ if(f < 0)
+ sysfatal("can't open %s: %r", argv[i]);
+ else{
+ cat(f, argv[i]);
+ close(f);
+ }
+ }
+ exits(0);
+}
+
diff --git a/bin/9base/cleanname/Makefile b/bin/9base/cleanname/Makefile
new file mode 100644
index 00000000..722f39a1
--- /dev/null
+++ b/bin/9base/cleanname/Makefile
@@ -0,0 +1,10 @@
+# cleanname - cleanname unix port from plan9
+# Depends on ../lib9
+
+TARG = cleanname
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/cleanname/cleanname.1 b/bin/9base/cleanname/cleanname.1
new file mode 100644
index 00000000..02ad0baa
--- /dev/null
+++ b/bin/9base/cleanname/cleanname.1
@@ -0,0 +1,32 @@
+.TH CLEANNAME 1
+.SH NAME
+cleanname \- clean a path name
+.SH SYNOPSIS
+.B cleanname
+[
+.B -d
+.I pwd
+]
+.I names ...
+.SH DESCRIPTION
+For each file name argument,
+.IR cleanname ,
+by lexical processing only,
+prints the shortest equivalent string that names the same
+(possibly hypothetical) file.
+It eliminates multiple and trailing slashes, and it lexically
+interprets
+.B .
+and
+.B ..
+directory components in the name.
+If the
+.B -d
+option is present,
+unrooted names are prefixed with
+.IB pwd /
+before processing.
+.SH SOURCE
+.B \*9/src/cmd/cleanname.c
+.SH SEE ALSO
+.IR cleanname (3).
diff --git a/bin/9base/cleanname/cleanname.c b/bin/9base/cleanname/cleanname.c
new file mode 100644
index 00000000..8fa18466
--- /dev/null
+++ b/bin/9base/cleanname/cleanname.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char **argv)
+{
+ char *dir;
+ char *name;
+ int i;
+
+ dir = nil;
+ ARGBEGIN{
+ case 'd':
+ if((dir=ARGF()) == nil)
+ goto Usage;
+ break;
+ default:
+ goto Usage;
+ }ARGEND;
+
+ if(argc < 1) {
+ Usage:
+ fprint(2, "usage: cleanname [-d pwd] name...\n");
+ exits("usage");
+ }
+
+ for(i=0; i<argc; i++) {
+ if(dir == nil || argv[i][0] == '/') {
+ cleanname(argv[i]);
+ print("%s\n", argv[i]);
+ } else {
+ name = malloc(strlen(argv[i])+1+strlen(dir)+1);
+ if(name == nil) {
+ fprint(2, "cleanname: out of memory\n");
+ exits("out of memory");
+ }
+ sprint(name, "%s/%s", dir, argv[i]);
+ cleanname(name);
+ print("%s\n", name);
+ free(name);
+ }
+ }
+ exits(0);
+}
diff --git a/bin/9base/cmp/Makefile b/bin/9base/cmp/Makefile
new file mode 100644
index 00000000..d17fe788
--- /dev/null
+++ b/bin/9base/cmp/Makefile
@@ -0,0 +1,10 @@
+# cmp - cmp unix port from plan9
+# Depends on ../lib9
+
+TARG = cmp
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/cmp/cmp.1 b/bin/9base/cmp/cmp.1
new file mode 100644
index 00000000..a3ab1c9b
--- /dev/null
+++ b/bin/9base/cmp/cmp.1
@@ -0,0 +1,57 @@
+.TH CMP 1
+.SH NAME
+cmp \- compare two files
+.SH SYNOPSIS
+.B cmp
+[
+.B -lsL
+]
+.I file1 file2
+[
+.I offset1
+[
+.I offset2
+]
+]
+.SH DESCRIPTION
+The two files are
+compared.
+A diagnostic results if the contents differ, otherwise
+there is no output.
+.PP
+The options are:
+.TP
+.B l
+Print the byte number (decimal) and the
+differing bytes (hexadecimal) for each difference.
+.TP
+.B s
+Print nothing for differing files,
+but set the exit status.
+.TP
+.B L
+Print the line number of the first differing byte.
+.PP
+If offsets are given,
+comparison starts at the designated byte position
+of the corresponding file.
+Offsets that begin with
+.B 0x
+are hexadecimal;
+with
+.BR 0 ,
+octal; with anything else, decimal.
+.SH SOURCE
+.B \*9/src/cmd/cmp.c
+.SH "SEE ALSO"
+.IR diff (1)
+.SH DIAGNOSTICS
+If a file is inaccessible or missing, the exit status is
+.LR open .
+If the files are the same, the exit status is empty (true).
+If they are the same except that one is longer than the other, the exit status is
+.LR EOF .
+Otherwise
+.I cmp
+reports the position of the first disagreeing byte and the exit status is
+.LR differ .
diff --git a/bin/9base/cmp/cmp.c b/bin/9base/cmp/cmp.c
new file mode 100644
index 00000000..4f69e181
--- /dev/null
+++ b/bin/9base/cmp/cmp.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+
+#define BUF 65536
+
+int sflag = 0;
+int lflag = 0;
+int Lflag = 0;
+
+static void usage(void);
+
+void
+main(int argc, char *argv[])
+{
+ int n, i;
+ uchar *p, *q;
+ uchar buf1[BUF], buf2[BUF];
+ int f1, f2;
+ vlong nc = 1, o, l = 1;
+ char *name1, *name2;
+ uchar *b1s, *b1e, *b2s, *b2e;
+
+ ARGBEGIN{
+ case 's': sflag = 1; break;
+ case 'l': lflag = 1; break;
+ case 'L': Lflag = 1; break;
+ default: usage();
+ }ARGEND
+ if(argc < 2)
+ usage();
+ if((f1 = open(name1 = *argv++, OREAD)) == -1){
+ if(!sflag) perror(name1);
+ exits("open");
+ }
+ if((f2 = open(name2 = *argv++, OREAD)) == -1){
+ if(!sflag) perror(name2);
+ exits("open");
+ }
+ if(*argv){
+ o = strtoll(*argv++, 0, 0);
+ if(seek(f1, o, 0) < 0){
+ if(!sflag) perror("cmp: seek by offset1");
+ exits("seek 1");
+ }
+ }
+ if(*argv){
+ o = strtoll(*argv++, 0, 0);
+ if(seek(f2, o, 0) < 0){
+ if(!sflag) perror("cmp: seek by offset2");
+ exits("seek 2");
+ }
+ }
+ if(*argv)
+ usage();
+ b1s = b1e = buf1;
+ b2s = b2e = buf2;
+ for(;;){
+ if(b1s >= b1e){
+ if(b1s >= &buf1[BUF])
+ b1s = buf1;
+ n = read(f1, b1s, &buf1[BUF] - b1s);
+ b1e = b1s + n;
+ }
+ if(b2s >= b2e){
+ if(b2s >= &buf2[BUF])
+ b2s = buf2;
+ n = read(f2, b2s, &buf2[BUF] - b2s);
+ b2e = b2s + n;
+ }
+ n = b2e - b2s;
+ if(n > b1e - b1s)
+ n = b1e - b1s;
+ if(n <= 0)
+ break;
+ if(memcmp((void *)b1s, (void *)b2s, n) != 0){
+ if(sflag)
+ exits("differ");
+ for(p = b1s, q = b2s, i = 0; i < n; p++, q++, i++) {
+ if(*p == '\n')
+ l++;
+ if(*p != *q){
+ if(!lflag){
+ print("%s %s differ: char %lld",
+ name1, name2, nc+i);
+ print(Lflag?" line %lld\n":"\n", l);
+ exits("differ");
+ }
+ print("%6lld 0x%.2x 0x%.2x\n", nc+i, *p, *q);
+ }
+ }
+ }
+ if(Lflag)
+ for(p = b1s; p < b1e;)
+ if(*p++ == '\n')
+ l++;
+ nc += n;
+ b1s += n;
+ b2s += n;
+ }
+ if(b1e - b1s == b2e - b2s)
+ exits((char *)0);
+ if(!sflag)
+ print("EOF on %s\n", (b1e - b1s > b2e - b2s)? name2 : name1);
+ exits("EOF");
+}
+
+static void
+usage(void)
+{
+ print("Usage: cmp [-lsL] file1 file2 [offset1 [offset2] ]\n");
+ exits("usage");
+}
diff --git a/bin/9base/config.mk b/bin/9base/config.mk
new file mode 100644
index 00000000..1ebfd491
--- /dev/null
+++ b/bin/9base/config.mk
@@ -0,0 +1,25 @@
+# Customize to fit your system
+
+# paths
+PREFIX = /usr/local/plan9
+MANPREFIX = ${PREFIX}/share/man
+
+VERSION = 7
+OBJTYPE = 386
+#OBJTYPE = arm
+#OBJTYPE = x86_64
+#OBJTYPE     = sun4u
+
+# Linux/BSD
+#CFLAGS += -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\""
+CFLAGS += -c -I. -DPLAN9PORT -DPREFIX="\"${PREFIX}\""
+LDFLAGS += -static
+
+# Solaris
+#CFLAGS = -fast -xtarget=ultra -D__sun__ -c -I. -DPREFIX="\"${PREFIX}\""
+#LDFLAGS = -dn
+
+# compiler
+AR = ar rc
+CC = cc
+YACC = ../yacc/9yacc
diff --git a/bin/9base/date/Makefile b/bin/9base/date/Makefile
new file mode 100644
index 00000000..472b7555
--- /dev/null
+++ b/bin/9base/date/Makefile
@@ -0,0 +1,10 @@
+# date - date unix port from plan9
+# Depends on ../lib9
+
+TARG = date
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/date/date.1 b/bin/9base/date/date.1
new file mode 100644
index 00000000..fc89113a
--- /dev/null
+++ b/bin/9base/date/date.1
@@ -0,0 +1,59 @@
+.TH DATE 1
+.SH NAME
+date \- date and time
+.SH SYNOPSIS
+.B date
+[
+.I option
+] [
+.I seconds
+]
+.\" .br
+.\" .B clock
+.SH DESCRIPTION
+Print the date, in the format
+.PP
+.B
+ Tue Aug 16 17:03:52 CDT 1977
+.PP
+The options are
+.TP
+.B -u
+Report Greenwich Mean Time (GMT) rather than local time.
+.TP
+.B -n
+Report the date as the number of seconds since the
+epoch, 00:00:00 GMT, January 1, 1970.
+.TP
+.B -i
+Report the date as ISO-8601 without time and timezone suffix.
+.TP
+.B -t
+Report the date as ISO-8601 with time and timezone suffix.
+.PP
+The conversion from Greenwich Mean Time to local time depends on the
+.B $timezone
+environment variable; see
+.IR ctime (3).
+.PP
+If the optional argument
+.I seconds
+is present, it is used as the time to convert rather than
+the real time.
+.\" .SH FILES
+.\" .TF /adm/timezone/local
+.\" .TP
+.\" .B /env/timezone
+.\" Current timezone name and adjustments.
+.\" .TP
+.\" .B /adm/timezone
+.\" A directory containing timezone tables.
+.\" .TP
+.\" .B /adm/timezone/local
+.\" Default timezone file, copied by
+.\" .IR init (8)
+.\" into
+.\" .BR /env/timezone .
+.\" .PD
+.SH SOURCE
+.B \*9/src/date/date.c
diff --git a/bin/9base/date/date.c b/bin/9base/date/date.c
new file mode 100644
index 00000000..ca0e389f
--- /dev/null
+++ b/bin/9base/date/date.c
@@ -0,0 +1,72 @@
+#include <u.h>
+#include <libc.h>
+
+int uflg, nflg, iflg, tflg;
+
+char*
+isodate(Tm *t)
+{
+ static char c[25]; /* leave room to append isotime */
+ snprint(c, 11, "%04d-%02d-%02d",
+ t->year + 1900, t->mon + 1, t->mday);
+ return c;
+}
+
+char*
+isotime(Tm *t)
+{
+ int tz;
+ char *c, *d;
+ d = isodate(t);
+ c = d+10;
+ snprint(c, 10, "T%02d:%02d:%02d",
+ t->hour, t->min, t->sec); /* append to isodate */
+ tz = t->tzoff / 60;
+ if(t->tzoff) {
+ /* localtime */
+ if (t->tzoff > 0) {
+ c[9] = '+';
+ } else {
+ c[9] = '-';
+ tz = -tz;
+ }
+ snprint(c+10, 5, "%02d%02d", tz / 60, tz % 60);
+ } else {
+ c[9] = 'Z';
+ c[10] = 0;
+ }
+ return d;
+}
+
+void
+main(int argc, char *argv[])
+{
+ ulong now;
+ Tm *tm;
+ ARGBEGIN{
+ case 'n': nflg = 1; break;
+ case 'u': uflg = 1; break;
+ case 't': tflg = 1; /* implies -i */
+ case 'i': iflg = 1; break;
+ default: fprint(2, "usage: date [-itun] [seconds]\n"); exits("usage");
+ }ARGEND
+
+ if(argc == 1)
+ now = strtoul(*argv, 0, 0);
+ else
+ now = time(0);
+
+ if(nflg)
+ print("%ld\n", now);
+ else {
+ tm = uflg ? gmtime(now) : localtime(now);
+ if(iflg) {
+ if(tflg)
+ print("%s\n", isotime(tm));
+ else
+ print("%s\n", isodate(tm));
+ } else
+ print("%s", asctime(tm));
+ }
+ exits(0);
+}
diff --git a/bin/9base/dc/Makefile b/bin/9base/dc/Makefile
new file mode 100644
index 00000000..95dafa63
--- /dev/null
+++ b/bin/9base/dc/Makefile
@@ -0,0 +1,10 @@
+# dc - dc unix port from plan9
+# Depends on ../lib9
+
+TARG = dc
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/dc/dc.1 b/bin/9base/dc/dc.1
new file mode 100644
index 00000000..4f1abca3
--- /dev/null
+++ b/bin/9base/dc/dc.1
@@ -0,0 +1,257 @@
+.TH DC 1
+.SH NAME
+dc \- desk calculator
+.SH SYNOPSIS
+.B dc
+[
+.I file
+]
+.SH DESCRIPTION
+.I Dc
+is an arbitrary precision desk calculator.
+Ordinarily it operates on decimal integers,
+but one may specify an input base, output base,
+and a number of fractional digits to be maintained.
+The overall structure of
+.I dc
+is
+a stacking (reverse Polish) calculator.
+If an argument is given,
+input is taken from that file until its end,
+then from the standard input.
+The following constructions are recognized:
+.TP
+number
+The value of the number is pushed on the stack.
+A number is an unbroken string of the digits
+.B 0-9A-F
+or
+.BR 0-9a-f .
+A hexadecimal number beginning with a lower case
+letter must be preceded by a zero to distinguish it
+from the command associated with the letter.
+It may be preceded by an underscore
+.B _
+to input a
+negative number.
+Numbers may contain decimal points.
+.TP
+.L
++ - / * % ^
+Add
+.LR + ,
+subtract
+.LR - ,
+multiply
+.LR * ,
+divide
+.LR / ,
+remainder
+.LR % ,
+or exponentiate
+.L ^
+the top two values on the stack.
+The two entries are popped off the stack;
+the result is pushed on the stack in their place.
+Any fractional part of an exponent is ignored.
+.TP
+.BI s x
+.br
+.ns
+.TP
+.BI S x
+Pop the top of the stack and store into
+a register named
+.IR x ,
+where
+.I x
+may be any character.
+Under operation
+.B S
+register
+.I x
+is treated as a stack and the value is pushed on it.
+.TP
+.BI l x
+.br
+.ns
+.TP
+.BI L x
+Push the value in register
+.I x
+onto the stack.
+The register
+.I x
+is not altered.
+All registers start with zero value.
+Under operation
+.B L
+register
+.I x
+is treated as a stack and its top value is popped onto the main stack.
+.TP
+.B d
+Duplicate the
+top value on the stack.
+.TP
+.B p
+Print the top value on the stack.
+The top value remains unchanged.
+.B P
+interprets the top of the stack as an
+text
+string,
+removes it, and prints it.
+.TP
+.B f
+Print the values on the stack.
+.TP
+.B q
+.br
+.ns
+.TP
+.B Q
+Exit the program.
+If executing a string, the recursion level is
+popped by two.
+Under operation
+.B Q
+the top value on the stack is popped and the string execution level is popped
+by that value.
+.TP
+.B x
+Treat the top element of the stack as a character string
+and execute it as a string of
+.I dc
+commands.
+.TP
+.B X
+Replace the number on the top of the stack with its scale factor.
+.TP
+.B "[ ... ]"
+Put the bracketed
+text
+string on the top of the stack.
+.TP
+.PD 0
+.BI < x
+.TP
+.BI > x
+.TP
+.BI = x
+.PD
+Pop and compare the
+top two elements of the stack.
+Register
+.I x
+is executed if they obey the stated
+relation.
+.TP
+.B v
+Replace the top element on the stack by its square root.
+Any existing fractional part of the argument is taken
+into account, but otherwise the scale factor is ignored.
+.TP
+.B !
+Interpret the rest of the line as a shell command.
+.TP
+.B c
+Clear the stack.
+.TP
+.B i
+The top value on the stack is popped and used as the
+number base for further input.
+.TP
+.B I
+Push the input base on the top of the stack.
+.TP
+.B o
+The top value on the stack is popped and used as the
+number base for further output.
+In bases larger than 10, each `digit' prints as a group of decimal digits.
+.TP
+.B O
+Push the output base on the top of the stack.
+.TP
+.B k
+Pop the top of the stack, and use that value as
+a non-negative scale factor:
+the appropriate number of places
+are printed on output,
+and maintained during multiplication, division, and exponentiation.
+The interaction of scale factor,
+input base, and output base will be reasonable if all are changed
+together.
+.TP
+.B z
+Push the stack level onto the stack.
+.TP
+.B Z
+Replace the number on the top of the stack with its length.
+.TP
+.B ?
+A line of input is taken from the input source (usually the terminal)
+and executed.
+.TP
+.B "; :"
+Used by
+.I bc
+for array operations.
+.PP
+The scale factor set by
+.B k
+determines how many digits are kept to the right of
+the decimal point.
+If
+.I s
+is the current scale factor,
+.I sa
+is the scale of the first operand,
+.I sb
+is the scale of the second,
+and
+.I b
+is the (integer) second operand,
+results are truncated to the following scales.
+.IP
+.nf
+\fL+\fR,\fL-\fR max(\fIsa,sb\fR)
+\fL*\fR min(\fIsa\fR+\fIsb \fR, max\fR(\fIs,sa,sb\fR))
+\fL/\fI s
+\fL%\fR so that dividend = divisor*quotient + remainder; remainder has sign of dividend
+\fL^\fR min(\fIsa\fR\(mu|\fIb\fR|, max(\fIs,sa\fR))
+\fLv\fR max(\fIs,sa\fR)
+.fi
+.SH EXAMPLES
+.LP
+Print the first ten values of
+.IR n !
+.IP
+.EX
+[la1+dsa*pla10>y]sy
+0sa1
+lyx
+.EE
+.SH SOURCE
+.B \*9/src/cmd/dc.c
+.SH "SEE ALSO"
+.IR bc (1),
+.IR hoc (1)
+.SH DIAGNOSTICS
+.I x
+.LR "is unimplemented" ,
+where
+.I x
+is an octal number: an internal error.
+.br
+`Out of headers'
+for too many numbers being kept around.
+.br
+`Nesting depth'
+for too many levels of nested execution.
+.SH BUGS
+When the input base exceeds 16,
+there is no notation for digits greater than
+.BR F .
+.PP
+Past its time.
diff --git a/bin/9base/dc/dc.c b/bin/9base/dc/dc.c
new file mode 100644
index 00000000..bc69ca50
--- /dev/null
+++ b/bin/9base/dc/dc.c
@@ -0,0 +1,2287 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+typedef void* pointer;
+
+#define div dcdiv
+
+#define FATAL 0
+#define NFATAL 1
+#define BLK sizeof(Blk)
+#define PTRSZ sizeof(int*)
+#define HEADSZ 1024
+#define STKSZ 100
+#define RDSKSZ 100
+#define TBLSZ 256
+#define ARRAYST 221
+#define MAXIND 2048
+#define NL 1
+#define NG 2
+#define NE 3
+#define length(p) ((p)->wt-(p)->beg)
+#define rewind(p) (p)->rd=(p)->beg
+#undef create
+#define create(p) (p)->rd = (p)->wt = (p)->beg
+#define fsfile(p) (p)->rd = (p)->wt
+#define truncate(p) (p)->wt = (p)->rd
+#define sfeof(p) (((p)->rd==(p)->wt)?1:0)
+#define sfbeg(p) (((p)->rd==(p)->beg)?1:0)
+#define sungetc(p,c) *(--(p)->rd)=c
+#define sgetc(p) (((p)->rd==(p)->wt)?-1:*(p)->rd++)
+#define skipc(p) {if((p)->rd<(p)->wt)(p)->rd++;}
+#define slookc(p) (((p)->rd==(p)->wt)?-1:*(p)->rd)
+#define sbackc(p) (((p)->rd==(p)->beg)?-1:*(--(p)->rd))
+#define backc(p) {if((p)->rd>(p)->beg) --(p)->rd;}
+#define sputc(p,c) {if((p)->wt==(p)->last)more(p);\
+ *(p)->wt++ = c; }
+#define salterc(p,c) {if((p)->rd==(p)->last)more(p);\
+ *(p)->rd++ = c;\
+ if((p)->rd>(p)->wt)(p)->wt=(p)->rd;}
+#define sunputc(p) (*((p)->rd = --(p)->wt))
+#define sclobber(p) ((p)->rd = --(p)->wt)
+#define zero(p) for(pp=(p)->beg;pp<(p)->last;)\
+ *pp++='\0'
+#define OUTC(x) {Bputc(&bout,x); if(--count == 0){Bprint(&bout,"\\\n"); count=ll;} }
+#define TEST2 {if((count -= 2) <=0){Bprint(&bout,"\\\n");count=ll;}}
+#define EMPTY if(stkerr != 0){Bprint(&bout,"stack empty\n"); continue; }
+#define EMPTYR(x) if(stkerr!=0){pushp(x);Bprint(&bout,"stack empty\n");continue;}
+#define EMPTYS if(stkerr != 0){Bprint(&bout,"stack empty\n"); return(1);}
+#define EMPTYSR(x) if(stkerr !=0){Bprint(&bout,"stack empty\n");pushp(x);return(1);}
+#define error(p) {Bprint(&bout,p); continue; }
+#define errorrt(p) {Bprint(&bout,p); return(1); }
+#define LASTFUN 026
+
+typedef struct Blk Blk;
+struct Blk
+{
+ char *rd;
+ char *wt;
+ char *beg;
+ char *last;
+};
+typedef struct Sym Sym;
+struct Sym
+{
+ Sym *next;
+ Blk *val;
+};
+typedef struct Wblk Wblk;
+struct Wblk
+{
+ Blk **rdw;
+ Blk **wtw;
+ Blk **begw;
+ Blk **lastw;
+};
+
+Biobuf *curfile, *fsave;
+Blk *arg1, *arg2;
+uchar savk;
+int dbg;
+int ifile;
+Blk *scalptr, *basptr, *tenptr, *inbas;
+Blk *sqtemp, *chptr, *strptr, *divxyz;
+Blk *stack[STKSZ];
+Blk **stkptr,**stkbeg;
+Blk **stkend;
+Blk *hfree;
+int stkerr;
+int lastchar;
+Blk *readstk[RDSKSZ];
+Blk **readptr;
+Blk *rem;
+int k;
+Blk *irem;
+int skd,skr;
+int neg;
+Sym symlst[TBLSZ];
+Sym *stable[TBLSZ];
+Sym *sptr, *sfree;
+long rel;
+long nbytes;
+long all;
+long headmor;
+long obase;
+int fw,fw1,ll;
+void (*outdit)(Blk *p, int flg);
+int logo;
+int logten;
+int count;
+char *pp;
+char *dummy;
+long longest, maxsize, active;
+int lall, lrel, lcopy, lmore, lbytes;
+int inside;
+Biobuf bin;
+Biobuf bout;
+
+void main(int argc, char *argv[]);
+void commnds(void);
+Blk* readin(void);
+Blk* div(Blk *ddivd, Blk *ddivr);
+int dscale(void);
+Blk* removr(Blk *p, int n);
+Blk* dcsqrt(Blk *p);
+void init(int argc, char *argv[]);
+void onintr(void);
+void pushp(Blk *p);
+Blk* pop(void);
+Blk* readin(void);
+Blk* add0(Blk *p, int ct);
+Blk* mult(Blk *p, Blk *q);
+void chsign(Blk *p);
+int readc(void);
+void unreadc(char c);
+void binop(char c);
+void dcprint(Blk *hptr);
+Blk* dcexp(Blk *base, Blk *ex);
+Blk* getdec(Blk *p, int sc);
+void tenot(Blk *p, int sc);
+void oneot(Blk *p, int sc, char ch);
+void hexot(Blk *p, int flg);
+void bigot(Blk *p, int flg);
+Blk* add(Blk *a1, Blk *a2);
+int eqk(void);
+Blk* removc(Blk *p, int n);
+Blk* scalint(Blk *p);
+Blk* scale(Blk *p, int n);
+int subt(void);
+int command(void);
+int cond(char c);
+void load(void);
+#define log2 dclog2
+int log2(long n);
+Blk* salloc(int size);
+Blk* morehd(void);
+Blk* copy(Blk *hptr, int size);
+void sdump(char *s1, Blk *hptr);
+void seekc(Blk *hptr, int n);
+void salterwd(Blk *hptr, Blk *n);
+void more(Blk *hptr);
+void ospace(char *s);
+void garbage(char *s);
+void release(Blk *p);
+Blk* dcgetwd(Blk *p);
+void putwd(Blk *p, Blk *c);
+Blk* lookwd(Blk *p);
+int getstk(void);
+
+/********debug only**/
+void
+tpr(char *cp, Blk *bp)
+{
+ print("%s-> ", cp);
+ print("beg: %lx rd: %lx wt: %lx last: %lx\n", bp->beg, bp->rd,
+ bp->wt, bp->last);
+ for (cp = bp->beg; cp != bp->wt; cp++) {
+ print("%d", *cp);
+ if (cp != bp->wt-1)
+ print("/");
+ }
+ print("\n");
+}
+/************/
+
+void
+main(int argc, char *argv[])
+{
+ Binit(&bin, 0, OREAD);
+ Binit(&bout, 1, OWRITE);
+ init(argc,argv);
+ commnds();
+ exits(0);
+}
+
+void
+commnds(void)
+{
+ Blk *p, *q, **ptr, *s, *t;
+ long l;
+ Sym *sp;
+ int sk, sk1, sk2, c, sign, n, d;
+
+ while(1) {
+ Bflush(&bout);
+ if(((c = readc())>='0' && c <= '9') ||
+ (c>='A' && c <='F') || c == '.') {
+ unreadc(c);
+ p = readin();
+ pushp(p);
+ continue;
+ }
+ switch(c) {
+ case ' ':
+ case '\n':
+ case -1:
+ continue;
+ case 'Y':
+ sdump("stk",*stkptr);
+ Bprint(&bout, "all %ld rel %ld headmor %ld\n",all,rel,headmor);
+ Bprint(&bout, "nbytes %ld\n",nbytes);
+ Bprint(&bout, "longest %ld active %ld maxsize %ld\n", longest,
+ active, maxsize);
+ Bprint(&bout, "new all %d rel %d copy %d more %d lbytes %d\n",
+ lall, lrel, lcopy, lmore, lbytes);
+ lall = lrel = lcopy = lmore = lbytes = 0;
+ continue;
+ case '_':
+ p = readin();
+ savk = sunputc(p);
+ chsign(p);
+ sputc(p,savk);
+ pushp(p);
+ continue;
+ case '-':
+ subt();
+ continue;
+ case '+':
+ if(eqk() != 0)
+ continue;
+ binop('+');
+ continue;
+ case '*':
+ arg1 = pop();
+ EMPTY;
+ arg2 = pop();
+ EMPTYR(arg1);
+ sk1 = sunputc(arg1);
+ sk2 = sunputc(arg2);
+ savk = sk1+sk2;
+ binop('*');
+ p = pop();
+ if(savk>k && savk>sk1 && savk>sk2) {
+ sclobber(p);
+ sk = sk1;
+ if(sk<sk2)
+ sk = sk2;
+ if(sk<k)
+ sk = k;
+ p = removc(p,savk-sk);
+ savk = sk;
+ sputc(p,savk);
+ }
+ pushp(p);
+ continue;
+ case '/':
+ casediv:
+ if(dscale() != 0)
+ continue;
+ binop('/');
+ if(irem != 0)
+ release(irem);
+ release(rem);
+ continue;
+ case '%':
+ if(dscale() != 0)
+ continue;
+ binop('/');
+ p = pop();
+ release(p);
+ if(irem == 0) {
+ sputc(rem,skr+k);
+ pushp(rem);
+ continue;
+ }
+ p = add0(rem,skd-(skr+k));
+ q = add(p,irem);
+ release(p);
+ release(irem);
+ sputc(q,skd);
+ pushp(q);
+ continue;
+ case 'v':
+ p = pop();
+ EMPTY;
+ savk = sunputc(p);
+ if(length(p) == 0) {
+ sputc(p,savk);
+ pushp(p);
+ continue;
+ }
+ if(sbackc(p)<0) {
+ error("sqrt of neg number\n");
+ }
+ if(k<savk)
+ n = savk;
+ else {
+ n = k*2-savk;
+ savk = k;
+ }
+ arg1 = add0(p,n);
+ arg2 = dcsqrt(arg1);
+ sputc(arg2,savk);
+ pushp(arg2);
+ continue;
+
+ case '^':
+ neg = 0;
+ arg1 = pop();
+ EMPTY;
+ if(sunputc(arg1) != 0)
+ error("exp not an integer\n");
+ arg2 = pop();
+ EMPTYR(arg1);
+ if(sfbeg(arg1) == 0 && sbackc(arg1)<0) {
+ neg++;
+ chsign(arg1);
+ }
+ if(length(arg1)>=3) {
+ error("exp too big\n");
+ }
+ savk = sunputc(arg2);
+ p = dcexp(arg2,arg1);
+ release(arg2);
+ rewind(arg1);
+ c = sgetc(arg1);
+ if(c == -1)
+ c = 0;
+ else
+ if(sfeof(arg1) == 0)
+ c = sgetc(arg1)*100 + c;
+ d = c*savk;
+ release(arg1);
+ /* if(neg == 0) { removed to fix -exp bug*/
+ if(k>=savk)
+ n = k;
+ else
+ n = savk;
+ if(n<d) {
+ q = removc(p,d-n);
+ sputc(q,n);
+ pushp(q);
+ } else {
+ sputc(p,d);
+ pushp(p);
+ }
+ /* } else { this is disaster for exp <-127 */
+ /* sputc(p,d); */
+ /* pushp(p); */
+ /* } */
+ if(neg == 0)
+ continue;
+ p = pop();
+ q = salloc(2);
+ sputc(q,1);
+ sputc(q,0);
+ pushp(q);
+ pushp(p);
+ goto casediv;
+ case 'z':
+ p = salloc(2);
+ n = stkptr - stkbeg;
+ if(n >= 100) {
+ sputc(p,n/100);
+ n %= 100;
+ }
+ sputc(p,n);
+ sputc(p,0);
+ pushp(p);
+ continue;
+ case 'Z':
+ p = pop();
+ EMPTY;
+ n = (length(p)-1)<<1;
+ fsfile(p);
+ backc(p);
+ if(sfbeg(p) == 0) {
+ if((c = sbackc(p))<0) {
+ n -= 2;
+ if(sfbeg(p) == 1)
+ n++;
+ else {
+ if((c = sbackc(p)) == 0)
+ n++;
+ else
+ if(c > 90)
+ n--;
+ }
+ } else
+ if(c < 10)
+ n--;
+ }
+ release(p);
+ q = salloc(1);
+ if(n >= 100) {
+ sputc(q,n%100);
+ n /= 100;
+ }
+ sputc(q,n);
+ sputc(q,0);
+ pushp(q);
+ continue;
+ case 'i':
+ p = pop();
+ EMPTY;
+ p = scalint(p);
+ release(inbas);
+ inbas = p;
+ continue;
+ case 'I':
+ p = copy(inbas,length(inbas)+1);
+ sputc(p,0);
+ pushp(p);
+ continue;
+ case 'o':
+ p = pop();
+ EMPTY;
+ p = scalint(p);
+ sign = 0;
+ n = length(p);
+ q = copy(p,n);
+ fsfile(q);
+ l = c = sbackc(q);
+ if(n != 1) {
+ if(c<0) {
+ sign = 1;
+ chsign(q);
+ n = length(q);
+ fsfile(q);
+ l = c = sbackc(q);
+ }
+ if(n != 1) {
+ while(sfbeg(q) == 0)
+ l = l*100+sbackc(q);
+ }
+ }
+ logo = log2(l);
+ obase = l;
+ release(basptr);
+ if(sign == 1)
+ obase = -l;
+ basptr = p;
+ outdit = bigot;
+ if(n == 1 && sign == 0) {
+ if(c <= 16) {
+ outdit = hexot;
+ fw = 1;
+ fw1 = 0;
+ ll = 70;
+ release(q);
+ continue;
+ }
+ }
+ n = 0;
+ if(sign == 1)
+ n++;
+ p = salloc(1);
+ sputc(p,-1);
+ t = add(p,q);
+ n += length(t)*2;
+ fsfile(t);
+ if(sbackc(t)>9)
+ n++;
+ release(t);
+ release(q);
+ release(p);
+ fw = n;
+ fw1 = n-1;
+ ll = 70;
+ if(fw>=ll)
+ continue;
+ ll = (70/fw)*fw;
+ continue;
+ case 'O':
+ p = copy(basptr,length(basptr)+1);
+ sputc(p,0);
+ pushp(p);
+ continue;
+ case '[':
+ n = 0;
+ p = salloc(0);
+ for(;;) {
+ if((c = readc()) == ']') {
+ if(n == 0)
+ break;
+ n--;
+ }
+ sputc(p,c);
+ if(c == '[')
+ n++;
+ }
+ pushp(p);
+ continue;
+ case 'k':
+ p = pop();
+ EMPTY;
+ p = scalint(p);
+ if(length(p)>1) {
+ error("scale too big\n");
+ }
+ rewind(p);
+ k = 0;
+ if(!sfeof(p))
+ k = sgetc(p);
+ release(scalptr);
+ scalptr = p;
+ continue;
+ case 'K':
+ p = copy(scalptr,length(scalptr)+1);
+ sputc(p,0);
+ pushp(p);
+ continue;
+ case 'X':
+ p = pop();
+ EMPTY;
+ fsfile(p);
+ n = sbackc(p);
+ release(p);
+ p = salloc(2);
+ sputc(p,n);
+ sputc(p,0);
+ pushp(p);
+ continue;
+ case 'Q':
+ p = pop();
+ EMPTY;
+ if(length(p)>2) {
+ error("Q?\n");
+ }
+ rewind(p);
+ if((c = sgetc(p))<0) {
+ error("neg Q\n");
+ }
+ release(p);
+ while(c-- > 0) {
+ if(readptr == &readstk[0]) {
+ error("readstk?\n");
+ }
+ if(*readptr != 0)
+ release(*readptr);
+ readptr--;
+ }
+ continue;
+ case 'q':
+ if(readptr <= &readstk[1])
+ exits(0);
+ if(*readptr != 0)
+ release(*readptr);
+ readptr--;
+ if(*readptr != 0)
+ release(*readptr);
+ readptr--;
+ continue;
+ case 'f':
+ if(stkptr == &stack[0])
+ Bprint(&bout,"empty stack\n");
+ else {
+ for(ptr = stkptr; ptr > &stack[0];) {
+ dcprint(*ptr--);
+ }
+ }
+ continue;
+ case 'p':
+ if(stkptr == &stack[0])
+ Bprint(&bout,"empty stack\n");
+ else {
+ dcprint(*stkptr);
+ }
+ continue;
+ case 'P':
+ p = pop();
+ EMPTY;
+ sputc(p,0);
+ Bprint(&bout,"%s",p->beg);
+ release(p);
+ continue;
+ case 'd':
+ if(stkptr == &stack[0]) {
+ Bprint(&bout,"empty stack\n");
+ continue;
+ }
+ q = *stkptr;
+ n = length(q);
+ p = copy(*stkptr,n);
+ pushp(p);
+ continue;
+ case 'c':
+ while(stkerr == 0) {
+ p = pop();
+ if(stkerr == 0)
+ release(p);
+ }
+ continue;
+ case 'S':
+ if(stkptr == &stack[0]) {
+ error("save: args\n");
+ }
+ c = getstk() & 0377;
+ sptr = stable[c];
+ sp = stable[c] = sfree;
+ sfree = sfree->next;
+ if(sfree == 0)
+ goto sempty;
+ sp->next = sptr;
+ p = pop();
+ EMPTY;
+ if(c >= ARRAYST) {
+ q = copy(p,length(p)+PTRSZ);
+ for(n = 0;n < PTRSZ;n++) {
+ sputc(q,0);
+ }
+ release(p);
+ p = q;
+ }
+ sp->val = p;
+ continue;
+ sempty:
+ error("symbol table overflow\n");
+ case 's':
+ if(stkptr == &stack[0]) {
+ error("save:args\n");
+ }
+ c = getstk() & 0377;
+ sptr = stable[c];
+ if(sptr != 0) {
+ p = sptr->val;
+ if(c >= ARRAYST) {
+ rewind(p);
+ while(sfeof(p) == 0)
+ release(dcgetwd(p));
+ }
+ release(p);
+ } else {
+ sptr = stable[c] = sfree;
+ sfree = sfree->next;
+ if(sfree == 0)
+ goto sempty;
+ sptr->next = 0;
+ }
+ p = pop();
+ sptr->val = p;
+ continue;
+ case 'l':
+ load();
+ continue;
+ case 'L':
+ c = getstk() & 0377;
+ sptr = stable[c];
+ if(sptr == 0) {
+ error("L?\n");
+ }
+ stable[c] = sptr->next;
+ sptr->next = sfree;
+ sfree = sptr;
+ p = sptr->val;
+ if(c >= ARRAYST) {
+ rewind(p);
+ while(sfeof(p) == 0) {
+ q = dcgetwd(p);
+ if(q != 0)
+ release(q);
+ }
+ }
+ pushp(p);
+ continue;
+ case ':':
+ p = pop();
+ EMPTY;
+ q = scalint(p);
+ fsfile(q);
+ c = 0;
+ if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+ error("neg index\n");
+ }
+ if(length(q)>2) {
+ error("index too big\n");
+ }
+ if(sfbeg(q) == 0)
+ c = c*100+sbackc(q);
+ if(c >= MAXIND) {
+ error("index too big\n");
+ }
+ release(q);
+ n = getstk() & 0377;
+ sptr = stable[n];
+ if(sptr == 0) {
+ sptr = stable[n] = sfree;
+ sfree = sfree->next;
+ if(sfree == 0)
+ goto sempty;
+ sptr->next = 0;
+ p = salloc((c+PTRSZ)*PTRSZ);
+ zero(p);
+ } else {
+ p = sptr->val;
+ if(length(p)-PTRSZ < c*PTRSZ) {
+ q = copy(p,(c+PTRSZ)*PTRSZ);
+ release(p);
+ p = q;
+ }
+ }
+ seekc(p,c*PTRSZ);
+ q = lookwd(p);
+ if(q!=0)
+ release(q);
+ s = pop();
+ EMPTY;
+ salterwd(p, s);
+ sptr->val = p;
+ continue;
+ case ';':
+ p = pop();
+ EMPTY;
+ q = scalint(p);
+ fsfile(q);
+ c = 0;
+ if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+ error("neg index\n");
+ }
+ if(length(q)>2) {
+ error("index too big\n");
+ }
+ if(sfbeg(q) == 0)
+ c = c*100+sbackc(q);
+ if(c >= MAXIND) {
+ error("index too big\n");
+ }
+ release(q);
+ n = getstk() & 0377;
+ sptr = stable[n];
+ if(sptr != 0){
+ p = sptr->val;
+ if(length(p)-PTRSZ >= c*PTRSZ) {
+ seekc(p,c*PTRSZ);
+ s = dcgetwd(p);
+ if(s != 0) {
+ q = copy(s,length(s));
+ pushp(q);
+ continue;
+ }
+ }
+ }
+ q = salloc(1); /*so uninitialized array elt prints as 0*/
+ sputc(q, 0);
+ pushp(q);
+ continue;
+ case 'x':
+ execute:
+ p = pop();
+ EMPTY;
+ if((readptr != &readstk[0]) && (*readptr != 0)) {
+ if((*readptr)->rd == (*readptr)->wt)
+ release(*readptr);
+ else {
+ if(readptr++ == &readstk[RDSKSZ]) {
+ error("nesting depth\n");
+ }
+ }
+ } else
+ readptr++;
+ *readptr = p;
+ if(p != 0)
+ rewind(p);
+ else {
+ if((c = readc()) != '\n')
+ unreadc(c);
+ }
+ continue;
+ case '?':
+ if(++readptr == &readstk[RDSKSZ]) {
+ error("nesting depth\n");
+ }
+ *readptr = 0;
+ fsave = curfile;
+ curfile = &bin;
+ while((c = readc()) == '!')
+ command();
+ p = salloc(0);
+ sputc(p,c);
+ while((c = readc()) != '\n') {
+ sputc(p,c);
+ if(c == '\\')
+ sputc(p,readc());
+ }
+ curfile = fsave;
+ *readptr = p;
+ continue;
+ case '!':
+ if(command() == 1)
+ goto execute;
+ continue;
+ case '<':
+ case '>':
+ case '=':
+ if(cond(c) == 1)
+ goto execute;
+ continue;
+ default:
+ Bprint(&bout,"%o is unimplemented\n",c);
+ }
+ }
+}
+
+Blk*
+div(Blk *ddivd, Blk *ddivr)
+{
+ int divsign, remsign, offset, divcarry,
+ carry, dig, magic, d, dd, under, first;
+ long c, td, cc;
+ Blk *ps, *px, *p, *divd, *divr;
+
+ dig = 0;
+ under = 0;
+ divcarry = 0;
+ rem = 0;
+ p = salloc(0);
+ if(length(ddivr) == 0) {
+ pushp(ddivr);
+ Bprint(&bout,"divide by 0\n");
+ return(p);
+ }
+ divsign = remsign = first = 0;
+ divr = ddivr;
+ fsfile(divr);
+ if(sbackc(divr) == -1) {
+ divr = copy(ddivr,length(ddivr));
+ chsign(divr);
+ divsign = ~divsign;
+ }
+ divd = copy(ddivd,length(ddivd));
+ fsfile(divd);
+ if(sfbeg(divd) == 0 && sbackc(divd) == -1) {
+ chsign(divd);
+ divsign = ~divsign;
+ remsign = ~remsign;
+ }
+ offset = length(divd) - length(divr);
+ if(offset < 0)
+ goto ddone;
+ seekc(p,offset+1);
+ sputc(divd,0);
+ magic = 0;
+ fsfile(divr);
+ c = sbackc(divr);
+ if(c < 10)
+ magic++;
+ c = c * 100 + (sfbeg(divr)?0:sbackc(divr));
+ if(magic>0){
+ c = (c * 100 +(sfbeg(divr)?0:sbackc(divr)))*2;
+ c /= 25;
+ }
+ while(offset >= 0) {
+ first++;
+ fsfile(divd);
+ td = sbackc(divd) * 100;
+ dd = sfbeg(divd)?0:sbackc(divd);
+ td = (td + dd) * 100;
+ dd = sfbeg(divd)?0:sbackc(divd);
+ td = td + dd;
+ cc = c;
+ if(offset == 0)
+ td++;
+ else
+ cc++;
+ if(magic != 0)
+ td = td<<3;
+ dig = td/cc;
+ under=0;
+ if(td%cc < 8 && dig > 0 && magic) {
+ dig--;
+ under=1;
+ }
+ rewind(divr);
+ rewind(divxyz);
+ carry = 0;
+ while(sfeof(divr) == 0) {
+ d = sgetc(divr)*dig+carry;
+ carry = d / 100;
+ salterc(divxyz,d%100);
+ }
+ salterc(divxyz,carry);
+ rewind(divxyz);
+ seekc(divd,offset);
+ carry = 0;
+ while(sfeof(divd) == 0) {
+ d = slookc(divd);
+ d = d-(sfeof(divxyz)?0:sgetc(divxyz))-carry;
+ carry = 0;
+ if(d < 0) {
+ d += 100;
+ carry = 1;
+ }
+ salterc(divd,d);
+ }
+ divcarry = carry;
+ backc(p);
+ salterc(p,dig);
+ backc(p);
+ fsfile(divd);
+ d=sbackc(divd);
+ if((d != 0) && /*!divcarry*/ (offset != 0)) {
+ d = sbackc(divd) + 100;
+ salterc(divd,d);
+ }
+ if(--offset >= 0)
+ divd->wt--;
+ }
+ if(under) { /* undershot last - adjust*/
+ px = copy(divr,length(divr)); /*11/88 don't corrupt ddivr*/
+ chsign(px);
+ ps = add(px,divd);
+ fsfile(ps);
+ if(length(ps) > 0 && sbackc(ps) < 0) {
+ release(ps); /*only adjust in really undershot*/
+ } else {
+ release(divd);
+ salterc(p, dig+1);
+ divd=ps;
+ }
+ }
+ if(divcarry != 0) {
+ salterc(p,dig-1);
+ salterc(divd,-1);
+ ps = add(divr,divd);
+ release(divd);
+ divd = ps;
+ }
+
+ rewind(p);
+ divcarry = 0;
+ while(sfeof(p) == 0){
+ d = slookc(p)+divcarry;
+ divcarry = 0;
+ if(d >= 100){
+ d -= 100;
+ divcarry = 1;
+ }
+ salterc(p,d);
+ }
+ if(divcarry != 0)salterc(p,divcarry);
+ fsfile(p);
+ while(sfbeg(p) == 0) {
+ if(sbackc(p) != 0)
+ break;
+ truncate(p);
+ }
+ if(divsign < 0)
+ chsign(p);
+ fsfile(divd);
+ while(sfbeg(divd) == 0) {
+ if(sbackc(divd) != 0)
+ break;
+ truncate(divd);
+ }
+ddone:
+ if(remsign<0)
+ chsign(divd);
+ if(divr != ddivr)
+ release(divr);
+ rem = divd;
+ return(p);
+}
+
+int
+dscale(void)
+{
+ Blk *dd, *dr, *r;
+ int c;
+
+ dr = pop();
+ EMPTYS;
+ dd = pop();
+ EMPTYSR(dr);
+ fsfile(dd);
+ skd = sunputc(dd);
+ fsfile(dr);
+ skr = sunputc(dr);
+ if(sfbeg(dr) == 1 || (sfbeg(dr) == 0 && sbackc(dr) == 0)) {
+ sputc(dr,skr);
+ pushp(dr);
+ Bprint(&bout,"divide by 0\n");
+ return(1);
+ }
+ if(sfbeg(dd) == 1 || (sfbeg(dd) == 0 && sbackc(dd) == 0)) {
+ sputc(dd,skd);
+ pushp(dd);
+ return(1);
+ }
+ c = k-skd+skr;
+ if(c < 0)
+ r = removr(dd,-c);
+ else {
+ r = add0(dd,c);
+ irem = 0;
+ }
+ arg1 = r;
+ arg2 = dr;
+ savk = k;
+ return(0);
+}
+
+Blk*
+removr(Blk *p, int n)
+{
+ int nn, neg;
+ Blk *q, *s, *r;
+
+ fsfile(p);
+ neg = sbackc(p);
+ if(neg < 0)
+ chsign(p);
+ rewind(p);
+ nn = (n+1)/2;
+ q = salloc(nn);
+ while(n>1) {
+ sputc(q,sgetc(p));
+ n -= 2;
+ }
+ r = salloc(2);
+ while(sfeof(p) == 0)
+ sputc(r,sgetc(p));
+ release(p);
+ if(n == 1){
+ s = div(r,tenptr);
+ release(r);
+ rewind(rem);
+ if(sfeof(rem) == 0)
+ sputc(q,sgetc(rem));
+ release(rem);
+ if(neg < 0){
+ chsign(s);
+ chsign(q);
+ irem = q;
+ return(s);
+ }
+ irem = q;
+ return(s);
+ }
+ if(neg < 0) {
+ chsign(r);
+ chsign(q);
+ irem = q;
+ return(r);
+ }
+ irem = q;
+ return(r);
+}
+
+Blk*
+dcsqrt(Blk *p)
+{
+ Blk *t, *r, *q, *s;
+ int c, n, nn;
+
+ n = length(p);
+ fsfile(p);
+ c = sbackc(p);
+ if((n&1) != 1)
+ c = c*100+(sfbeg(p)?0:sbackc(p));
+ n = (n+1)>>1;
+ r = salloc(n);
+ zero(r);
+ seekc(r,n);
+ nn=1;
+ while((c -= nn)>=0)
+ nn+=2;
+ c=(nn+1)>>1;
+ fsfile(r);
+ backc(r);
+ if(c>=100) {
+ c -= 100;
+ salterc(r,c);
+ sputc(r,1);
+ } else
+ salterc(r,c);
+ for(;;){
+ q = div(p,r);
+ s = add(q,r);
+ release(q);
+ release(rem);
+ q = div(s,sqtemp);
+ release(s);
+ release(rem);
+ s = copy(r,length(r));
+ chsign(s);
+ t = add(s,q);
+ release(s);
+ fsfile(t);
+ nn = sfbeg(t)?0:sbackc(t);
+ if(nn>=0)
+ break;
+ release(r);
+ release(t);
+ r = q;
+ }
+ release(t);
+ release(q);
+ release(p);
+ return(r);
+}
+
+Blk*
+dcexp(Blk *base, Blk *ex)
+{
+ Blk *r, *e, *p, *e1, *t, *cp;
+ int temp, c, n;
+
+ r = salloc(1);
+ sputc(r,1);
+ p = copy(base,length(base));
+ e = copy(ex,length(ex));
+ fsfile(e);
+ if(sfbeg(e) != 0)
+ goto edone;
+ temp=0;
+ c = sbackc(e);
+ if(c<0) {
+ temp++;
+ chsign(e);
+ }
+ while(length(e) != 0) {
+ e1=div(e,sqtemp);
+ release(e);
+ e = e1;
+ n = length(rem);
+ release(rem);
+ if(n != 0) {
+ e1=mult(p,r);
+ release(r);
+ r = e1;
+ }
+ t = copy(p,length(p));
+ cp = mult(p,t);
+ release(p);
+ release(t);
+ p = cp;
+ }
+ if(temp != 0) {
+ if((c = length(base)) == 0) {
+ goto edone;
+ }
+ if(c>1)
+ create(r);
+ else {
+ rewind(base);
+ if((c = sgetc(base))<=1) {
+ create(r);
+ sputc(r,c);
+ } else
+ create(r);
+ }
+ }
+edone:
+ release(p);
+ release(e);
+ return(r);
+}
+
+void
+init(int argc, char *argv[])
+{
+ Sym *sp;
+ Dir *d;
+
+ ARGBEGIN {
+ default:
+ dbg = 1;
+ break;
+ } ARGEND
+ ifile = 1;
+ curfile = &bin;
+ if(*argv){
+ d = dirstat(*argv);
+ if(d == nil) {
+ fprint(2, "dc: can't open file %s\n", *argv);
+ exits("open");
+ }
+ if(d->mode & DMDIR) {
+ fprint(2, "dc: file %s is a directory\n", *argv);
+ exits("open");
+ }
+ free(d);
+ if((curfile = Bopen(*argv, OREAD)) == 0) {
+ fprint(2,"dc: can't open file %s\n", *argv);
+ exits("open");
+ }
+ }
+/* dummy = malloc(0); *//* prepare for garbage-collection */
+ scalptr = salloc(1);
+ sputc(scalptr,0);
+ basptr = salloc(1);
+ sputc(basptr,10);
+ obase=10;
+ logten=log2(10L);
+ ll=70;
+ fw=1;
+ fw1=0;
+ tenptr = salloc(1);
+ sputc(tenptr,10);
+ obase=10;
+ inbas = salloc(1);
+ sputc(inbas,10);
+ sqtemp = salloc(1);
+ sputc(sqtemp,2);
+ chptr = salloc(0);
+ strptr = salloc(0);
+ divxyz = salloc(0);
+ stkbeg = stkptr = &stack[0];
+ stkend = &stack[STKSZ];
+ stkerr = 0;
+ readptr = &readstk[0];
+ k=0;
+ sp = sptr = &symlst[0];
+ while(sptr < &symlst[TBLSZ-1]) {
+ sptr->next = ++sp;
+ sptr++;
+ }
+ sptr->next=0;
+ sfree = &symlst[0];
+}
+
+void
+pushp(Blk *p)
+{
+ if(stkptr == stkend) {
+ Bprint(&bout,"out of stack space\n");
+ return;
+ }
+ stkerr=0;
+ *++stkptr = p;
+ return;
+}
+
+Blk*
+pop(void)
+{
+ if(stkptr == stack) {
+ stkerr=1;
+ return(0);
+ }
+ return(*stkptr--);
+}
+
+Blk*
+readin(void)
+{
+ Blk *p, *q;
+ int dp, dpct, c;
+
+ dp = dpct=0;
+ p = salloc(0);
+ for(;;){
+ c = readc();
+ switch(c) {
+ case '.':
+ if(dp != 0)
+ goto gotnum;
+ dp++;
+ continue;
+ case '\\':
+ readc();
+ continue;
+ default:
+ if(c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else
+ if(c >= '0' && c <= '9')
+ c -= '0';
+ else
+ goto gotnum;
+ if(dp != 0) {
+ if(dpct >= 99)
+ continue;
+ dpct++;
+ }
+ create(chptr);
+ if(c != 0)
+ sputc(chptr,c);
+ q = mult(p,inbas);
+ release(p);
+ p = add(chptr,q);
+ release(q);
+ }
+ }
+gotnum:
+ unreadc(c);
+ if(dp == 0) {
+ sputc(p,0);
+ return(p);
+ } else {
+ q = scale(p,dpct);
+ return(q);
+ }
+}
+
+/*
+ * returns pointer to struct with ct 0's & p
+ */
+Blk*
+add0(Blk *p, int ct)
+{
+ Blk *q, *t;
+
+ q = salloc(length(p)+(ct+1)/2);
+ while(ct>1) {
+ sputc(q,0);
+ ct -= 2;
+ }
+ rewind(p);
+ while(sfeof(p) == 0) {
+ sputc(q,sgetc(p));
+ }
+ release(p);
+ if(ct == 1) {
+ t = mult(tenptr,q);
+ release(q);
+ return(t);
+ }
+ return(q);
+}
+
+Blk*
+mult(Blk *p, Blk *q)
+{
+ Blk *mp, *mq, *mr;
+ int sign, offset, carry;
+ int cq, cp, mt, mcr;
+
+ offset = sign = 0;
+ fsfile(p);
+ mp = p;
+ if(sfbeg(p) == 0) {
+ if(sbackc(p)<0) {
+ mp = copy(p,length(p));
+ chsign(mp);
+ sign = ~sign;
+ }
+ }
+ fsfile(q);
+ mq = q;
+ if(sfbeg(q) == 0){
+ if(sbackc(q)<0) {
+ mq = copy(q,length(q));
+ chsign(mq);
+ sign = ~sign;
+ }
+ }
+ mr = salloc(length(mp)+length(mq));
+ zero(mr);
+ rewind(mq);
+ while(sfeof(mq) == 0) {
+ cq = sgetc(mq);
+ rewind(mp);
+ rewind(mr);
+ mr->rd += offset;
+ carry=0;
+ while(sfeof(mp) == 0) {
+ cp = sgetc(mp);
+ mcr = sfeof(mr)?0:slookc(mr);
+ mt = cp*cq + carry + mcr;
+ carry = mt/100;
+ salterc(mr,mt%100);
+ }
+ offset++;
+ if(carry != 0) {
+ mcr = sfeof(mr)?0:slookc(mr);
+ salterc(mr,mcr+carry);
+ }
+ }
+ if(sign < 0) {
+ chsign(mr);
+ }
+ if(mp != p)
+ release(mp);
+ if(mq != q)
+ release(mq);
+ return(mr);
+}
+
+void
+chsign(Blk *p)
+{
+ int carry;
+ char ct;
+
+ carry=0;
+ rewind(p);
+ while(sfeof(p) == 0) {
+ ct=100-slookc(p)-carry;
+ carry=1;
+ if(ct>=100) {
+ ct -= 100;
+ carry=0;
+ }
+ salterc(p,ct);
+ }
+ if(carry != 0) {
+ sputc(p,-1);
+ fsfile(p);
+ backc(p);
+ ct = sbackc(p);
+ if(ct == 99 /*&& !sfbeg(p)*/) {
+ truncate(p);
+ sputc(p,-1);
+ }
+ } else{
+ fsfile(p);
+ ct = sbackc(p);
+ if(ct == 0)
+ truncate(p);
+ }
+ return;
+}
+
+int
+readc(void)
+{
+loop:
+ if((readptr != &readstk[0]) && (*readptr != 0)) {
+ if(sfeof(*readptr) == 0)
+ return(lastchar = sgetc(*readptr));
+ release(*readptr);
+ readptr--;
+ goto loop;
+ }
+ lastchar = Bgetc(curfile);
+ if(lastchar != -1)
+ return(lastchar);
+ if(readptr != &readptr[0]) {
+ readptr--;
+ if(*readptr == 0)
+ curfile = &bin;
+ goto loop;
+ }
+ if(curfile != &bin) {
+ Bterm(curfile);
+ curfile = &bin;
+ goto loop;
+ }
+ exits(0);
+ return 0; /* shut up ken */
+}
+
+void
+unreadc(char c)
+{
+
+ if((readptr != &readstk[0]) && (*readptr != 0)) {
+ sungetc(*readptr,c);
+ } else
+ Bungetc(curfile);
+ return;
+}
+
+void
+binop(char c)
+{
+ Blk *r;
+
+ r = 0;
+ switch(c) {
+ case '+':
+ r = add(arg1,arg2);
+ break;
+ case '*':
+ r = mult(arg1,arg2);
+ break;
+ case '/':
+ r = div(arg1,arg2);
+ break;
+ }
+ release(arg1);
+ release(arg2);
+ sputc(r,savk);
+ pushp(r);
+}
+
+void
+dcprint(Blk *hptr)
+{
+ Blk *p, *q, *dec;
+ int dig, dout, ct, sc;
+
+ rewind(hptr);
+ while(sfeof(hptr) == 0) {
+ if(sgetc(hptr)>99) {
+ rewind(hptr);
+ while(sfeof(hptr) == 0) {
+ Bprint(&bout,"%c",sgetc(hptr));
+ }
+ Bprint(&bout,"\n");
+ return;
+ }
+ }
+ fsfile(hptr);
+ sc = sbackc(hptr);
+ if(sfbeg(hptr) != 0) {
+ Bprint(&bout,"0\n");
+ return;
+ }
+ count = ll;
+ p = copy(hptr,length(hptr));
+ sclobber(p);
+ fsfile(p);
+ if(sbackc(p)<0) {
+ chsign(p);
+ OUTC('-');
+ }
+ if((obase == 0) || (obase == -1)) {
+ oneot(p,sc,'d');
+ return;
+ }
+ if(obase == 1) {
+ oneot(p,sc,'1');
+ return;
+ }
+ if(obase == 10) {
+ tenot(p,sc);
+ return;
+ }
+ /* sleazy hack to scale top of stack - divide by 1 */
+ pushp(p);
+ sputc(p, sc);
+ p=salloc(0);
+ create(p);
+ sputc(p, 1);
+ sputc(p, 0);
+ pushp(p);
+ if(dscale() != 0)
+ return;
+ p = div(arg1, arg2);
+ release(arg1);
+ release(arg2);
+ sc = savk;
+
+ create(strptr);
+ dig = logten*sc;
+ dout = ((dig/10) + dig) / logo;
+ dec = getdec(p,sc);
+ p = removc(p,sc);
+ while(length(p) != 0) {
+ q = div(p,basptr);
+ release(p);
+ p = q;
+ (*outdit)(rem,0);
+ }
+ release(p);
+ fsfile(strptr);
+ while(sfbeg(strptr) == 0)
+ OUTC(sbackc(strptr));
+ if(sc == 0) {
+ release(dec);
+ Bprint(&bout,"\n");
+ return;
+ }
+ create(strptr);
+ OUTC('.');
+ ct=0;
+ do {
+ q = mult(basptr,dec);
+ release(dec);
+ dec = getdec(q,sc);
+ p = removc(q,sc);
+ (*outdit)(p,1);
+ } while(++ct < dout);
+ release(dec);
+ rewind(strptr);
+ while(sfeof(strptr) == 0)
+ OUTC(sgetc(strptr));
+ Bprint(&bout,"\n");
+}
+
+Blk*
+getdec(Blk *p, int sc)
+{
+ int cc;
+ Blk *q, *t, *s;
+
+ rewind(p);
+ if(length(p)*2 < sc) {
+ q = copy(p,length(p));
+ return(q);
+ }
+ q = salloc(length(p));
+ while(sc >= 1) {
+ sputc(q,sgetc(p));
+ sc -= 2;
+ }
+ if(sc != 0) {
+ t = mult(q,tenptr);
+ s = salloc(cc = length(q));
+ release(q);
+ rewind(t);
+ while(cc-- > 0)
+ sputc(s,sgetc(t));
+ sputc(s,0);
+ release(t);
+ t = div(s,tenptr);
+ release(s);
+ release(rem);
+ return(t);
+ }
+ return(q);
+}
+
+void
+tenot(Blk *p, int sc)
+{
+ int c, f;
+
+ fsfile(p);
+ f=0;
+ while((sfbeg(p) == 0) && ((p->rd-p->beg-1)*2 >= sc)) {
+ c = sbackc(p);
+ if((c<10) && (f == 1))
+ Bprint(&bout,"0%d",c);
+ else
+ Bprint(&bout,"%d",c);
+ f=1;
+ TEST2;
+ }
+ if(sc == 0) {
+ Bprint(&bout,"\n");
+ release(p);
+ return;
+ }
+ if((p->rd-p->beg)*2 > sc) {
+ c = sbackc(p);
+ Bprint(&bout,"%d.",c/10);
+ TEST2;
+ OUTC(c%10 +'0');
+ sc--;
+ } else {
+ OUTC('.');
+ }
+ while(sc>(p->rd-p->beg)*2) {
+ OUTC('0');
+ sc--;
+ }
+ while(sc > 1) {
+ c = sbackc(p);
+ if(c<10)
+ Bprint(&bout,"0%d",c);
+ else
+ Bprint(&bout,"%d",c);
+ sc -= 2;
+ TEST2;
+ }
+ if(sc == 1) {
+ OUTC(sbackc(p)/10 +'0');
+ }
+ Bprint(&bout,"\n");
+ release(p);
+}
+
+void
+oneot(Blk *p, int sc, char ch)
+{
+ Blk *q;
+
+ q = removc(p,sc);
+ create(strptr);
+ sputc(strptr,-1);
+ while(length(q)>0) {
+ p = add(strptr,q);
+ release(q);
+ q = p;
+ OUTC(ch);
+ }
+ release(q);
+ Bprint(&bout,"\n");
+}
+
+void
+hexot(Blk *p, int flg)
+{
+ int c;
+
+ USED(flg);
+ rewind(p);
+ if(sfeof(p) != 0) {
+ sputc(strptr,'0');
+ release(p);
+ return;
+ }
+ c = sgetc(p);
+ release(p);
+ if(c >= 16) {
+ Bprint(&bout,"hex digit > 16");
+ return;
+ }
+ sputc(strptr,c<10?c+'0':c-10+'a');
+}
+
+void
+bigot(Blk *p, int flg)
+{
+ Blk *t, *q;
+ int neg, l;
+
+ if(flg == 1) {
+ t = salloc(0);
+ l = 0;
+ } else {
+ t = strptr;
+ l = length(strptr)+fw-1;
+ }
+ neg=0;
+ if(length(p) != 0) {
+ fsfile(p);
+ if(sbackc(p)<0) {
+ neg=1;
+ chsign(p);
+ }
+ while(length(p) != 0) {
+ q = div(p,tenptr);
+ release(p);
+ p = q;
+ rewind(rem);
+ sputc(t,sfeof(rem)?'0':sgetc(rem)+'0');
+ release(rem);
+ }
+ }
+ release(p);
+ if(flg == 1) {
+ l = fw1-length(t);
+ if(neg != 0) {
+ l--;
+ sputc(strptr,'-');
+ }
+ fsfile(t);
+ while(l-- > 0)
+ sputc(strptr,'0');
+ while(sfbeg(t) == 0)
+ sputc(strptr,sbackc(t));
+ release(t);
+ } else {
+ l -= length(strptr);
+ while(l-- > 0)
+ sputc(strptr,'0');
+ if(neg != 0) {
+ sclobber(strptr);
+ sputc(strptr,'-');
+ }
+ }
+ sputc(strptr,' ');
+}
+
+Blk*
+add(Blk *a1, Blk *a2)
+{
+ Blk *p;
+ int carry, n, size, c, n1, n2;
+
+ size = length(a1)>length(a2)?length(a1):length(a2);
+ p = salloc(size);
+ rewind(a1);
+ rewind(a2);
+ carry=0;
+ while(--size >= 0) {
+ n1 = sfeof(a1)?0:sgetc(a1);
+ n2 = sfeof(a2)?0:sgetc(a2);
+ n = n1 + n2 + carry;
+ if(n>=100) {
+ carry=1;
+ n -= 100;
+ } else
+ if(n<0) {
+ carry = -1;
+ n += 100;
+ } else
+ carry = 0;
+ sputc(p,n);
+ }
+ if(carry != 0)
+ sputc(p,carry);
+ fsfile(p);
+ if(sfbeg(p) == 0) {
+ c = 0;
+ while(sfbeg(p) == 0 && (c = sbackc(p)) == 0)
+ ;
+ if(c != 0)
+ salterc(p,c);
+ truncate(p);
+ }
+ fsfile(p);
+ if(sfbeg(p) == 0 && sbackc(p) == -1) {
+ while((c = sbackc(p)) == 99) {
+ if(c == -1)
+ break;
+ }
+ skipc(p);
+ salterc(p,-1);
+ truncate(p);
+ }
+ return(p);
+}
+
+int
+eqk(void)
+{
+ Blk *p, *q;
+ int skp, skq;
+
+ p = pop();
+ EMPTYS;
+ q = pop();
+ EMPTYSR(p);
+ skp = sunputc(p);
+ skq = sunputc(q);
+ if(skp == skq) {
+ arg1=p;
+ arg2=q;
+ savk = skp;
+ return(0);
+ }
+ if(skp < skq) {
+ savk = skq;
+ p = add0(p,skq-skp);
+ } else {
+ savk = skp;
+ q = add0(q,skp-skq);
+ }
+ arg1=p;
+ arg2=q;
+ return(0);
+}
+
+Blk*
+removc(Blk *p, int n)
+{
+ Blk *q, *r;
+
+ rewind(p);
+ while(n>1) {
+ skipc(p);
+ n -= 2;
+ }
+ q = salloc(2);
+ while(sfeof(p) == 0)
+ sputc(q,sgetc(p));
+ if(n == 1) {
+ r = div(q,tenptr);
+ release(q);
+ release(rem);
+ q = r;
+ }
+ release(p);
+ return(q);
+}
+
+Blk*
+scalint(Blk *p)
+{
+ int n;
+
+ n = sunputc(p);
+ p = removc(p,n);
+ return(p);
+}
+
+Blk*
+scale(Blk *p, int n)
+{
+ Blk *q, *s, *t;
+
+ t = add0(p,n);
+ q = salloc(1);
+ sputc(q,n);
+ s = dcexp(inbas,q);
+ release(q);
+ q = div(t,s);
+ release(t);
+ release(s);
+ release(rem);
+ sputc(q,n);
+ return(q);
+}
+
+int
+subt(void)
+{
+ arg1=pop();
+ EMPTYS;
+ savk = sunputc(arg1);
+ chsign(arg1);
+ sputc(arg1,savk);
+ pushp(arg1);
+ if(eqk() != 0)
+ return(1);
+ binop('+');
+ return(0);
+}
+
+int
+command(void)
+{
+ char line[100], *sl;
+ int pid, p, c;
+
+ switch(c = readc()) {
+ case '<':
+ return(cond(NL));
+ case '>':
+ return(cond(NG));
+ case '=':
+ return(cond(NE));
+ default:
+ sl = line;
+ *sl++ = c;
+ while((c = readc()) != '\n')
+ *sl++ = c;
+ *sl = 0;
+ if((pid = fork()) == 0) {
+ execl("/bin/rc","rc","-c",line,0);
+ exits("shell");
+ }
+ for(;;) {
+ if((p = waitpid()) < 0)
+ break;
+ if(p== pid)
+ break;
+ }
+ Bprint(&bout,"!\n");
+ return(0);
+ }
+}
+
+int
+cond(char c)
+{
+ Blk *p;
+ int cc;
+
+ if(subt() != 0)
+ return(1);
+ p = pop();
+ sclobber(p);
+ if(length(p) == 0) {
+ release(p);
+ if(c == '<' || c == '>' || c == NE) {
+ getstk();
+ return(0);
+ }
+ load();
+ return(1);
+ }
+ if(c == '='){
+ release(p);
+ getstk();
+ return(0);
+ }
+ if(c == NE) {
+ release(p);
+ load();
+ return(1);
+ }
+ fsfile(p);
+ cc = sbackc(p);
+ release(p);
+ if((cc<0 && (c == '<' || c == NG)) ||
+ (cc >0) && (c == '>' || c == NL)) {
+ getstk();
+ return(0);
+ }
+ load();
+ return(1);
+}
+
+void
+load(void)
+{
+ int c;
+ Blk *p, *q, *t, *s;
+
+ c = getstk() & 0377;
+ sptr = stable[c];
+ if(sptr != 0) {
+ p = sptr->val;
+ if(c >= ARRAYST) {
+ q = salloc(length(p));
+ rewind(p);
+ while(sfeof(p) == 0) {
+ s = dcgetwd(p);
+ if(s == 0) {
+ putwd(q, (Blk*)0);
+ } else {
+ t = copy(s,length(s));
+ putwd(q,t);
+ }
+ }
+ pushp(q);
+ } else {
+ q = copy(p,length(p));
+ pushp(q);
+ }
+ } else {
+ q = salloc(1);
+ if(c <= LASTFUN) {
+ Bprint(&bout,"function %c undefined\n",c+'a'-1);
+ sputc(q,'c');
+ sputc(q,'0');
+ sputc(q,' ');
+ sputc(q,'1');
+ sputc(q,'Q');
+ }
+ else
+ sputc(q,0);
+ pushp(q);
+ }
+}
+
+int
+log2(long n)
+{
+ int i;
+
+ if(n == 0)
+ return(0);
+ i=31;
+ if(n<0)
+ return(i);
+ while((n= n<<1) >0)
+ i--;
+ return i-1;
+}
+
+Blk*
+salloc(int size)
+{
+ Blk *hdr;
+ char *ptr;
+
+ all++;
+ lall++;
+ if(all - rel > active)
+ active = all - rel;
+ nbytes += size;
+ lbytes += size;
+ if(nbytes >maxsize)
+ maxsize = nbytes;
+ if(size > longest)
+ longest = size;
+ ptr = malloc((unsigned)size);
+ if(ptr == 0){
+ garbage("salloc");
+ if((ptr = malloc((unsigned)size)) == 0)
+ ospace("salloc");
+ }
+ if((hdr = hfree) == 0)
+ hdr = morehd();
+ hfree = (Blk *)hdr->rd;
+ hdr->rd = hdr->wt = hdr->beg = ptr;
+ hdr->last = ptr+size;
+ return(hdr);
+}
+
+Blk*
+morehd(void)
+{
+ Blk *h, *kk;
+
+ headmor++;
+ nbytes += HEADSZ;
+ hfree = h = (Blk *)malloc(HEADSZ);
+ if(hfree == 0) {
+ garbage("morehd");
+ if((hfree = h = (Blk*)malloc(HEADSZ)) == 0)
+ ospace("headers");
+ }
+ kk = h;
+ while(h<hfree+(HEADSZ/BLK))
+ (h++)->rd = (char*)++kk;
+ (h-1)->rd=0;
+ return(hfree);
+}
+
+Blk*
+copy(Blk *hptr, int size)
+{
+ Blk *hdr;
+ unsigned sz;
+ char *ptr;
+
+ all++;
+ lall++;
+ lcopy++;
+ nbytes += size;
+ lbytes += size;
+ if(size > longest)
+ longest = size;
+ if(size > maxsize)
+ maxsize = size;
+ sz = length(hptr);
+ ptr = malloc(size);
+ if(ptr == 0) {
+ Bprint(&bout,"copy size %d\n",size);
+ ospace("copy");
+ }
+ memmove(ptr, hptr->beg, sz);
+ memset(ptr+sz, 0, size-sz);
+ if((hdr = hfree) == 0)
+ hdr = morehd();
+ hfree = (Blk *)hdr->rd;
+ hdr->rd = hdr->beg = ptr;
+ hdr->last = ptr+size;
+ hdr->wt = ptr+sz;
+ ptr = hdr->wt;
+ while(ptr<hdr->last)
+ *ptr++ = '\0';
+ return(hdr);
+}
+
+void
+sdump(char *s1, Blk *hptr)
+{
+ char *p;
+
+ Bprint(&bout,"%s %lx rd %lx wt %lx beg %lx last %lx\n",
+ s1,hptr,hptr->rd,hptr->wt,hptr->beg,hptr->last);
+ p = hptr->beg;
+ while(p < hptr->wt)
+ Bprint(&bout,"%d ",*p++);
+ Bprint(&bout,"\n");
+}
+
+void
+seekc(Blk *hptr, int n)
+{
+ char *nn,*p;
+
+ nn = hptr->beg+n;
+ if(nn > hptr->last) {
+ nbytes += nn - hptr->last;
+ if(nbytes > maxsize)
+ maxsize = nbytes;
+ lbytes += nn - hptr->last;
+ if(n > longest)
+ longest = n;
+/* free(hptr->beg); */
+ p = realloc(hptr->beg, n);
+ if(p == 0) {
+/* hptr->beg = realloc(hptr->beg, hptr->last-hptr->beg);
+** garbage("seekc");
+** if((p = realloc(hptr->beg, n)) == 0)
+*/ ospace("seekc");
+ }
+ hptr->beg = p;
+ hptr->wt = hptr->last = hptr->rd = p+n;
+ return;
+ }
+ hptr->rd = nn;
+ if(nn>hptr->wt)
+ hptr->wt = nn;
+}
+
+void
+salterwd(Blk *ahptr, Blk *n)
+{
+ Wblk *hptr;
+
+ hptr = (Wblk*)ahptr;
+ if(hptr->rdw == hptr->lastw)
+ more(ahptr);
+ *hptr->rdw++ = n;
+ if(hptr->rdw > hptr->wtw)
+ hptr->wtw = hptr->rdw;
+}
+
+void
+more(Blk *hptr)
+{
+ unsigned size;
+ char *p;
+
+ if((size=(hptr->last-hptr->beg)*2) == 0)
+ size=2;
+ nbytes += size/2;
+ if(nbytes > maxsize)
+ maxsize = nbytes;
+ if(size > longest)
+ longest = size;
+ lbytes += size/2;
+ lmore++;
+/* free(hptr->beg);*/
+ p = realloc(hptr->beg, size);
+
+ if(p == 0) {
+/* hptr->beg = realloc(hptr->beg, (hptr->last-hptr->beg));
+** garbage("more");
+** if((p = realloc(hptr->beg,size)) == 0)
+*/ ospace("more");
+ }
+ hptr->rd = p + (hptr->rd - hptr->beg);
+ hptr->wt = p + (hptr->wt - hptr->beg);
+ hptr->beg = p;
+ hptr->last = p+size;
+}
+
+void
+ospace(char *s)
+{
+ Bprint(&bout,"out of space: %s\n",s);
+ Bprint(&bout,"all %ld rel %ld headmor %ld\n",all,rel,headmor);
+ Bprint(&bout,"nbytes %ld\n",nbytes);
+ sdump("stk",*stkptr);
+ abort();
+}
+
+void
+garbage(char *s)
+{
+ USED(s);
+}
+
+void
+release(Blk *p)
+{
+ rel++;
+ lrel++;
+ nbytes -= p->last - p->beg;
+ p->rd = (char*)hfree;
+ hfree = p;
+ free(p->beg);
+}
+
+Blk*
+dcgetwd(Blk *p)
+{
+ Wblk *wp;
+
+ wp = (Wblk*)p;
+ if(wp->rdw == wp->wtw)
+ return(0);
+ return(*wp->rdw++);
+}
+
+void
+putwd(Blk *p, Blk *c)
+{
+ Wblk *wp;
+
+ wp = (Wblk*)p;
+ if(wp->wtw == wp->lastw)
+ more(p);
+ *wp->wtw++ = c;
+}
+
+Blk*
+lookwd(Blk *p)
+{
+ Wblk *wp;
+
+ wp = (Wblk*)p;
+ if(wp->rdw == wp->wtw)
+ return(0);
+ return(*wp->rdw);
+}
+
+int
+getstk(void)
+{
+ int n;
+ uchar c;
+
+ c = readc();
+ if(c != '<')
+ return c;
+ n = 0;
+ while(1) {
+ c = readc();
+ if(c == '>')
+ break;
+ n = n*10+c-'0';
+ }
+ return n;
+}
diff --git a/bin/9base/dd/Makefile b/bin/9base/dd/Makefile
new file mode 100644
index 00000000..5f347981
--- /dev/null
+++ b/bin/9base/dd/Makefile
@@ -0,0 +1,10 @@
+# dd - dd unix port from plan9
+# Depends on ../lib9
+
+TARG = dd
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/dd/dd.1 b/bin/9base/dd/dd.1
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/bin/9base/dd/dd.1
diff --git a/bin/9base/dd/dd.c b/bin/9base/dd/dd.c
new file mode 100644
index 00000000..15599145
--- /dev/null
+++ b/bin/9base/dd/dd.c
@@ -0,0 +1,660 @@
+#include <u.h>
+#include <libc.h>
+
+#define BIG 2147483647
+#define LCASE (1<<0)
+#define UCASE (1<<1)
+#define SWAB (1<<2)
+#define NERR (1<<3)
+#define SYNC (1<<4)
+int cflag;
+int fflag;
+char *string;
+char *ifile;
+char *ofile;
+char *ibuf;
+char *obuf;
+vlong skip;
+vlong oseekn;
+vlong iseekn;
+vlong count;
+long files = 1;
+long ibs = 512;
+long obs = 512;
+long bs;
+long cbs;
+long ibc;
+long obc;
+long cbc;
+long nifr;
+long nipr;
+long nofr;
+long nopr;
+long ntrunc;
+int dotrunc = 1;
+int ibf;
+int obf;
+char *op;
+int nspace;
+uchar etoa[256];
+uchar atoe[256];
+uchar atoibm[256];
+
+void flsh(void);
+int match(char *s);
+vlong number(long big);
+void cnull(int cc);
+void null(int c);
+void ascii(int cc);
+void unblock(int cc);
+void ebcdic(int cc);
+void ibm(int cc);
+void block(int cc);
+void term(void);
+void stats(void);
+
+#define iskey(s) ((key[0] == '-') && (strcmp(key+1, s) == 0))
+
+void
+main(int argc, char *argv[])
+{
+ void (*conv)(int);
+ char *ip;
+ char *key;
+ int a, c;
+
+ conv = null;
+ for(c=1; c<argc; c++) {
+ key = argv[c++];
+ if(c >= argc){
+ fprint(2, "dd: arg %s needs a value\n", key);
+ exits("arg");
+ }
+ string = argv[c];
+ if(iskey("ibs")) {
+ ibs = number(BIG);
+ continue;
+ }
+ if(iskey("obs")) {
+ obs = number(BIG);
+ continue;
+ }
+ if(iskey("cbs")) {
+ cbs = number(BIG);
+ continue;
+ }
+ if(iskey("bs")) {
+ bs = number(BIG);
+ continue;
+ }
+ if(iskey("if")) {
+ ifile = string;
+ continue;
+ }
+ if(iskey("of")) {
+ ofile = string;
+ continue;
+ }
+ if(iskey("trunc")) {
+ dotrunc = number(BIG);
+ continue;
+ }
+ if(iskey("skip")) {
+ skip = number(BIG);
+ continue;
+ }
+ if(iskey("seek") || iskey("oseek")) {
+ oseekn = number(BIG);
+ continue;
+ }
+ if(iskey("iseek")) {
+ iseekn = number(BIG);
+ continue;
+ }
+ if(iskey("count")) {
+ count = number(BIG);
+ continue;
+ }
+ if(iskey("files")) {
+ files = number(BIG);
+ continue;
+ }
+ if(iskey("conv")) {
+ cloop:
+ if(match(","))
+ goto cloop;
+ if(*string == '\0')
+ continue;
+ if(match("ebcdic")) {
+ conv = ebcdic;
+ goto cloop;
+ }
+ if(match("ibm")) {
+ conv = ibm;
+ goto cloop;
+ }
+ if(match("ascii")) {
+ conv = ascii;
+ goto cloop;
+ }
+ if(match("block")) {
+ conv = block;
+ goto cloop;
+ }
+ if(match("unblock")) {
+ conv = unblock;
+ goto cloop;
+ }
+ if(match("lcase")) {
+ cflag |= LCASE;
+ goto cloop;
+ }
+ if(match("ucase")) {
+ cflag |= UCASE;
+ goto cloop;
+ }
+ if(match("swab")) {
+ cflag |= SWAB;
+ goto cloop;
+ }
+ if(match("noerror")) {
+ cflag |= NERR;
+ goto cloop;
+ }
+ if(match("sync")) {
+ cflag |= SYNC;
+ goto cloop;
+ }
+ }
+ fprint(2, "dd: bad arg: %s\n", key);
+ exits("arg");
+ }
+ if(conv == null && cflag&(LCASE|UCASE))
+ conv = cnull;
+ if(ifile)
+ ibf = open(ifile, 0);
+ else
+ ibf = dup(0, -1);
+ if(ibf < 0) {
+ fprint(2, "dd: open %s: %r\n", ifile);
+ exits("open");
+ }
+ if(ofile){
+ if(dotrunc)
+ obf = create(ofile, 1, 0664);
+ else
+ obf = open(ofile, 1);
+ if(obf < 0) {
+ fprint(2, "dd: create %s: %r\n", ofile);
+ exits("create");
+ }
+ }else{
+ obf = dup(1, -1);
+ if(obf < 0) {
+ fprint(2, "dd: can't dup file descriptor: %s: %r\n", ofile);
+ exits("dup");
+ }
+ }
+ if(bs)
+ ibs = obs = bs;
+ if(ibs == obs && conv == null)
+ fflag++;
+ if(ibs == 0 || obs == 0) {
+ fprint(2, "dd: counts: cannot be zero\n");
+ exits("counts");
+ }
+ ibuf = sbrk(ibs);
+ if(fflag)
+ obuf = ibuf;
+ else
+ obuf = sbrk(obs);
+ sbrk(64); /* For good measure */
+ if(ibuf == (char *)-1 || obuf == (char *)-1) {
+ fprint(2, "dd: not enough memory: %r\n");
+ exits("memory");
+ }
+ ibc = 0;
+ obc = 0;
+ cbc = 0;
+ op = obuf;
+
+/*
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, term);
+*/
+ seek(obf, obs*oseekn, 1);
+ seek(ibf, ibs*iseekn, 1);
+ while(skip) {
+ read(ibf, ibuf, ibs);
+ skip--;
+ }
+
+ ip = 0;
+loop:
+ if(ibc-- == 0) {
+ ibc = 0;
+ if(count==0 || nifr+nipr!=count) {
+ if(cflag&(NERR|SYNC))
+ for(ip=ibuf+ibs; ip>ibuf;)
+ *--ip = 0;
+ ibc = read(ibf, ibuf, ibs);
+ }
+ if(ibc == -1) {
+ perror("read");
+ if((cflag&NERR) == 0) {
+ flsh();
+ term();
+ }
+ ibc = 0;
+ for(c=0; c<ibs; c++)
+ if(ibuf[c] != 0)
+ ibc = c;
+ stats();
+ }
+ if(ibc == 0 && --files<=0) {
+ flsh();
+ term();
+ }
+ if(ibc != ibs) {
+ nipr++;
+ if(cflag&SYNC)
+ ibc = ibs;
+ } else
+ nifr++;
+ ip = ibuf;
+ c = (ibc>>1) & ~1;
+ if(cflag&SWAB && c)
+ do {
+ a = *ip++;
+ ip[-1] = *ip;
+ *ip++ = a;
+ } while(--c);
+ ip = ibuf;
+ if(fflag) {
+ obc = ibc;
+ flsh();
+ ibc = 0;
+ }
+ goto loop;
+ }
+ c = 0;
+ c |= *ip++;
+ c &= 0377;
+ (*conv)(c);
+ goto loop;
+}
+
+void
+flsh(void)
+{
+ int c;
+
+ if(obc) {
+ c = write(obf, obuf, obc);
+ if(c != obc) {
+ if(c > 0)
+ ++nopr;
+ perror("write");
+ term();
+ }
+ if(obc == obs)
+ nofr++;
+ else
+ nopr++;
+ obc = 0;
+ }
+}
+
+int
+match(char *s)
+{
+ char *cs;
+
+ cs = string;
+ while(*cs++ == *s)
+ if(*s++ == '\0')
+ goto true;
+ if(*s != '\0')
+ return 0;
+
+true:
+ cs--;
+ string = cs;
+ return 1;
+}
+
+vlong
+number(long big)
+{
+ char *cs;
+ vlong n;
+
+ cs = string;
+ n = 0;
+ while(*cs >= '0' && *cs <= '9')
+ n = n*10 + *cs++ - '0';
+ for(;;)
+ switch(*cs++) {
+
+ case 'k':
+ n *= 1024;
+ continue;
+
+/* case 'w':
+ n *= sizeof(int);
+ continue;
+*/
+
+ case 'b':
+ n *= 512;
+ continue;
+
+/* case '*':*/
+ case 'x':
+ string = cs;
+ n *= number(BIG);
+
+ case '\0':
+ if(n>=big || n<0) {
+ fprint(2, "dd: argument %lld out of range\n", n);
+ exits("range");
+ }
+ return n;
+ }
+ /* never gets here */
+}
+
+void
+cnull(int cc)
+{
+ int c;
+
+ c = cc;
+ if((cflag&UCASE) && c>='a' && c<='z')
+ c += 'A'-'a';
+ if((cflag&LCASE) && c>='A' && c<='Z')
+ c += 'a'-'A';
+ null(c);
+}
+
+void
+null(int c)
+{
+
+ *op = c;
+ op++;
+ if(++obc >= obs) {
+ flsh();
+ op = obuf;
+ }
+}
+
+void
+ascii(int cc)
+{
+ int c;
+
+ c = etoa[cc];
+ if(cbs == 0) {
+ cnull(c);
+ return;
+ }
+ if(c == ' ') {
+ nspace++;
+ goto out;
+ }
+ while(nspace > 0) {
+ null(' ');
+ nspace--;
+ }
+ cnull(c);
+
+out:
+ if(++cbc >= cbs) {
+ null('\n');
+ cbc = 0;
+ nspace = 0;
+ }
+}
+
+void
+unblock(int cc)
+{
+ int c;
+
+ c = cc & 0377;
+ if(cbs == 0) {
+ cnull(c);
+ return;
+ }
+ if(c == ' ') {
+ nspace++;
+ goto out;
+ }
+ while(nspace > 0) {
+ null(' ');
+ nspace--;
+ }
+ cnull(c);
+
+out:
+ if(++cbc >= cbs) {
+ null('\n');
+ cbc = 0;
+ nspace = 0;
+ }
+}
+
+void
+ebcdic(int cc)
+{
+ int c;
+
+ c = cc;
+ if(cflag&UCASE && c>='a' && c<='z')
+ c += 'A'-'a';
+ if(cflag&LCASE && c>='A' && c<='Z')
+ c += 'a'-'A';
+ c = atoe[c];
+ if(cbs == 0) {
+ null(c);
+ return;
+ }
+ if(cc == '\n') {
+ while(cbc < cbs) {
+ null(atoe[' ']);
+ cbc++;
+ }
+ cbc = 0;
+ return;
+ }
+ if(cbc == cbs)
+ ntrunc++;
+ cbc++;
+ if(cbc <= cbs)
+ null(c);
+}
+
+void
+ibm(int cc)
+{
+ int c;
+
+ c = cc;
+ if(cflag&UCASE && c>='a' && c<='z')
+ c += 'A'-'a';
+ if(cflag&LCASE && c>='A' && c<='Z')
+ c += 'a'-'A';
+ c = atoibm[c] & 0377;
+ if(cbs == 0) {
+ null(c);
+ return;
+ }
+ if(cc == '\n') {
+ while(cbc < cbs) {
+ null(atoibm[' ']);
+ cbc++;
+ }
+ cbc = 0;
+ return;
+ }
+ if(cbc == cbs)
+ ntrunc++;
+ cbc++;
+ if(cbc <= cbs)
+ null(c);
+}
+
+void
+block(int cc)
+{
+ int c;
+
+ c = cc;
+ if(cflag&UCASE && c>='a' && c<='z')
+ c += 'A'-'a';
+ if(cflag&LCASE && c>='A' && c<='Z')
+ c += 'a'-'A';
+ c &= 0377;
+ if(cbs == 0) {
+ null(c);
+ return;
+ }
+ if(cc == '\n') {
+ while(cbc < cbs) {
+ null(' ');
+ cbc++;
+ }
+ cbc = 0;
+ return;
+ }
+ if(cbc == cbs)
+ ntrunc++;
+ cbc++;
+ if(cbc <= cbs)
+ null(c);
+}
+
+void
+term(void)
+{
+
+ stats();
+ exits(0);
+}
+
+void
+stats(void)
+{
+
+ fprint(2, "%lud+%lud records in\n", nifr, nipr);
+ fprint(2, "%lud+%lud records out\n", nofr, nopr);
+ if(ntrunc)
+ fprint(2, "%lud truncated records\n", ntrunc);
+}
+
+uchar etoa[] =
+{
+ 0000,0001,0002,0003,0234,0011,0206,0177,
+ 0227,0215,0216,0013,0014,0015,0016,0017,
+ 0020,0021,0022,0023,0235,0205,0010,0207,
+ 0030,0031,0222,0217,0034,0035,0036,0037,
+ 0200,0201,0202,0203,0204,0012,0027,0033,
+ 0210,0211,0212,0213,0214,0005,0006,0007,
+ 0220,0221,0026,0223,0224,0225,0226,0004,
+ 0230,0231,0232,0233,0024,0025,0236,0032,
+ 0040,0240,0241,0242,0243,0244,0245,0246,
+ 0247,0250,0133,0056,0074,0050,0053,0041,
+ 0046,0251,0252,0253,0254,0255,0256,0257,
+ 0260,0261,0135,0044,0052,0051,0073,0136,
+ 0055,0057,0262,0263,0264,0265,0266,0267,
+ 0270,0271,0174,0054,0045,0137,0076,0077,
+ 0272,0273,0274,0275,0276,0277,0300,0301,
+ 0302,0140,0072,0043,0100,0047,0075,0042,
+ 0303,0141,0142,0143,0144,0145,0146,0147,
+ 0150,0151,0304,0305,0306,0307,0310,0311,
+ 0312,0152,0153,0154,0155,0156,0157,0160,
+ 0161,0162,0313,0314,0315,0316,0317,0320,
+ 0321,0176,0163,0164,0165,0166,0167,0170,
+ 0171,0172,0322,0323,0324,0325,0326,0327,
+ 0330,0331,0332,0333,0334,0335,0336,0337,
+ 0340,0341,0342,0343,0344,0345,0346,0347,
+ 0173,0101,0102,0103,0104,0105,0106,0107,
+ 0110,0111,0350,0351,0352,0353,0354,0355,
+ 0175,0112,0113,0114,0115,0116,0117,0120,
+ 0121,0122,0356,0357,0360,0361,0362,0363,
+ 0134,0237,0123,0124,0125,0126,0127,0130,
+ 0131,0132,0364,0365,0366,0367,0370,0371,
+ 0060,0061,0062,0063,0064,0065,0066,0067,
+ 0070,0071,0372,0373,0374,0375,0376,0377,
+};
+uchar atoe[] =
+{
+ 0000,0001,0002,0003,0067,0055,0056,0057,
+ 0026,0005,0045,0013,0014,0015,0016,0017,
+ 0020,0021,0022,0023,0074,0075,0062,0046,
+ 0030,0031,0077,0047,0034,0035,0036,0037,
+ 0100,0117,0177,0173,0133,0154,0120,0175,
+ 0115,0135,0134,0116,0153,0140,0113,0141,
+ 0360,0361,0362,0363,0364,0365,0366,0367,
+ 0370,0371,0172,0136,0114,0176,0156,0157,
+ 0174,0301,0302,0303,0304,0305,0306,0307,
+ 0310,0311,0321,0322,0323,0324,0325,0326,
+ 0327,0330,0331,0342,0343,0344,0345,0346,
+ 0347,0350,0351,0112,0340,0132,0137,0155,
+ 0171,0201,0202,0203,0204,0205,0206,0207,
+ 0210,0211,0221,0222,0223,0224,0225,0226,
+ 0227,0230,0231,0242,0243,0244,0245,0246,
+ 0247,0250,0251,0300,0152,0320,0241,0007,
+ 0040,0041,0042,0043,0044,0025,0006,0027,
+ 0050,0051,0052,0053,0054,0011,0012,0033,
+ 0060,0061,0032,0063,0064,0065,0066,0010,
+ 0070,0071,0072,0073,0004,0024,0076,0341,
+ 0101,0102,0103,0104,0105,0106,0107,0110,
+ 0111,0121,0122,0123,0124,0125,0126,0127,
+ 0130,0131,0142,0143,0144,0145,0146,0147,
+ 0150,0151,0160,0161,0162,0163,0164,0165,
+ 0166,0167,0170,0200,0212,0213,0214,0215,
+ 0216,0217,0220,0232,0233,0234,0235,0236,
+ 0237,0240,0252,0253,0254,0255,0256,0257,
+ 0260,0261,0262,0263,0264,0265,0266,0267,
+ 0270,0271,0272,0273,0274,0275,0276,0277,
+ 0312,0313,0314,0315,0316,0317,0332,0333,
+ 0334,0335,0336,0337,0352,0353,0354,0355,
+ 0356,0357,0372,0373,0374,0375,0376,0377,
+};
+uchar atoibm[] =
+{
+ 0000,0001,0002,0003,0067,0055,0056,0057,
+ 0026,0005,0045,0013,0014,0015,0016,0017,
+ 0020,0021,0022,0023,0074,0075,0062,0046,
+ 0030,0031,0077,0047,0034,0035,0036,0037,
+ 0100,0132,0177,0173,0133,0154,0120,0175,
+ 0115,0135,0134,0116,0153,0140,0113,0141,
+ 0360,0361,0362,0363,0364,0365,0366,0367,
+ 0370,0371,0172,0136,0114,0176,0156,0157,
+ 0174,0301,0302,0303,0304,0305,0306,0307,
+ 0310,0311,0321,0322,0323,0324,0325,0326,
+ 0327,0330,0331,0342,0343,0344,0345,0346,
+ 0347,0350,0351,0255,0340,0275,0137,0155,
+ 0171,0201,0202,0203,0204,0205,0206,0207,
+ 0210,0211,0221,0222,0223,0224,0225,0226,
+ 0227,0230,0231,0242,0243,0244,0245,0246,
+ 0247,0250,0251,0300,0117,0320,0241,0007,
+ 0040,0041,0042,0043,0044,0025,0006,0027,
+ 0050,0051,0052,0053,0054,0011,0012,0033,
+ 0060,0061,0032,0063,0064,0065,0066,0010,
+ 0070,0071,0072,0073,0004,0024,0076,0341,
+ 0101,0102,0103,0104,0105,0106,0107,0110,
+ 0111,0121,0122,0123,0124,0125,0126,0127,
+ 0130,0131,0142,0143,0144,0145,0146,0147,
+ 0150,0151,0160,0161,0162,0163,0164,0165,
+ 0166,0167,0170,0200,0212,0213,0214,0215,
+ 0216,0217,0220,0232,0233,0234,0235,0236,
+ 0237,0240,0252,0253,0254,0255,0256,0257,
+ 0260,0261,0262,0263,0264,0265,0266,0267,
+ 0270,0271,0272,0273,0274,0275,0276,0277,
+ 0312,0313,0314,0315,0316,0317,0332,0333,
+ 0334,0335,0336,0337,0352,0353,0354,0355,
+ 0356,0357,0372,0373,0374,0375,0376,0377,
+};
diff --git a/bin/9base/diff/Makefile b/bin/9base/diff/Makefile
new file mode 100644
index 00000000..68535bfa
--- /dev/null
+++ b/bin/9base/diff/Makefile
@@ -0,0 +1,12 @@
+# diff - diff unix port from plan9
+# Depends on ../lib9
+
+TARG = diff
+OFILES = diffdir.o diffio.o diffreg.o main.o
+MANFILES = diff.1
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/diff/diff.1 b/bin/9base/diff/diff.1
new file mode 100644
index 00000000..fd42643d
--- /dev/null
+++ b/bin/9base/diff/diff.1
@@ -0,0 +1,163 @@
+.TH DIFF 1
+.SH NAME
+diff \- differential file comparator
+.SH SYNOPSIS
+.B diff
+[
+.B -acefmnbwr
+] file1 ... file2
+.SH DESCRIPTION
+.I Diff
+tells what lines must be changed in two files to bring them
+into agreement.
+If one file
+is a directory,
+then a file in that directory with basename the same as that of
+the other file is used.
+If both files are directories, similarly named files in the
+two directories are compared by the method of
+.I diff
+for text
+files and
+.IR cmp (1)
+otherwise.
+If more than two file names are given, then each argument is compared
+to the last argument as above.
+The
+.B -r
+option causes
+.I diff
+to process similarly named subdirectories recursively.
+When processing more than one file,
+.I diff
+prefixes file differences with a single line
+listing the two differing files, in the form of
+a
+.I diff
+command line.
+The
+.B -m
+flag causes this behavior even when processing single files.
+.PP
+The normal output contains lines of these forms:
+.IP "" 5
+.I n1
+.B a
+.I n3,n4
+.br
+.I n1,n2
+.B d
+.I n3
+.br
+.I n1,n2
+.B c
+.I n3,n4
+.PP
+These lines resemble
+.I ed
+commands to convert
+.I file1
+into
+.IR file2 .
+The numbers after the letters pertain to
+.IR file2 .
+In fact, by exchanging `a' for `d' and reading backward
+one may ascertain equally how to convert
+.I file2
+into
+.IR file1 .
+As in
+.IR ed ,
+identical pairs where
+.I n1
+=
+.I n2
+or
+.I n3
+=
+.I n4
+are abbreviated as a single number.
+.PP
+Following each of these lines come all the lines that are
+affected in the first file flagged by `<',
+then all the lines that are affected in the second file
+flagged by `>'.
+.PP
+The
+.B -b
+option causes
+trailing blanks (spaces and tabs) to be ignored
+and other strings of blanks to compare equal.
+The
+.B -w
+option causes all white-space to be removed from input lines
+before applying the difference algorithm.
+.PP
+The
+.B -n
+option prefixes each range with
+.IB file : \fR
+and inserts a space around the
+.BR a ,
+.BR c ,
+and
+.B d
+verbs.
+The
+.B -e
+option produces a script of
+.I "a, c"
+and
+.I d
+commands for the editor
+.IR ed ,
+which will recreate
+.I file2
+from
+.IR file1 .
+The
+.B -f
+option produces a similar script,
+not useful with
+.IR ed ,
+in the opposite order. It may, however, be
+useful as input to a stream-oriented post-processor.
+.PP
+The
+.B -c
+option includes three lines of context around each
+change, merging changes whose contexts overlap.
+The
+.B -a
+flag displays the entire file as context.
+.PP
+Except in rare circumstances,
+.I diff
+finds a smallest sufficient set of file
+differences.
+.SH FILES
+.B /tmp/diff[12]
+.SH SOURCE
+.B \*9/src/cmd/diff
+.SH "SEE ALSO"
+.IR cmp (1),
+.IR comm (1),
+.IR ed (1)
+.SH DIAGNOSTICS
+Exit status is the empty string
+for no differences,
+.L some
+for some,
+and
+.L error
+for trouble.
+.SH BUGS
+Editing scripts produced under the
+.BR -e " or"
+.BR -f " option are naive about"
+creating lines consisting of a single `\fB.\fR'.
+.PP
+When running
+.I diff
+on directories, the notion of what is a text
+file is open to debate.
diff --git a/bin/9base/diff/diff.h b/bin/9base/diff/diff.h
new file mode 100644
index 00000000..25e39c6c
--- /dev/null
+++ b/bin/9base/diff/diff.h
@@ -0,0 +1,27 @@
+#define stdout bstdout
+
+char mode; /* '\0', 'e', 'f', 'h' */
+char bflag; /* ignore multiple and trailing blanks */
+char rflag; /* recurse down directory trees */
+char mflag; /* pseudo flag: doing multiple files, one dir */
+int anychange;
+extern Biobuf stdout;
+extern int binary;
+
+#define MALLOC(t, n) ((t *)emalloc((n)*sizeof(t)))
+#define REALLOC(p, t, n) ((t *)erealloc((void *)(p), (n)*sizeof(t)))
+#define FREE(p) free((void *)(p))
+
+#define MAXPATHLEN 1024
+
+int mkpathname(char *, char *, char *);
+void *emalloc(unsigned);
+void *erealloc(void *, unsigned);
+void diff(char *, char *, int);
+void diffdir(char *, char *, int);
+void diffreg(char *, char *);
+Biobuf *prepare(int, char *);
+void panic(int, char *, ...);
+void check(Biobuf *, Biobuf *);
+void change(int, int, int, int);
+void flushchanges(void);
diff --git a/bin/9base/diff/diffdir.c b/bin/9base/diff/diffdir.c
new file mode 100644
index 00000000..b6c696ab
--- /dev/null
+++ b/bin/9base/diff/diffdir.c
@@ -0,0 +1,113 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "diff.h"
+
+static int
+itemcmp(const void *v1, const void *v2)
+{
+ char *const*d1 = v1, *const*d2 = v2;
+
+ return strcmp(*d1, *d2);
+}
+
+static char **
+scandir(char *name)
+{
+ char **cp;
+ Dir *db;
+ int nitems;
+ int fd, n;
+
+ if ((fd = open(name, OREAD)) < 0){
+ panic(mflag ? 0 : 2, "can't open %s\n", name);
+ return nil;
+ }
+ cp = 0;
+ nitems = 0;
+ if((n = dirreadall(fd, &db)) > 0){
+ while (n--) {
+ cp = REALLOC(cp, char *, (nitems+1));
+ cp[nitems] = MALLOC(char, strlen((db+n)->name)+1);
+ strcpy(cp[nitems], (db+n)->name);
+ nitems++;
+ }
+ free(db);
+ }
+ cp = REALLOC(cp, char*, (nitems+1));
+ cp[nitems] = 0;
+ close(fd);
+ qsort((char *)cp, nitems, sizeof(char*), itemcmp);
+ return cp;
+}
+
+static int
+isdotordotdot(char *p)
+{
+ if (*p == '.') {
+ if (!p[1])
+ return 1;
+ if (p[1] == '.' && !p[2])
+ return 1;
+ }
+ return 0;
+}
+
+void
+diffdir(char *f, char *t, int level)
+{
+ char **df, **dt, **dirf, **dirt;
+ char *from, *to;
+ int res;
+ char fb[MAXPATHLEN+1], tb[MAXPATHLEN+1];
+
+ df = scandir(f);
+ dt = scandir(t);
+ dirf = df;
+ dirt = dt;
+ if(df == nil || dt == nil)
+ goto Out;
+ while (*df || *dt) {
+ from = *df;
+ to = *dt;
+ if (from && isdotordotdot(from)) {
+ df++;
+ continue;
+ }
+ if (to && isdotordotdot(to)) {
+ dt++;
+ continue;
+ }
+ if (!from)
+ res = 1;
+ else if (!to)
+ res = -1;
+ else
+ res = strcmp(from, to);
+ if (res < 0) {
+ if (mode == 0 || mode == 'n')
+ Bprint(&stdout, "Only in %s: %s\n", f, from);
+ df++;
+ continue;
+ }
+ if (res > 0) {
+ if (mode == 0 || mode == 'n')
+ Bprint(&stdout, "Only in %s: %s\n", t, to);
+ dt++;
+ continue;
+ }
+ if (mkpathname(fb, f, from))
+ continue;
+ if (mkpathname(tb, t, to))
+ continue;
+ diff(fb, tb, level+1);
+ df++; dt++;
+ }
+Out:
+ for (df = dirf; df && *df; df++)
+ FREE(*df);
+ for (dt = dirt; dt && *dt; dt++)
+ FREE(*dt);
+ FREE(dirf);
+ FREE(dirt);
+}
diff --git a/bin/9base/diff/diffio.c b/bin/9base/diff/diffio.c
new file mode 100644
index 00000000..93de4e37
--- /dev/null
+++ b/bin/9base/diff/diffio.c
@@ -0,0 +1,387 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "diff.h"
+
+struct line {
+ int serial;
+ int value;
+};
+extern struct line *file[2];
+extern int len[2];
+extern long *ixold, *ixnew;
+extern int *J;
+
+static Biobuf *input[2];
+static char *file1, *file2;
+static int firstchange;
+
+#define MAXLINELEN 4096
+#define MIN(x, y) ((x) < (y) ? (x): (y))
+
+static int
+readline(Biobuf *bp, char *buf)
+{
+ int c;
+ char *p, *e;
+
+ p = buf;
+ e = p + MAXLINELEN-1;
+ do {
+ c = Bgetc(bp);
+ if (c < 0) {
+ if (p == buf)
+ return -1;
+ break;
+ }
+ if (c == '\n')
+ break;
+ *p++ = c;
+ } while (p < e);
+ *p = 0;
+ if (c != '\n' && c >= 0) {
+ do c = Bgetc(bp);
+ while (c >= 0 && c != '\n');
+ }
+ return p - buf;
+}
+
+#define HALFLONG 16
+#define low(x) (x&((1L<<HALFLONG)-1))
+#define high(x) (x>>HALFLONG)
+
+/*
+ * hashing has the effect of
+ * arranging line in 7-bit bytes and then
+ * summing 1-s complement in 16-bit hunks
+ */
+static int
+readhash(Biobuf *bp, char *buf)
+{
+ long sum;
+ unsigned shift;
+ char *p;
+ int len, space;
+
+ sum = 1;
+ shift = 0;
+ if ((len = readline(bp, buf)) == -1)
+ return 0;
+ p = buf;
+ switch(bflag) /* various types of white space handling */
+ {
+ case 0:
+ while (len--) {
+ sum += (long)*p++ << (shift &= (HALFLONG-1));
+ shift += 7;
+ }
+ break;
+ case 1:
+ /*
+ * coalesce multiple white-space
+ */
+ for (space = 0; len--; p++) {
+ if (isspace((uchar)*p)) {
+ space++;
+ continue;
+ }
+ if (space) {
+ shift += 7;
+ space = 0;
+ }
+ sum += (long)*p << (shift &= (HALFLONG-1));
+ shift += 7;
+ }
+ break;
+ default:
+ /*
+ * strip all white-space
+ */
+ while (len--) {
+ if (isspace((uchar)*p)) {
+ p++;
+ continue;
+ }
+ sum += (long)*p++ << (shift &= (HALFLONG-1));
+ shift += 7;
+ }
+ break;
+ }
+ sum = low(sum) + high(sum);
+ return ((short)low(sum) + (short)high(sum));
+}
+
+Biobuf *
+prepare(int i, char *arg)
+{
+ struct line *p;
+ int j, h;
+ Biobuf *bp;
+ char *cp, buf[MAXLINELEN];
+ int nbytes;
+ Rune r;
+
+ bp = Bopen(arg, OREAD);
+ if (!bp) {
+ panic(mflag ? 0: 2, "cannot open %s: %r\n", arg);
+ return 0;
+ }
+ if (binary)
+ return bp;
+ nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN));
+ if (nbytes > 0) {
+ cp = buf;
+ while (cp < buf+nbytes-UTFmax) {
+ /*
+ * heuristic for a binary file in the
+ * brave new UNICODE world
+ */
+ cp += chartorune(&r, cp);
+ if (r == 0 || (r > 0x7f && r <= 0xa0)) {
+ binary++;
+ return bp;
+ }
+ }
+ Bseek(bp, 0, 0);
+ }
+ p = MALLOC(struct line, 3);
+ for (j = 0; h = readhash(bp, buf); p[j].value = h)
+ p = REALLOC(p, struct line, (++j+3));
+ len[i] = j;
+ file[i] = p;
+ input[i] = bp; /*fix*/
+ if (i == 0) { /*fix*/
+ file1 = arg;
+ firstchange = 0;
+ }
+ else
+ file2 = arg;
+ return bp;
+}
+
+static int
+squishspace(char *buf)
+{
+ char *p, *q;
+ int space;
+
+ for (space = 0, q = p = buf; *q; q++) {
+ if (isspace((uchar)*q)) {
+ space++;
+ continue;
+ }
+ if (space && bflag == 1) {
+ *p++ = ' ';
+ space = 0;
+ }
+ *p++ = *q;
+ }
+ *p = 0;
+ return p - buf;
+}
+
+/*
+ * need to fix up for unexpected EOF's
+ */
+void
+check(Biobuf *bf, Biobuf *bt)
+{
+ int f, t, flen, tlen;
+ char fbuf[MAXLINELEN], tbuf[MAXLINELEN];
+
+ ixold[0] = ixnew[0] = 0;
+ for (f = t = 1; f < len[0]; f++) {
+ flen = readline(bf, fbuf);
+ ixold[f] = ixold[f-1] + flen + 1; /* ftell(bf) */
+ if (J[f] == 0)
+ continue;
+ do {
+ tlen = readline(bt, tbuf);
+ ixnew[t] = ixnew[t-1] + tlen + 1; /* ftell(bt) */
+ } while (t++ < J[f]);
+ if (bflag) {
+ flen = squishspace(fbuf);
+ tlen = squishspace(tbuf);
+ }
+ if (flen != tlen || strcmp(fbuf, tbuf))
+ J[f] = 0;
+ }
+ while (t < len[1]) {
+ tlen = readline(bt, tbuf);
+ ixnew[t] = ixnew[t-1] + tlen + 1; /* fseek(bt) */
+ t++;
+ }
+}
+
+static void
+range(int a, int b, char *separator)
+{
+ Bprint(&stdout, "%d", a > b ? b: a);
+ if (a < b)
+ Bprint(&stdout, "%s%d", separator, b);
+}
+
+static void
+fetch(long *f, int a, int b, Biobuf *bp, char *s)
+{
+ char buf[MAXLINELEN];
+ int maxb;
+
+ if(a <= 1)
+ a = 1;
+ if(bp == input[0])
+ maxb = len[0];
+ else
+ maxb = len[1];
+ if(b > maxb)
+ b = maxb;
+ if(a > maxb)
+ return;
+ Bseek(bp, f[a-1], 0);
+ while (a++ <= b) {
+ readline(bp, buf);
+ Bprint(&stdout, "%s%s\n", s, buf);
+ }
+}
+
+typedef struct Change Change;
+struct Change
+{
+ int a;
+ int b;
+ int c;
+ int d;
+};
+
+Change *changes;
+int nchanges;
+
+void
+change(int a, int b, int c, int d)
+{
+ char verb;
+ char buf[4];
+ Change *ch;
+
+ if (a > b && c > d)
+ return;
+ anychange = 1;
+ if (mflag && firstchange == 0) {
+ if(mode) {
+ buf[0] = '-';
+ buf[1] = mode;
+ buf[2] = ' ';
+ buf[3] = '\0';
+ } else {
+ buf[0] = '\0';
+ }
+ Bprint(&stdout, "diff %s%s %s\n", buf, file1, file2);
+ firstchange = 1;
+ }
+ verb = a > b ? 'a': c > d ? 'd': 'c';
+ switch(mode) {
+ case 'e':
+ range(a, b, ",");
+ Bputc(&stdout, verb);
+ break;
+ case 0:
+ range(a, b, ",");
+ Bputc(&stdout, verb);
+ range(c, d, ",");
+ break;
+ case 'n':
+ Bprint(&stdout, "%s:", file1);
+ range(a, b, ",");
+ Bprint(&stdout, " %c ", verb);
+ Bprint(&stdout, "%s:", file2);
+ range(c, d, ",");
+ break;
+ case 'f':
+ Bputc(&stdout, verb);
+ range(a, b, " ");
+ break;
+ case 'c':
+ case 'a':
+ if(nchanges%1024 == 0)
+ changes = erealloc(changes, (nchanges+1024)*sizeof(changes[0]));
+ ch = &changes[nchanges++];
+ ch->a = a;
+ ch->b = b;
+ ch->c = c;
+ ch->d = d;
+ return;
+ }
+ Bputc(&stdout, '\n');
+ if (mode == 0 || mode == 'n') {
+ fetch(ixold, a, b, input[0], "< ");
+ if (a <= b && c <= d)
+ Bprint(&stdout, "---\n");
+ }
+ fetch(ixnew, c, d, input[1], mode == 0 || mode == 'n' ? "> ": "");
+ if (mode != 0 && mode != 'n' && c <= d)
+ Bprint(&stdout, ".\n");
+}
+
+enum
+{
+ Lines = 3 /* number of lines of context shown */
+};
+
+int
+changeset(int i)
+{
+ while(i<nchanges && changes[i].b+1+2*Lines > changes[i+1].a)
+ i++;
+ if(i<nchanges)
+ return i+1;
+ return nchanges;
+}
+
+void
+flushchanges(void)
+{
+ int a, b, c, d, at;
+ int i, j;
+
+ if(nchanges == 0)
+ return;
+
+ for(i=0; i<nchanges; ){
+ j = changeset(i);
+ a = changes[i].a-Lines;
+ b = changes[j-1].b+Lines;
+ c = changes[i].c-Lines;
+ d = changes[j-1].d+Lines;
+ if(a < 1)
+ a = 1;
+ if(c < 1)
+ c = 1;
+ if(b > len[0])
+ b = len[0];
+ if(d > len[1])
+ d = len[1];
+ if(mode == 'a'){
+ a = 1;
+ b = len[0];
+ c = 1;
+ d = len[1];
+ j = nchanges;
+ }
+ Bprint(&stdout, "%s:", file1);
+ range(a, b, ",");
+ Bprint(&stdout, " - ");
+ Bprint(&stdout, "%s:", file2);
+ range(c, d, ",");
+ Bputc(&stdout, '\n');
+ at = a;
+ for(; i<j; i++){
+ fetch(ixold, at, changes[i].a-1, input[0], " ");
+ fetch(ixold, changes[i].a, changes[i].b, input[0], "- ");
+ fetch(ixnew, changes[i].c, changes[i].d, input[1], "+ ");
+ at = changes[i].b+1;
+ }
+ fetch(ixold, at, b, input[0], " ");
+ }
+ nchanges = 0;
+}
diff --git a/bin/9base/diff/diffreg.c b/bin/9base/diff/diffreg.c
new file mode 100644
index 00000000..bb3fd112
--- /dev/null
+++ b/bin/9base/diff/diffreg.c
@@ -0,0 +1,420 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "diff.h"
+
+/* diff - differential file comparison
+*
+* Uses an algorithm due to Harold Stone, which finds
+* a pair of longest identical subsequences in the two
+* files.
+*
+* The major goal is to generate the match vector J.
+* J[i] is the index of the line in file1 corresponding
+* to line i file0. J[i] = 0 if there is no
+* such line in file1.
+*
+* Lines are hashed so as to work in core. All potential
+* matches are located by sorting the lines of each file
+* on the hash (called value). In particular, this
+* collects the equivalence classes in file1 together.
+* Subroutine equiv replaces the value of each line in
+* file0 by the index of the first element of its
+* matching equivalence in (the reordered) file1.
+* To save space equiv squeezes file1 into a single
+* array member in which the equivalence classes
+* are simply concatenated, except that their first
+* members are flagged by changing sign.
+*
+* Next the indices that point into member are unsorted into
+* array class according to the original order of file0.
+*
+* The cleverness lies in routine stone. This marches
+* through the lines of file0, developing a vector klist
+* of "k-candidates". At step i a k-candidate is a matched
+* pair of lines x,y (x in file0 y in file1) such that
+* there is a common subsequence of lenght k
+* between the first i lines of file0 and the first y
+* lines of file1, but there is no such subsequence for
+* any smaller y. x is the earliest possible mate to y
+* that occurs in such a subsequence.
+*
+* Whenever any of the members of the equivalence class of
+* lines in file1 matable to a line in file0 has serial number
+* less than the y of some k-candidate, that k-candidate
+* with the smallest such y is replaced. The new
+* k-candidate is chained (via pred) to the current
+* k-1 candidate so that the actual subsequence can
+* be recovered. When a member has serial number greater
+* that the y of all k-candidates, the klist is extended.
+* At the end, the longest subsequence is pulled out
+* and placed in the array J by unravel.
+*
+* With J in hand, the matches there recorded are
+* check'ed against reality to assure that no spurious
+* matches have crept in due to hashing. If they have,
+* they are broken, and "jackpot " is recorded--a harmless
+* matter except that a true match for a spuriously
+* mated line may now be unnecessarily reported as a change.
+*
+* Much of the complexity of the program comes simply
+* from trying to minimize core utilization and
+* maximize the range of doable problems by dynamically
+* allocating what is needed and reusing what is not.
+* The core requirements for problems larger than somewhat
+* are (in words) 2*length(file0) + length(file1) +
+* 3*(number of k-candidates installed), typically about
+* 6n words for files of length n.
+*/
+/* TIDY THIS UP */
+struct cand {
+ int x;
+ int y;
+ int pred;
+} cand;
+struct line {
+ int serial;
+ int value;
+} *file[2], line;
+int len[2];
+int binary;
+struct line *sfile[2]; /*shortened by pruning common prefix and suffix*/
+int slen[2];
+int pref, suff; /*length of prefix and suffix*/
+int *class; /*will be overlaid on file[0]*/
+int *member; /*will be overlaid on file[1]*/
+int *klist; /*will be overlaid on file[0] after class*/
+struct cand *clist; /* merely a free storage pot for candidates */
+int clen;
+int *J; /*will be overlaid on class*/
+long *ixold; /*will be overlaid on klist*/
+long *ixnew; /*will be overlaid on file[1]*/
+/* END OF SOME TIDYING */
+
+static void
+sort(struct line *a, int n) /*shellsort CACM #201*/
+{
+ int m;
+ struct line *ai, *aim, *j, *k;
+ struct line w;
+ int i;
+
+ m = 0;
+ for (i = 1; i <= n; i *= 2)
+ m = 2*i - 1;
+ for (m /= 2; m != 0; m /= 2) {
+ k = a+(n-m);
+ for (j = a+1; j <= k; j++) {
+ ai = j;
+ aim = ai+m;
+ do {
+ if (aim->value > ai->value ||
+ aim->value == ai->value &&
+ aim->serial > ai->serial)
+ break;
+ w = *ai;
+ *ai = *aim;
+ *aim = w;
+
+ aim = ai;
+ ai -= m;
+ } while (ai > a && aim >= ai);
+ }
+ }
+}
+
+static void
+unsort(struct line *f, int l, int *b)
+{
+ int *a;
+ int i;
+
+ a = MALLOC(int, (l+1));
+ for(i=1;i<=l;i++)
+ a[f[i].serial] = f[i].value;
+ for(i=1;i<=l;i++)
+ b[i] = a[i];
+ FREE(a);
+}
+
+static void
+prune(void)
+{
+ int i,j;
+
+ for(pref=0;pref<len[0]&&pref<len[1]&&
+ file[0][pref+1].value==file[1][pref+1].value;
+ pref++ ) ;
+ for(suff=0;suff<len[0]-pref&&suff<len[1]-pref&&
+ file[0][len[0]-suff].value==file[1][len[1]-suff].value;
+ suff++) ;
+ for(j=0;j<2;j++) {
+ sfile[j] = file[j]+pref;
+ slen[j] = len[j]-pref-suff;
+ for(i=0;i<=slen[j];i++)
+ sfile[j][i].serial = i;
+ }
+}
+
+static void
+equiv(struct line *a, int n, struct line *b, int m, int *c)
+{
+ int i, j;
+
+ i = j = 1;
+ while(i<=n && j<=m) {
+ if(a[i].value < b[j].value)
+ a[i++].value = 0;
+ else if(a[i].value == b[j].value)
+ a[i++].value = j;
+ else
+ j++;
+ }
+ while(i <= n)
+ a[i++].value = 0;
+ b[m+1].value = 0;
+ j = 0;
+ while(++j <= m) {
+ c[j] = -b[j].serial;
+ while(b[j+1].value == b[j].value) {
+ j++;
+ c[j] = b[j].serial;
+ }
+ }
+ c[j] = -1;
+}
+
+static int
+newcand(int x, int y, int pred)
+{
+ struct cand *q;
+
+ clist = REALLOC(clist, struct cand, (clen+1));
+ q = clist + clen;
+ q->x = x;
+ q->y = y;
+ q->pred = pred;
+ return clen++;
+}
+
+static int
+search(int *c, int k, int y)
+{
+ int i, j, l;
+ int t;
+
+ if(clist[c[k]].y < y) /*quick look for typical case*/
+ return k+1;
+ i = 0;
+ j = k+1;
+ while((l=(i+j)/2) > i) {
+ t = clist[c[l]].y;
+ if(t > y)
+ j = l;
+ else if(t < y)
+ i = l;
+ else
+ return l;
+ }
+ return l+1;
+}
+
+static int
+stone(int *a, int n, int *b, int *c)
+{
+ int i, k,y;
+ int j, l;
+ int oldc, tc;
+ int oldl;
+
+ k = 0;
+ c[0] = newcand(0,0,0);
+ for(i=1; i<=n; i++) {
+ j = a[i];
+ if(j==0)
+ continue;
+ y = -b[j];
+ oldl = 0;
+ oldc = c[0];
+ do {
+ if(y <= clist[oldc].y)
+ continue;
+ l = search(c, k, y);
+ if(l!=oldl+1)
+ oldc = c[l-1];
+ if(l<=k) {
+ if(clist[c[l]].y <= y)
+ continue;
+ tc = c[l];
+ c[l] = newcand(i,y,oldc);
+ oldc = tc;
+ oldl = l;
+ } else {
+ c[l] = newcand(i,y,oldc);
+ k++;
+ break;
+ }
+ } while((y=b[++j]) > 0);
+ }
+ return k;
+}
+
+static void
+unravel(int p)
+{
+ int i;
+ struct cand *q;
+
+ for(i=0; i<=len[0]; i++) {
+ if (i <= pref)
+ J[i] = i;
+ else if (i > len[0]-suff)
+ J[i] = i+len[1]-len[0];
+ else
+ J[i] = 0;
+ }
+ for(q=clist+p;q->y!=0;q=clist+q->pred)
+ J[q->x+pref] = q->y+pref;
+}
+
+static void
+output(void)
+{
+ int m, i0, i1, j0, j1;
+
+ m = len[0];
+ J[0] = 0;
+ J[m+1] = len[1]+1;
+ if (mode != 'e') {
+ for (i0 = 1; i0 <= m; i0 = i1+1) {
+ while (i0 <= m && J[i0] == J[i0-1]+1)
+ i0++;
+ j0 = J[i0-1]+1;
+ i1 = i0-1;
+ while (i1 < m && J[i1+1] == 0)
+ i1++;
+ j1 = J[i1+1]-1;
+ J[i1] = j1;
+ change(i0, i1, j0, j1);
+ }
+ }
+ else {
+ for (i0 = m; i0 >= 1; i0 = i1-1) {
+ while (i0 >= 1 && J[i0] == J[i0+1]-1 && J[i0])
+ i0--;
+ j0 = J[i0+1]-1;
+ i1 = i0+1;
+ while (i1 > 1 && J[i1-1] == 0)
+ i1--;
+ j1 = J[i1-1]+1;
+ J[i1] = j1;
+ change(i1 , i0, j1, j0);
+ }
+ }
+ if (m == 0)
+ change(1, 0, 1, len[1]);
+ flushchanges();
+}
+
+#define BUF 4096
+static int
+cmp(Biobuf* b1, Biobuf* b2)
+{
+ int n;
+ uchar buf1[BUF], buf2[BUF];
+ int f1, f2;
+ vlong nc = 1;
+ uchar *b1s, *b1e, *b2s, *b2e;
+
+ f1 = Bfildes(b1);
+ f2 = Bfildes(b2);
+ seek(f1, 0, 0);
+ seek(f2, 0, 0);
+ b1s = b1e = buf1;
+ b2s = b2e = buf2;
+ for(;;){
+ if(b1s >= b1e){
+ if(b1s >= &buf1[BUF])
+ b1s = buf1;
+ n = read(f1, b1s, &buf1[BUF] - b1s);
+ b1e = b1s + n;
+ }
+ if(b2s >= b2e){
+ if(b2s >= &buf2[BUF])
+ b2s = buf2;
+ n = read(f2, b2s, &buf2[BUF] - b2s);
+ b2e = b2s + n;
+ }
+ n = b2e - b2s;
+ if(n > b1e - b1s)
+ n = b1e - b1s;
+ if(n <= 0)
+ break;
+ if(memcmp((void *)b1s, (void *)b2s, n) != 0){
+ return 1;
+ }
+ nc += n;
+ b1s += n;
+ b2s += n;
+ }
+ if(b1e - b1s == b2e - b2s)
+ return 0;
+ return 1;
+}
+
+void
+diffreg(char *f, char *t)
+{
+ Biobuf *b0, *b1;
+ int k;
+
+ binary = 0;
+ b0 = prepare(0, f);
+ if (!b0)
+ return;
+ b1 = prepare(1, t);
+ if (!b1) {
+ FREE(file[0]);
+ Bterm(b0);
+ return;
+ }
+ if (binary){
+ /* could use b0 and b1 but this is simpler. */
+ if (cmp(b0, b1))
+ print("binary files %s %s differ\n", f, t);
+ Bterm(b0);
+ Bterm(b1);
+ return;
+ }
+ clen = 0;
+ prune();
+ sort(sfile[0], slen[0]);
+ sort(sfile[1], slen[1]);
+
+ member = (int *)file[1];
+ equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+ member = REALLOC(member, int, slen[1]+2);
+
+ class = (int *)file[0];
+ unsort(sfile[0], slen[0], class);
+ class = REALLOC(class, int, slen[0]+2);
+
+ klist = MALLOC(int, slen[0]+2);
+ clist = MALLOC(struct cand, 1);
+ k = stone(class, slen[0], member, klist);
+ FREE(member);
+ FREE(class);
+
+ J = MALLOC(int, len[0]+2);
+ unravel(klist[k]);
+ FREE(clist);
+ FREE(klist);
+
+ ixold = MALLOC(long, len[0]+2);
+ ixnew = MALLOC(long, len[1]+2);
+ Bseek(b0, 0, 0); Bseek(b1, 0, 0);
+ check(b0, b1);
+ output();
+ FREE(J); FREE(ixold); FREE(ixnew);
+ Bterm(b0); Bterm(b1); /* ++++ */
+}
diff --git a/bin/9base/diff/main.c b/bin/9base/diff/main.c
new file mode 100644
index 00000000..408ad90f
--- /dev/null
+++ b/bin/9base/diff/main.c
@@ -0,0 +1,270 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "diff.h"
+
+#define DIRECTORY(s) ((s)->qid.type&QTDIR)
+#define REGULAR_FILE(s) ((s)->type == 'M' && !DIRECTORY(s))
+
+Biobuf stdout;
+
+static char *tmp[] = {"/tmp/diff1XXXXXXXXXXX", "/tmp/diff2XXXXXXXXXXX"};
+static int whichtmp;
+static char *progname;
+static char usage[] = "diff [ -acefmnbwr ] file1 ... file2\n";
+
+static void
+rmtmpfiles(void)
+{
+ while (whichtmp > 0) {
+ whichtmp--;
+ remove(tmp[whichtmp]);
+ }
+}
+
+void
+done(int status)
+{
+ rmtmpfiles();
+ switch(status)
+ {
+ case 0:
+ exits("");
+ case 1:
+ exits("some");
+ default:
+ exits("error");
+ }
+ /*NOTREACHED*/
+}
+
+void
+panic(int status, char *fmt, ...)
+{
+ va_list arg;
+
+ Bflush(&stdout);
+
+ fprint(2, "%s: ", progname);
+ va_start(arg, fmt);
+ vfprint(2, fmt, arg);
+ va_end(arg);
+ if (status)
+ done(status);
+ /*NOTREACHED*/
+}
+
+static int
+catch(void *a, char *msg)
+{
+ USED(a);
+ panic(2, msg);
+ return 1;
+}
+
+int
+mkpathname(char *pathname, char *path, char *name)
+{
+ if (strlen(path) + strlen(name) > MAXPATHLEN) {
+ panic(0, "pathname %s/%s too long\n", path, name);
+ return 1;
+ }
+ sprint(pathname, "%s/%s", path, name);
+ return 0;
+}
+
+static char *
+mktmpfile(int input, Dir **sb)
+{
+ int fd, i;
+ char *p;
+ char buf[8192];
+
+ atnotify(catch, 1);
+/*
+ p = mktemp(tmp[whichtmp++]);
+ fd = create(p, OWRITE, 0600);
+*/
+ fd = mkstemp(p=tmp[whichtmp++]);
+ if (fd < 0) {
+ panic(mflag ? 0: 2, "cannot create %s: %r\n", p);
+ return 0;
+ }
+ while ((i = read(input, buf, sizeof(buf))) > 0) {
+ if ((i = write(fd, buf, i)) < 0)
+ break;
+ }
+ *sb = dirfstat(fd);
+ close(fd);
+ if (i < 0) {
+ panic(mflag ? 0: 2, "cannot read/write %s: %r\n", p);
+ return 0;
+ }
+ return p;
+}
+
+static char *
+statfile(char *file, Dir **sb)
+{
+ Dir *dir;
+ int input;
+
+ dir = dirstat(file);
+ if(dir == nil) {
+ if (strcmp(file, "-") || (dir = dirfstat(0)) == nil) {
+ panic(mflag ? 0: 2, "cannot stat %s: %r\n", file);
+ return 0;
+ }
+ free(dir);
+ return mktmpfile(0, sb);
+ }
+ else if (!REGULAR_FILE(dir) && !DIRECTORY(dir)) {
+ free(dir);
+ if ((input = open(file, OREAD)) == -1) {
+ panic(mflag ? 0: 2, "cannot open %s: %r\n", file);
+ return 0;
+ }
+ file = mktmpfile(input, sb);
+ close(input);
+ }
+ else
+ *sb = dir;
+ return file;
+}
+
+void
+diff(char *f, char *t, int level)
+{
+ char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1];
+ Dir *fsb, *tsb;
+
+ if ((fp = statfile(f, &fsb)) == 0)
+ goto Return;
+ if ((tp = statfile(t, &tsb)) == 0){
+ free(fsb);
+ goto Return;
+ }
+ if (DIRECTORY(fsb) && DIRECTORY(tsb)) {
+ if (rflag || level == 0)
+ diffdir(fp, tp, level);
+ else
+ Bprint(&stdout, "Common subdirectories: %s and %s\n",
+ fp, tp);
+ }
+ else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb))
+ diffreg(fp, tp);
+ else {
+ if (REGULAR_FILE(fsb)) {
+ if ((p = utfrrune(f, '/')) == 0)
+ p = f;
+ else
+ p++;
+ if (mkpathname(tb, tp, p) == 0)
+ diffreg(fp, tb);
+ }
+ else {
+ if ((p = utfrrune(t, '/')) == 0)
+ p = t;
+ else
+ p++;
+ if (mkpathname(fb, fp, p) == 0)
+ diffreg(fb, tp);
+ }
+ }
+ free(fsb);
+ free(tsb);
+Return:
+ rmtmpfiles();
+}
+
+void
+main(int argc, char *argv[])
+{
+ char *p;
+ int i;
+ Dir *fsb, *tsb;
+ extern int _p9usepwlibrary;
+
+ _p9usepwlibrary = 0;
+ Binit(&stdout, 1, OWRITE);
+ progname = *argv;
+ while (--argc && (*++argv)[0] == '-' && (*argv)[1]) {
+ for (p = *argv+1; *p; p++) {
+ switch (*p) {
+
+ case 'e':
+ case 'f':
+ case 'n':
+ case 'c':
+ case 'a':
+ mode = *p;
+ break;
+
+ case 'w':
+ bflag = 2;
+ break;
+
+ case 'b':
+ bflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ mflag = 1;
+ break;
+
+ case 'm':
+ mflag = 1;
+ break;
+
+ case 'h':
+ default:
+ progname = "Usage";
+ panic(2, usage);
+ }
+ }
+ }
+ if (argc < 2)
+ panic(2, usage, progname);
+ if ((tsb = dirstat(argv[argc-1])) == nil)
+ panic(2, "can't stat %s\n", argv[argc-1]);
+ if (argc > 2) {
+ if (!DIRECTORY(tsb))
+ panic(2, usage, progname);
+ mflag = 1;
+ }
+ else {
+ if ((fsb = dirstat(argv[0])) == nil)
+ panic(2, "can't stat %s\n", argv[0]);
+ if (DIRECTORY(fsb) && DIRECTORY(tsb))
+ mflag = 1;
+ free(fsb);
+ }
+ free(tsb);
+ for (i = 0; i < argc-1; i++)
+ diff(argv[i], argv[argc-1], 0);
+ done(anychange);
+ /*NOTREACHED*/
+}
+
+static char noroom[] = "out of memory - try diff -h\n";
+
+void *
+emalloc(unsigned n)
+{
+ register void *p;
+
+ if ((p = malloc(n)) == 0)
+ panic(2, noroom);
+ return p;
+}
+
+void *
+erealloc(void *p, unsigned n)
+{
+ register void *rp;
+
+ if ((rp = realloc(p, n)) == 0)
+ panic(2, noroom);
+ return rp;
+}
diff --git a/bin/9base/du/Makefile b/bin/9base/du/Makefile
new file mode 100644
index 00000000..342de741
--- /dev/null
+++ b/bin/9base/du/Makefile
@@ -0,0 +1,10 @@
+# du - du unix port from plan9
+# Depends on ../lib9
+
+TARG = du
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/du/du.1 b/bin/9base/du/du.1
new file mode 100644
index 00000000..9ada0cb4
--- /dev/null
+++ b/bin/9base/du/du.1
@@ -0,0 +1,9 @@
+.TH DU 1
+.SH NAME
+du
+.SH SYNOPSIS
+.B du
+.SH DESCRIPTION
+.I du
+.SH SOURCE
+.B \*9/src/cmd/du.c
diff --git a/bin/9base/du/du.c b/bin/9base/du/du.c
new file mode 100644
index 00000000..f62c45b7
--- /dev/null
+++ b/bin/9base/du/du.c
@@ -0,0 +1,194 @@
+#include <u.h>
+#include <libc.h>
+
+extern vlong du(char*, Dir*);
+extern vlong k(vlong);
+extern void err(char*);
+extern int warn(char*);
+extern int seen(Dir*);
+
+int aflag;
+int fflag;
+int nflag;
+int sflag;
+int tflag;
+int uflag;
+int qflag;
+char *fmt = "%llud\t%s\n";
+vlong blocksize = 1024LL;
+
+void
+main(int argc, char *argv[])
+{
+ int i;
+ char *s, *ss;
+
+ ARGBEGIN {
+ case 'a': /* all files */
+ aflag = 1;
+ break;
+ case 's': /* only top level */
+ sflag = 1;
+ break;
+ case 'f': /* ignore errors */
+ fflag = 1;
+ break;
+ case 'n': /* all files, number of bytes */
+ aflag = 1;
+ nflag = 1;
+ break;
+ case 't': /* return modified/accessed time */
+ tflag = 1;
+ break;
+ case 'u': /* accessed time */
+ uflag = 1;
+ break;
+ case 'q': /* qid */
+ fmt = "%.16llux\t%s\n";
+ qflag = 1;
+ break;
+ case 'b': /* block size */
+ s = ARGF();
+ if(s) {
+ blocksize = strtoul(s, &ss, 0);
+ if(s == ss)
+ blocksize = 1;
+ if(*ss == 'k')
+ blocksize *= 1024;
+ }
+ break;
+ } ARGEND
+ if(argc==0)
+ print(fmt, du(".", dirstat(".")), ".");
+ else
+ for(i=0; i<argc; i++)
+ print(fmt, du(argv[i], dirstat(argv[i])), argv[i]);
+ exits(0);
+}
+
+vlong
+du(char *name, Dir *dir)
+{
+ int fd, i, n;
+ Dir *buf, *d;
+ char file[256];
+ vlong nk, t;
+
+ if(dir == nil)
+ return warn(name);
+
+ fd = open(name, OREAD);
+ if(fd < 0)
+ return warn(name);
+
+ if((dir->qid.type&QTDIR) == 0)
+ nk = k(dir->length);
+ else{
+ nk = 0;
+ while((n=dirread(fd, &buf)) > 0) {
+ d = buf;
+ for(i=0; i<n; i++, d++) {
+ if((d->qid.type&QTDIR) == 0) {
+ t = k(d->length);
+ nk += t;
+ if(aflag) {
+ sprint(file, "%s/%s", name, d->name);
+ if(tflag) {
+ t = d->mtime;
+ if(uflag)
+ t = d->atime;
+ }
+ if(qflag)
+ t = d->qid.path;
+ print(fmt, t, file);
+ }
+ continue;
+ }
+ if(strcmp(d->name, ".") == 0 ||
+ strcmp(d->name, "..") == 0 ||
+ seen(d))
+ continue;
+ sprint(file, "%s/%s", name, d->name);
+ t = du(file, d);
+ nk += t;
+ if(tflag) {
+ t = d->mtime;
+ if(uflag)
+ t = d->atime;
+ }
+ if(qflag)
+ t = d->qid.path;
+ if(!sflag)
+ print(fmt, t, file);
+ }
+ free(buf);
+ }
+ if(n < 0)
+ warn(name);
+ }
+ close(fd);
+ if(tflag) {
+ if(uflag)
+ return dir->atime;
+ return dir->mtime;
+ }
+ if(qflag)
+ return dir->qid.path;
+ return nk;
+}
+
+#define NCACHE 128 /* must be power of two */
+typedef struct Cache Cache;
+struct Cache
+{
+ Dir* cache;
+ int n;
+ int max;
+} cache[NCACHE];
+
+int
+seen(Dir *dir)
+{
+ Dir *dp;
+ int i;
+ Cache *c;
+
+ c = &cache[dir->qid.path&(NCACHE-1)];
+ dp = c->cache;
+ for(i=0; i<c->n; i++, dp++)
+ if(dir->qid.path == dp->qid.path &&
+ dir->type == dp->type &&
+ dir->dev == dp->dev)
+ return 1;
+ if(c->n == c->max){
+ c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir));
+ if(c->cache == 0)
+ err("malloc failure");
+ }
+ c->cache[c->n++] = *dir;
+ return 0;
+}
+
+void
+err(char *s)
+{
+ fprint(2, "du: %s: %r\n", s);
+ exits(s);
+}
+
+int
+warn(char *s)
+{
+ if(fflag == 0)
+ fprint(2, "du: %s: %r\n", s);
+ return 0;
+}
+
+vlong
+k(vlong n)
+{
+ if(nflag)
+ return n;
+ n = (n+blocksize-1)/blocksize;
+ return n*blocksize/1024LL;
+}
diff --git a/bin/9base/echo/Makefile b/bin/9base/echo/Makefile
new file mode 100644
index 00000000..bcb0ca88
--- /dev/null
+++ b/bin/9base/echo/Makefile
@@ -0,0 +1,10 @@
+# echo - echo unix port from plan9
+# Depends on ../lib9
+
+TARG = echo
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/echo/echo.1 b/bin/9base/echo/echo.1
new file mode 100644
index 00000000..cfeb58c5
--- /dev/null
+++ b/bin/9base/echo/echo.1
@@ -0,0 +1,26 @@
+.TH ECHO 1
+.SH NAME
+echo \- print arguments
+.SH SYNOPSIS
+.B echo
+[
+.B -n
+]
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.I Echo
+writes its arguments separated by blanks and terminated by
+a newline on the standard output.
+Option
+.B -n
+suppresses the newline.
+.SH SOURCE
+.B \*9/src/cmd/echo.c
+.SH DIAGNOSTICS
+If
+.I echo
+draws an error while writing to standard output, the exit status is
+.LR "write error" .
+Otherwise the exit status is empty.
diff --git a/bin/9base/echo/echo.c b/bin/9base/echo/echo.c
new file mode 100644
index 00000000..25b69692
--- /dev/null
+++ b/bin/9base/echo/echo.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+ int nflag;
+ int i, len;
+ char *buf, *p;
+
+ nflag = 0;
+ if(argc > 1 && strcmp(argv[1], "-n") == 0)
+ nflag = 1;
+
+ len = 1;
+ for(i = 1+nflag; i < argc; i++)
+ len += strlen(argv[i])+1;
+
+ buf = malloc(len);
+ if(buf == 0)
+ exits("no memory");
+
+ p = buf;
+ for(i = 1+nflag; i < argc; i++){
+ strcpy(p, argv[i]);
+ p += strlen(p);
+ if(i < argc-1)
+ *p++ = ' ';
+ }
+
+ if(!nflag)
+ *p++ = '\n';
+
+ if(write(1, buf, p-buf) < 0)
+ fprint(2, "echo: write error: %r\n");
+
+ exits((char *)0);
+}
diff --git a/bin/9base/ed/Makefile b/bin/9base/ed/Makefile
new file mode 100644
index 00000000..c05ca04c
--- /dev/null
+++ b/bin/9base/ed/Makefile
@@ -0,0 +1,10 @@
+# ed - ed unix port from plan9
+# Depends on ../lib9
+
+TARG = ed
+
+include ../std.mk
+
+pre-uninstall:
+
+post-install:
diff --git a/bin/9base/ed/ed.1 b/bin/9base/ed/ed.1
new file mode 100644
index 00000000..00eb095a
--- /dev/null
+++ b/bin/9base/ed/ed.1
@@ -0,0 +1,683 @@
+.TH ED 1
+.SH NAME
+ed \- text editor
+.SH SYNOPSIS
+.B ed
+[
+.B -
+]
+[
+.B -o
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Ed
+is a venerable text editor.
+.PP
+If a
+.I file
+argument is given,
+.I ed
+simulates an
+.L e
+command (see below) on that file:
+it is read into
+.I ed's
+buffer so that it can be edited.
+The options are
+.TP
+.B -
+Suppress the printing
+of character counts by
+.LR e ,
+.LR r ,
+and
+.L w
+commands and of the confirming
+.L !
+by
+.L !
+commands.
+.TP
+.B -o
+(for output piping)
+Write all output to the standard error file except writing by
+.L w
+commands.
+If no
+.I file
+is given, make
+.B /dev/stdout
+the remembered file; see the
+.L e
+command below.
+.PP
+.I Ed
+operates on a `buffer', a copy of the file it is editing;
+changes made
+in the buffer have no effect on the file until a
+.L w
+(write)
+command is given.
+The copy of the text being edited resides
+in a temporary file called the
+.IR buffer .
+.PP
+Commands to
+.I ed
+have a simple and regular structure: zero, one, or
+two
+.I addresses
+followed by a single character
+.IR command ,
+possibly
+followed by parameters to the command.
+These addresses specify one or more lines in the buffer.
+Missing addresses are supplied by default.
+.PP
+In general, only one command may appear on a line.
+Certain commands allow the
+addition of text to the buffer.
+While
+.I ed
+is accepting text, it is said
+to be in
+.I "input mode."
+In this mode, no commands are recognized;
+all input is merely collected.
+Input mode is left by typing a period
+.L .
+alone at the
+beginning of a line.
+.PP
+.I Ed
+supports the
+.I "regular expression"
+notation described in
+.IR regexp (7).
+Regular expressions are used in addresses to specify
+lines and in one command
+(see
+.I s
+below)
+to specify a portion of a line which is to be replaced.
+If it is desired to use one of
+the regular expression metacharacters as an ordinary
+character, that character may be preceded by
+.RB ` \e '.
+This also applies to the character bounding the regular
+expression (often
+.LR / )
+and to
+.L \e
+itself.
+.PP
+To understand addressing in
+.I ed
+it is necessary to know that at any time there is a
+.I "current line."
+Generally, the current line is
+the last line affected by a command; however,
+the exact effect on the current line
+is discussed under the description of
+each command.
+Addresses are constructed as follows.
+.TP
+1.
+The character
+.LR . ,
+customarily called `dot',
+addresses the current line.
+.TP
+2.
+The character
+.L $
+addresses the last line of the buffer.
+.TP
+3.
+A decimal number
+.I n
+addresses the
+.IR n -th
+line of the buffer.
+.TP
+4.
+.BI \'x
+addresses the line marked with the name
+.IR x ,
+which must be a lower-case letter.
+Lines are marked with the
+.L k
+command.
+.TP
+5.
+A regular expression enclosed in slashes (
+.LR / )
+addresses
+the line found by searching forward from the current line
+and stopping at the first line containing a
+string that matches the regular expression.
+If necessary the search wraps around to the beginning of the
+buffer.
+.TP
+6.
+A regular expression enclosed in queries
+.L ?
+addresses
+the line found by searching backward from the current line
+and stopping at the first line containing
+a string that matches the regular expression.
+If necessary
+the search wraps around to the end of the buffer.
+.TP
+7.
+An address followed by a plus sign
+.L +
+or a minus sign
+.L -
+followed by a decimal number specifies that address plus
+(resp. minus) the indicated number of lines.
+The plus sign may be omitted.
+.TP
+8.
+An address followed by
+.L +
+(or
+.LR - )
+followed by a
+regular expression enclosed in slashes specifies the first
+matching line following (or preceding) that address.
+The search wraps around if necessary.
+The
+.L +
+may be omitted, so
+.L 0/x/
+addresses the
+.I first
+line in the buffer with an
+.LR x .
+Enclosing the regular expression in
+.L ?
+reverses the search direction.
+.TP
+9.
+If an address begins with
+.L +
+or
+.L -
+the addition or subtraction is taken with respect to the current line;
+e.g.\&
+.L -5
+is understood to mean
+.LR .-5 .
+.TP
+10.
+If an address ends with
+.L +
+or
+.LR - ,
+then 1 is added (resp. subtracted).
+As a consequence of this rule and rule 9,
+the address
+.L -
+refers to the line before the current line.
+Moreover,
+trailing
+.L +
+and
+.L -
+characters
+have cumulative effect, so
+.L --
+refers to the current
+line less 2.
+.TP
+11.
+To maintain compatibility with earlier versions of the editor,
+the character
+.L ^
+in addresses is
+equivalent to
+.LR - .
+.PP
+Commands may require zero, one, or two addresses.
+Commands which require no addresses regard the presence
+of an address as an error.
+Commands which accept one or two addresses
+assume default addresses when insufficient are given.
+If more addresses are given than a command requires,
+the last one or two (depending on what is accepted) are used.
+.PP
+Addresses are separated from each other typically by a comma
+.LR , .
+They may also be separated by a semicolon
+.LR ; .
+In this case the current line
+is set to
+the previous address before the next address is interpreted.
+If no address precedes a comma or semicolon, line 1 is assumed;
+if no address follows, the last line of the buffer is assumed.
+The second address of any two-address sequence
+must correspond to a line following the line corresponding to the first address.
+.PP
+In the following list of
+.I ed
+commands, the default addresses
+are shown in parentheses.
+The parentheses are not part of
+the address, but are used to show that the given addresses are
+the default.
+`Dot' means the current line.
+.TP
+.RB (\|\fL.\fP\|) \|a
+.br
+.ns
+.TP
+<text>
+.br
+.ns
+.TP
+.B .
+Read the given text
+and append it after the addressed line.
+Dot is left
+on the last line input, if there
+were any, otherwise at the addressed line.
+Address
+.L 0
+is legal for this command; text is placed
+at the beginning of the buffer.
+.TP
+.RB (\|\fL.,.\fP\|) \|b [ +- ][\fIpagesize\fP][ pln\fR]
+Browse.
+Print a `page', normally 20 lines.
+The optional
+.L +
+(default) or
+.L -
+specifies whether the next or previous
+page is to be printed.
+The optional
+.I pagesize
+is the number of lines in a page.
+The optional
+.LR p ,
+.LR n ,
+or
+.L l
+causes printing in the specified format, initially
+.LR p .
+Pagesize and format are remembered between
+.L b
+commands.
+Dot is left at the last line displayed.
+.TP
+.RB (\|\fL.,.\fP\|) \|c
+.br
+.ns
+.TP
+<text>
+.br
+.ns
+.TP
+.B .
+Change.
+Delete the addressed lines, then accept input
+text to replace these lines.
+Dot is left at the last line input; if there were none,
+it is left at the line preceding the deleted lines.
+.TP
+.RB (\|\fL.,.\fP\|) \|d
+Delete the addressed lines from the buffer.
+Dot is set to the line following the last line deleted, or to
+the last line of the buffer if the deleted lines had no successor.
+.TP
+.BI e " filename"
+Edit.
+Delete the entire contents of the buffer;
+then read the named file into the buffer.
+Dot is set to the last line of the buffer.
+The number of characters read is typed.
+The file name is remembered for possible use in later
+.LR e ,
+.LR r ,
+or
+.L w
+commands.
+If
+.I filename
+is missing, the remembered name is used.
+.TP
+.BI E " filename"
+Unconditional
+.LR e ;
+see
+.RL ` q '
+below.
+.TP
+.BI f " filename"
+Print the currently remembered file name.
+If
+.I filename
+is given,
+the currently remembered file name is first changed to
+.IR filename .
+.TP
+.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP/\fIcommand\ list\fP
+.PD 0
+.TP
+.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP/
+.TP
+.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP
+.PD
+Global.
+First mark every line which matches
+the given
+.IR regular expression .
+Then for every such line, execute the
+.I command list
+with dot initially set to that line.
+A single command or the first of multiple commands
+appears on the same line with the global command.
+All lines of a multi-line list except the last line must end with
+.LR \e .
+The
+.RB \&` \&. \&'
+terminating input mode for an
+.LR a ,
+.LR i ,
+.L c
+command may be omitted if it would be on the
+last line of the command list.
+The commands
+.L g
+and
+.L v
+are not permitted in the command list.
+Any character other than space or newline may
+be used instead of
+.L /
+to delimit the regular expression.
+The second and third forms mean
+.BI g/ regular\ expression /p \f1.
+.TP
+.RB (\| .\| ) \|i
+.PD 0
+.TP
+<text>
+.TP
+.B .
+Insert the given text before the addressed line.
+Dot is left at the last line input, or, if there were none,
+at the line before the addressed line.
+This command differs from the
+.I a
+command only in the placement of the
+text.
+.PD
+.TP
+.RB (\| .,.+1 \|) \|j
+Join the addressed lines into a single line;
+intermediate newlines are deleted.
+Dot is left at the resulting line.
+.TP
+.RB (\|\fL.\fP\|) \|k\fIx\fP
+Mark the addressed line with name
+.IR x ,
+which must be a lower-case letter.
+The address form
+.BI \' x
+then addresses this line.
+.ne 2.5
+.TP
+.RB (\|\fL.,.\fP\|) \|l
+List.
+Print the addressed lines in an unambiguous way:
+a tab is printed as
+.LR \et ,
+a backspace as
+.LR \eb ,
+backslashes as
+.LR \e\e ,
+and non-printing characters as
+a backslash, an
+.LR x ,
+and four hexadecimal digits.
+Long lines are folded,
+with the second and subsequent sub-lines indented one tab stop.
+If the last character in the line is a blank,
+it is followed by
+.LR \en .
+An
+.L l
+may be appended, like
+.LR p ,
+to any non-I/O command.
+.TP
+.RB (\|\fL.,.\fP\|) \|m\fIa
+Move.
+Reposition the addressed lines after the line
+addressed by
+.IR a .
+Dot is left at the last moved line.
+.TP
+.RB (\|\fL.,.\fP\|) \|n
+Number.
+Perform
+.LR p ,
+prefixing each line with its line number and a tab.
+An
+.L n
+may be appended, like
+.LR p ,
+to any non-I/O command.
+.TP
+.RB (\|\fL.,.\fP\|) \|p
+Print the addressed lines.
+Dot is left at the last line printed.
+A
+.L p
+appended to any non-I/O command causes the then current line
+to be printed after the command is executed.
+.TP
+.RB (\|\fL.,.\fP\|) \|P
+This command is a synonym for
+.LR p .
+.TP
+.B q
+Quit the editor.
+No automatic write
+of a file is done.
+A
+.L q
+or
+.L e
+command is considered to be in error if the buffer has
+been modified since the last
+.LR w ,
+.LR q ,
+or
+.L e
+command.
+.TP
+.B Q
+Quit unconditionally.
+.TP
+.RB ( $ )\|r\ \fIfilename\fP
+Read in the given file after the addressed line.
+If no
+.I filename
+is given, the remembered file name is used.
+The file name is remembered if there were no
+remembered file name already.
+If the read is successful, the number of characters
+read is printed.
+Dot is left at the last line read from the file.
+.TP
+.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP/
+.PD 0
+.TP
+.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP/g
+.TP
+.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP
+.PD
+Substitute.
+Search each addressed
+line for an occurrence of the specified regular expression.
+On each line in which
+.I n
+matches are found
+.RI ( n
+defaults to 1 if missing),
+the
+.IR n th
+matched string is replaced by the replacement specified.
+If the global replacement indicator
+.L g
+appears after the command,
+all subsequent matches on the line are also replaced.
+It is an error for the substitution to fail on all addressed lines.
+Any character other than space or newline
+may be used instead of
+.L /
+to delimit the regular expression
+and the replacement.
+Dot is left at the last line substituted.
+The third form means
+.BI s n / regular\ expression / replacement\fP/p\f1.
+The second
+.L /
+may be omitted if the replacement is
+empty.
+.IP
+An ampersand
+.L &
+appearing in the replacement
+is replaced by the string matching the regular expression.
+The characters
+.BI \e n\f1,
+where
+.I n
+is a digit,
+are replaced by the text matched by the
+.IR n -th
+regular subexpression
+enclosed between
+.L (
+and
+.LR ) .
+When
+nested parenthesized subexpressions
+are present,
+.I n
+is determined by counting occurrences of
+.L (
+starting from the left.
+.IP
+A literal
+.LR & ,
+.LR / ,
+.L \e
+or newline may be included in a replacement
+by prefixing it with
+.LR \e .
+.TP
+.RB (\|\fL.,.\fP\|) \|t\|\fIa
+Transfer.
+Copy the addressed lines
+after the line addressed by
+.IR a .
+Dot is left at the last line of the copy.
+.TP
+.RB (\|\fL.,.\fP\|) \|u
+Undo.
+Restore the preceding contents
+of the first addressed line (sic), which must be the last line
+in which a substitution was made (double sic).
+.TP
+.RB (\|\fL1,$\fP\|) \|v/\fIregular\ expression\fP/\fIcommand\ list\fP
+This command is the same as the global command
+.L g
+except that the command list is executed with
+dot initially set to every line
+.I except
+those
+matching the regular expression.
+.TP
+.RB (\|\fL1,$\fP\|) \|w " \fIfilename\fP"
+Write the addressed lines to
+the given file.
+If the file does not exist,
+it is created with mode 666 (readable and writable by everyone).
+If no
+.I filename
+is given, the remembered file name, if any, is used.
+The file name is remembered if there were no
+remembered file name already.
+Dot is unchanged.
+If the write is successful, the number of characters written is
+printed.
+.TP
+.RB (\|\fL1,$\fP\|) \|W " \fIfilename\fP"
+Perform
+.LR w ,
+but append to, instead of overwriting, any existing file contents.
+.TP
+.RB ( $ ) \|=
+Print the line number of the addressed line.
+Dot is unchanged.
+.TP
+.BI ! shell\ command
+Send the remainder of the line after the
+.L !
+to
+.IR rc (1)
+to be interpreted as a command.
+Dot is unchanged.
+.TP
+.RB (\| .+1 )\|<newline>
+An address without a command is taken as a
+.L p
+command.
+A terminal
+.L /
+may be omitted from the address.
+A blank line alone is equivalent to
+.LR .+1p ;
+it is useful
+for stepping through text.
+.PP
+If an interrupt signal
+.SM (DEL)
+is sent,
+.I ed
+prints a
+.L ?
+and returns to its command level.
+.PP
+When reading a file,
+.I ed
+discards
+.SM NUL
+characters
+and all characters after the last newline.
+.SH FILES
+.B /tmp/e*
+.br
+.B ed.hup
+\ \ work is saved here if terminal hangs up
+.SH SOURCE
+.B \*9/src/cmd/ed.c
+.SH "SEE ALSO"
+.IR sam (1),
+.IR sed (1),
+.IR regexp (7)
+.SH DIAGNOSTICS
+.BI ? name
+for inaccessible file;
+.L ?TMP
+for temporary file overflow;
+.L ?
+for errors in commands or other overflows.
diff --git a/bin/9base/ed/ed.c b/bin/9base/ed/ed.c
new file mode 100644
index 00000000..85eb4552
--- /dev/null
+++ b/bin/9base/ed/ed.c
@@ -0,0 +1,1617 @@
+/*
+ * Editor
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+
+#undef EOF /* stdio? */
+
+enum
+{
+ FNSIZE = 128, /* file name */
+ LBSIZE = 4096, /* max line size */
+ BLKSIZE = 4096, /* block size in temp file */
+ NBLK = 8191, /* max size of temp file */
+ ESIZE = 256, /* max size of reg exp */
+ GBSIZE = 256, /* max size of global command */
+ MAXSUB = 9, /* max number of sub reg exp */
+ ESCFLG = 0xFFFF, /* escape Rune - user defined code */
+ EOF = -1
+};
+
+void (*oldhup)(int);
+void (*oldquit)(int);
+int* addr1;
+int* addr2;
+int anymarks;
+int col;
+long count;
+int* dol;
+int* dot;
+int fchange;
+char file[FNSIZE];
+Rune genbuf[LBSIZE];
+int given;
+Rune* globp;
+int iblock;
+int ichanged;
+int io;
+Biobuf iobuf;
+int lastc;
+char line[70];
+Rune* linebp;
+Rune linebuf[LBSIZE];
+int listf;
+int listn;
+Rune* loc1;
+Rune* loc2;
+int names[26];
+int nleft;
+int oblock;
+int oflag;
+Reprog *pattern;
+int peekc;
+int pflag;
+int rescuing;
+Rune rhsbuf[LBSIZE/sizeof(Rune)];
+char savedfile[FNSIZE];
+jmp_buf savej;
+int subnewa;
+int subolda;
+Resub subexp[MAXSUB];
+char* tfname;
+int tline;
+int waiting;
+int wrapp;
+int* zero;
+
+char Q[] = "";
+char T[] = "TMP";
+char WRERR[] = "WRITE ERROR";
+int bpagesize = 20;
+char hex[] = "0123456789abcdef";
+char* linp = line;
+ulong nlall = 128;
+int tfile = -1;
+int vflag = 1;
+
+void add(int);
+int* address(void);
+int append(int(*)(void), int*);
+void browse(void);
+void callunix(void);
+void commands(void);
+void compile(int);
+int compsub(void);
+void dosub(void);
+void error(char*);
+int match(int*);
+void exfile(int);
+void filename(int);
+Rune* getblock(int, int);
+int getchr(void);
+int getcopy(void);
+int getfile(void);
+Rune* getline(int);
+int getnum(void);
+int getsub(void);
+int gettty(void);
+void global(int);
+void init(void);
+void join(void);
+void move(int);
+void newline(void);
+void nonzero(void);
+void notifyf(void*, char*);
+Rune* place(Rune*, Rune*, Rune*);
+void printcom(void);
+void putchr(int);
+void putd(void);
+void putfile(void);
+int putline(void);
+void putshst(Rune*);
+void putst(char*);
+void quit(void);
+void rdelete(int*, int*);
+void regerror(char *);
+void reverse(int*, int*);
+void setnoaddr(void);
+void setwide(void);
+void squeeze(int);
+void substitute(int);
+char* __mktemp(char *as);
+
+Rune La[] = { 'a', 0 };
+Rune Lr[] = { 'r', 0 };
+
+char tmp[] = "/var/tmp/eXXXXX";
+
+void
+main(int argc, char *argv[])
+{
+ char *p1, *p2;
+
+ notify(notifyf);
+ ARGBEGIN {
+ case 'o':
+ oflag = 1;
+ vflag = 0;
+ break;
+ } ARGEND
+
+ USED(argc);
+ if(*argv && (strcmp(*argv, "-") == 0)) {
+ argv++;
+ vflag = 0;
+ }
+ if(oflag) {
+ p1 = "/dev/stdout";
+ p2 = savedfile;
+ while(*p2++ = *p1++)
+ ;
+ globp = La;
+ } else
+ if(*argv) {
+ p1 = *argv;
+ p2 = savedfile;
+ while(*p2++ = *p1++)
+ if(p2 >= &savedfile[sizeof(savedfile)])
+ p2--;
+ globp = Lr;
+ }
+ zero = malloc((nlall+5)*sizeof(int*));
+ tfname = __mktemp(tmp);
+ init();
+ setjmp(savej);
+ commands();
+ quit();
+}
+
+void
+commands(void)
+{
+ int *a1, c, temp;
+ char lastsep;
+ Dir *d;
+
+ for(;;) {
+ if(pflag) {
+ pflag = 0;
+ addr1 = addr2 = dot;
+ printcom();
+ }
+ c = '\n';
+ for(addr1 = 0;;) {
+ lastsep = c;
+ a1 = address();
+ c = getchr();
+ if(c != ',' && c != ';')
+ break;
+ if(lastsep == ',')
+ error(Q);
+ if(a1 == 0) {
+ a1 = zero+1;
+ if(a1 > dol)
+ a1--;
+ }
+ addr1 = a1;
+ if(c == ';')
+ dot = a1;
+ }
+ if(lastsep != '\n' && a1 == 0)
+ a1 = dol;
+ if((addr2=a1) == 0) {
+ given = 0;
+ addr2 = dot;
+ } else
+ given = 1;
+ if(addr1 == 0)
+ addr1 = addr2;
+ switch(c) {
+
+ case 'a':
+ add(0);
+ continue;
+
+ case 'b':
+ nonzero();
+ browse();
+ continue;
+
+ case 'c':
+ nonzero();
+ newline();
+ rdelete(addr1, addr2);
+ append(gettty, addr1-1);
+ continue;
+
+ case 'd':
+ nonzero();
+ newline();
+ rdelete(addr1, addr2);
+ continue;
+
+ case 'E':
+ fchange = 0;
+ c = 'e';
+ case 'e':
+ setnoaddr();
+ if(vflag && fchange) {
+ fchange = 0;
+ error(Q);
+ }
+ filename(c);
+ init();
+ addr2 = zero;
+ goto caseread;
+
+ case 'f':
+ setnoaddr();
+ filename(c);
+ putst(savedfile);
+ continue;
+
+ case 'g':
+ global(1);
+ continue;
+
+ case 'i':
+ add(-1);
+ continue;
+
+
+ case 'j':
+ if(!given)
+ addr2++;
+ newline();
+ join();
+ continue;
+
+ case 'k':
+ nonzero();
+ c = getchr();
+ if(c < 'a' || c > 'z')
+ error(Q);
+ newline();
+ names[c-'a'] = *addr2 & ~01;
+ anymarks |= 01;
+ continue;
+
+ case 'm':
+ move(0);
+ continue;
+
+ case 'n':
+ listn++;
+ newline();
+ printcom();
+ continue;
+
+ case '\n':
+ if(a1==0) {
+ a1 = dot+1;
+ addr2 = a1;
+ addr1 = a1;
+ }
+ if(lastsep==';')
+ addr1 = a1;
+ printcom();
+ continue;
+
+ case 'l':
+ listf++;
+ case 'p':
+ case 'P':
+ newline();
+ printcom();
+ continue;
+
+ case 'Q':
+ fchange = 0;
+ case 'q':
+ setnoaddr();
+ newline();
+ quit();
+
+ case 'r':
+ filename(c);
+ caseread:
+ if((io=open(file, OREAD)) < 0) {
+ lastc = '\n';
+ error(file);
+ }
+ if((d = dirfstat(io)) != nil){
+ if(d->mode & DMAPPEND)
+ print("warning: %s is append only\n", file);
+ free(d);
+ }
+ Binit(&iobuf, io, OREAD);
+ setwide();
+ squeeze(0);
+ c = zero != dol;
+ append(getfile, addr2);
+ exfile(OREAD);
+
+ fchange = c;
+ continue;
+
+ case 's':
+ nonzero();
+ substitute(globp != 0);
+ continue;
+
+ case 't':
+ move(1);
+ continue;
+
+ case 'u':
+ nonzero();
+ newline();
+ if((*addr2&~01) != subnewa)
+ error(Q);
+ *addr2 = subolda;
+ dot = addr2;
+ continue;
+
+ case 'v':
+ global(0);
+ continue;
+
+ case 'W':
+ wrapp++;
+ case 'w':
+ setwide();
+ squeeze(dol>zero);
+ temp = getchr();
+ if(temp != 'q' && temp != 'Q') {
+ peekc = temp;
+ temp = 0;
+ }
+ filename(c);
+ if(!wrapp ||
+ ((io = open(file, OWRITE)) == -1) ||
+ ((seek(io, 0L, 2)) == -1))
+ if((io = create(file, OWRITE, 0666)) < 0)
+ error(file);
+ Binit(&iobuf, io, OWRITE);
+ wrapp = 0;
+ if(dol > zero)
+ putfile();
+ exfile(OWRITE);
+ if(addr1<=zero+1 && addr2==dol)
+ fchange = 0;
+ if(temp == 'Q')
+ fchange = 0;
+ if(temp)
+ quit();
+ continue;
+
+ case '=':
+ setwide();
+ squeeze(0);
+ newline();
+ count = addr2 - zero;
+ putd();
+ putchr('\n');
+ continue;
+
+ case '!':
+ callunix();
+ continue;
+
+ case EOF:
+ return;
+
+ }
+ error(Q);
+ }
+}
+
+void
+printcom(void)
+{
+ int *a1;
+
+ nonzero();
+ a1 = addr1;
+ do {
+ if(listn) {
+ count = a1-zero;
+ putd();
+ putchr('\t');
+ }
+ putshst(getline(*a1++));
+ } while(a1 <= addr2);
+ dot = addr2;
+ listf = 0;
+ listn = 0;
+ pflag = 0;
+}
+
+int*
+address(void)
+{
+ int sign, *a, opcnt, nextopand, *b, c;
+
+ nextopand = -1;
+ sign = 1;
+ opcnt = 0;
+ a = dot;
+ do {
+ do {
+ c = getchr();
+ } while(c == ' ' || c == '\t');
+ if(c >= '0' && c <= '9') {
+ peekc = c;
+ if(!opcnt)
+ a = zero;
+ a += sign*getnum();
+ } else
+ switch(c) {
+ case '$':
+ a = dol;
+ case '.':
+ if(opcnt)
+ error(Q);
+ break;
+ case '\'':
+ c = getchr();
+ if(opcnt || c < 'a' || c > 'z')
+ error(Q);
+ a = zero;
+ do {
+ a++;
+ } while(a <= dol && names[c-'a'] != (*a & ~01));
+ break;
+ case '?':
+ sign = -sign;
+ case '/':
+ compile(c);
+ b = a;
+ for(;;) {
+ a += sign;
+ if(a <= zero)
+ a = dol;
+ if(a > dol)
+ a = zero;
+ if(match(a))
+ break;
+ if(a == b)
+ error(Q);
+ }
+ break;
+ default:
+ if(nextopand == opcnt) {
+ a += sign;
+ if(a < zero || dol < a)
+ continue; /* error(Q); */
+ }
+ if(c != '+' && c != '-' && c != '^') {
+ peekc = c;
+ if(opcnt == 0)
+ a = 0;
+ return a;
+ }
+ sign = 1;
+ if(c != '+')
+ sign = -sign;
+ nextopand = ++opcnt;
+ continue;
+ }
+ sign = 1;
+ opcnt++;
+ } while(zero <= a && a <= dol);
+ error(Q);
+ return 0;
+}
+
+int
+getnum(void)
+{
+ int r, c;
+
+ r = 0;
+ for(;;) {
+ c = getchr();
+ if(c < '0' || c > '9')
+ break;
+ r = r*10 + (c-'0');
+ }
+ peekc = c;
+ return r;
+}
+
+void
+setwide(void)
+{
+ if(!given) {
+ addr1 = zero + (dol>zero);
+ addr2 = dol;
+ }
+}
+
+void
+setnoaddr(void)
+{
+ if(given)
+ error(Q);
+}
+
+void
+nonzero(void)
+{
+ squeeze(1);
+}
+
+void
+squeeze(int i)
+{
+ if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
+ error(Q);
+}
+
+void
+newline(void)
+{
+ int c;
+
+ c = getchr();
+ if(c == '\n' || c == EOF)
+ return;
+ if(c == 'p' || c == 'l' || c == 'n') {
+ pflag++;
+ if(c == 'l')
+ listf++;
+ else
+ if(c == 'n')
+ listn++;
+ c = getchr();
+ if(c == '\n')
+ return;
+ }
+ error(Q);
+}
+
+void
+filename(int comm)
+{
+ char *p1, *p2;
+ Rune rune;
+ int c;
+
+ count = 0;
+ c = getchr();
+ if(c == '\n' || c == EOF) {
+ p1 = savedfile;
+ if(*p1 == 0 && comm != 'f')
+ error(Q);
+ p2 = file;
+ while(*p2++ = *p1++)
+ ;
+ return;
+ }
+ if(c != ' ')
+ error(Q);
+ while((c=getchr()) == ' ')
+ ;
+ if(c == '\n')
+ error(Q);
+ p1 = file;
+ do {
+ if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
+ error(Q);
+ rune = c;
+ p1 += runetochar(p1, &rune);
+ } while((c=getchr()) != '\n');
+ *p1 = 0;
+ if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
+ p1 = savedfile;
+ p2 = file;
+ while(*p1++ = *p2++)
+ ;
+ }
+}
+
+void
+exfile(int om)
+{
+
+ if(om == OWRITE)
+ if(Bflush(&iobuf) < 0)
+ error(Q);
+ close(io);
+ io = -1;
+ if(vflag) {
+ putd();
+ putchr('\n');
+ }
+}
+
+void
+error1(char *s)
+{
+ int c;
+
+ wrapp = 0;
+ listf = 0;
+ listn = 0;
+ count = 0;
+ seek(0, 0, 2);
+ pflag = 0;
+ if(globp)
+ lastc = '\n';
+ globp = 0;
+ peekc = lastc;
+ if(lastc)
+ for(;;) {
+ c = getchr();
+ if(c == '\n' || c == EOF)
+ break;
+ }
+ if(io > 0) {
+ close(io);
+ io = -1;
+ }
+ putchr('?');
+ putst(s);
+}
+
+void
+error(char *s)
+{
+ error1(s);
+ longjmp(savej, 1);
+}
+
+void
+rescue(void)
+{
+ rescuing = 1;
+ if(dol > zero) {
+ addr1 = zero+1;
+ addr2 = dol;
+ io = create("ed.hup", OWRITE, 0666);
+ if(io > 0){
+ Binit(&iobuf, io, OWRITE);
+ putfile();
+ }
+ }
+ fchange = 0;
+ quit();
+}
+
+void
+notifyf(void *a, char *s)
+{
+ if(strcmp(s, "interrupt") == 0){
+ if(rescuing || waiting)
+ noted(NCONT);
+ putchr('\n');
+ lastc = '\n';
+ error1(Q);
+ notejmp(a, savej, 0);
+ }
+ if(strcmp(s, "hangup") == 0 || strcmp(s, "kill") == 0){
+ if(rescuing)
+ noted(NDFLT);
+ rescue();
+ }
+ if(strstr(s, "child"))
+ noted(NCONT);
+ fprint(2, "ed: note: %s\n", s);
+ abort();
+}
+
+int
+getchr(void)
+{
+ char s[UTFmax];
+ int i;
+ Rune r;
+
+ if(lastc = peekc) {
+ peekc = 0;
+ return lastc;
+ }
+ if(globp) {
+ if((lastc=*globp++) != 0)
+ return lastc;
+ globp = 0;
+ return EOF;
+ }
+ for(i=0;;) {
+ if(read(0, s+i, 1) <= 0)
+ return lastc = EOF;
+ i++;
+ if(fullrune(s, i))
+ break;
+
+ }
+ chartorune(&r, s);
+ lastc = r;
+ return lastc;
+}
+
+int
+gety(void)
+{
+ int c;
+ Rune *gf, *p;
+
+ p = linebuf;
+ gf = globp;
+ for(;;) {
+ c = getchr();
+ if(c == '\n') {
+ *p = 0;
+ return 0;
+ }
+ if(c == EOF) {
+ if(gf)
+ peekc = c;
+ return c;
+ }
+ if(c == 0)
+ continue;
+ *p++ = c;
+ if(p >= &linebuf[LBSIZE-2])
+ error(Q);
+ }
+}
+
+int
+gettty(void)
+{
+ int rc;
+
+ rc = gety();
+ if(rc)
+ return rc;
+ if(linebuf[0] == '.' && linebuf[1] == 0)
+ return EOF;
+ return 0;
+}
+
+int
+getfile(void)
+{
+ int c;
+ Rune *lp;
+
+ lp = linebuf;
+ do {
+ c = Bgetrune(&iobuf);
+ if(c < 0) {
+ if(lp > linebuf) {
+ putst("'\\n' appended");
+ c = '\n';
+ } else
+ return EOF;
+ }
+ if(lp >= &linebuf[LBSIZE]) {
+ lastc = '\n';
+ error(Q);
+ }
+ *lp++ = c;
+ count++;
+ } while(c != '\n');
+ lp[-1] = 0;
+ return 0;
+}
+
+void
+putfile(void)
+{
+ int *a1;
+ Rune *lp;
+ long c;
+
+ a1 = addr1;
+ do {
+ lp = getline(*a1++);
+ for(;;) {
+ count++;
+ c = *lp++;
+ if(c == 0) {
+ if(Bputrune(&iobuf, '\n') < 0)
+ error(Q);
+ break;
+ }
+ if(Bputrune(&iobuf, c) < 0)
+ error(Q);
+ }
+ } while(a1 <= addr2);
+ if(Bflush(&iobuf) < 0)
+ error(Q);
+}
+
+int
+append(int (*f)(void), int *a)
+{
+ int *a1, *a2, *rdot, nline, d;
+
+ nline = 0;
+ dot = a;
+ while((*f)() == 0) {
+ if((dol-zero) >= nlall) {
+ nlall += 512;
+ a1 = realloc(zero, (nlall+50)*sizeof(int*));
+ if(a1 == 0) {
+ error("MEM?");
+ rescue();
+ }
+ /* relocate pointers; avoid wraparound if sizeof(int) < sizeof(int*) */
+ d = addr1 - zero;
+ addr1 = a1 + d;
+ d = addr2 - zero;
+ addr2 = a1 + d;
+ d = dol - zero;
+ dol = a1 + d;
+ d = dot - zero;
+ dot = a1 + d;
+ zero = a1;
+ }
+ d = putline();
+ nline++;
+ a1 = ++dol;
+ a2 = a1+1;
+ rdot = ++dot;
+ while(a1 > rdot)
+ *--a2 = *--a1;
+ *rdot = d;
+ }
+ return nline;
+}
+
+void
+add(int i)
+{
+ if(i && (given || dol > zero)) {
+ addr1--;
+ addr2--;
+ }
+ squeeze(0);
+ newline();
+ append(gettty, addr2);
+}
+
+void
+browse(void)
+{
+ int forward, n;
+ static int bformat, bnum; /* 0 */
+
+ forward = 1;
+ peekc = getchr();
+ if(peekc != '\n'){
+ if(peekc == '-' || peekc == '+') {
+ if(peekc == '-')
+ forward = 0;
+ getchr();
+ }
+ n = getnum();
+ if(n > 0)
+ bpagesize = n;
+ }
+ newline();
+ if(pflag) {
+ bformat = listf;
+ bnum = listn;
+ } else {
+ listf = bformat;
+ listn = bnum;
+ }
+ if(forward) {
+ addr1 = addr2;
+ addr2 += bpagesize;
+ if(addr2 > dol)
+ addr2 = dol;
+ } else {
+ addr1 = addr2-bpagesize;
+ if(addr1 <= zero)
+ addr1 = zero+1;
+ }
+ printcom();
+}
+
+void
+callunix(void)
+{
+ int c, pid;
+ Rune rune;
+ char buf[512];
+ char *p;
+
+ setnoaddr();
+ p = buf;
+ while((c=getchr()) != EOF && c != '\n')
+ if(p < &buf[sizeof(buf) - 6]) {
+ rune = c;
+ p += runetochar(p, &rune);
+ }
+ *p = 0;
+ pid = fork();
+ if(pid == 0) {
+ execlp("rc", "rc", "-c", buf, (char*)0);
+ sysfatal("exec failed: %r");
+ exits("execl failed");
+ }
+ waiting = 1;
+ while(waitpid() != pid)
+ ;
+ waiting = 0;
+ if(vflag)
+ putst("!");
+}
+
+void
+quit(void)
+{
+ if(vflag && fchange && dol!=zero) {
+ fchange = 0;
+ error(Q);
+ }
+ remove(tfname);
+ exits(0);
+}
+
+void
+onquit(int sig)
+{
+ USED(sig);
+ quit();
+}
+
+void
+rdelete(int *ad1, int *ad2)
+{
+ int *a1, *a2, *a3;
+
+ a1 = ad1;
+ a2 = ad2+1;
+ a3 = dol;
+ dol -= a2 - a1;
+ do {
+ *a1++ = *a2++;
+ } while(a2 <= a3);
+ a1 = ad1;
+ if(a1 > dol)
+ a1 = dol;
+ dot = a1;
+ fchange = 1;
+}
+
+void
+gdelete(void)
+{
+ int *a1, *a2, *a3;
+
+ a3 = dol;
+ for(a1=zero; (*a1&01)==0; a1++)
+ if(a1>=a3)
+ return;
+ for(a2=a1+1; a2<=a3;) {
+ if(*a2 & 01) {
+ a2++;
+ dot = a1;
+ } else
+ *a1++ = *a2++;
+ }
+ dol = a1-1;
+ if(dot > dol)
+ dot = dol;
+ fchange = 1;
+}
+
+Rune*
+getline(int tl)
+{
+ Rune *lp, *bp;
+ int nl;
+
+ lp = linebuf;
+ bp = getblock(tl, OREAD);
+ nl = nleft;
+ tl &= ~((BLKSIZE/sizeof(Rune)) - 1);
+ while(*lp++ = *bp++) {
+ nl -= sizeof(Rune);
+ if(nl == 0) {
+ bp = getblock(tl += BLKSIZE/sizeof(Rune), OREAD);
+ nl = nleft;
+ }
+ }
+ return linebuf;
+}
+
+int
+putline(void)
+{
+ Rune *lp, *bp;
+ int nl, tl;
+
+ fchange = 1;
+ lp = linebuf;
+ tl = tline;
+ bp = getblock(tl, OWRITE);
+ nl = nleft;
+ tl &= ~((BLKSIZE/sizeof(Rune))-1);
+ while(*bp = *lp++) {
+ if(*bp++ == '\n') {
+ bp[-1] = 0;
+ linebp = lp;
+ break;
+ }
+ nl -= sizeof(Rune);
+ if(nl == 0) {
+ tl += BLKSIZE/sizeof(Rune);
+ bp = getblock(tl, OWRITE);
+ nl = nleft;
+ }
+ }
+ nl = tline;
+ tline += ((lp-linebuf) + 03) & 077776;
+ return nl;
+}
+
+void
+blkio(int b, uchar *buf, int isread)
+{
+ int n;
+
+ seek(tfile, b*BLKSIZE, 0);
+ if(isread)
+ n = read(tfile, buf, BLKSIZE);
+ else
+ n = write(tfile, buf, BLKSIZE);
+ if(n != BLKSIZE)
+ error(T);
+}
+
+Rune*
+getblock(int atl, int iof)
+{
+ int bno, off;
+
+ static uchar ibuff[BLKSIZE];
+ static uchar obuff[BLKSIZE];
+
+ bno = atl / (BLKSIZE/sizeof(Rune));
+ off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~03;
+ if(bno >= NBLK) {
+ lastc = '\n';
+ error(T);
+ }
+ nleft = BLKSIZE - off;
+ if(bno == iblock) {
+ ichanged |= iof;
+ return (Rune*)(ibuff+off);
+ }
+ if(bno == oblock)
+ return (Rune*)(obuff+off);
+ if(iof == OREAD) {
+ if(ichanged)
+ blkio(iblock, ibuff, 0);
+ ichanged = 0;
+ iblock = bno;
+ blkio(bno, ibuff, 1);
+ return (Rune*)(ibuff+off);
+ }
+ if(oblock >= 0)
+ blkio(oblock, obuff, 0);
+ oblock = bno;
+ return (Rune*)(obuff+off);
+}
+
+void
+init(void)
+{
+ int *markp;
+
+ close(tfile);
+ tline = 2;
+ for(markp = names; markp < &names[26]; )
+ *markp++ = 0;
+ subnewa = 0;
+ anymarks = 0;
+ iblock = -1;
+ oblock = -1;
+ ichanged = 0;
+ if((tfile = create(tfname, ORDWR, 0600)) < 0){
+ error1(T);
+ exits(0);
+ }
+ dot = dol = zero;
+}
+
+void
+global(int k)
+{
+ Rune *gp, globuf[GBSIZE];
+ int c, *a1;
+
+ if(globp)
+ error(Q);
+ setwide();
+ squeeze(dol > zero);
+ c = getchr();
+ if(c == '\n')
+ error(Q);
+ compile(c);
+ gp = globuf;
+ while((c=getchr()) != '\n') {
+ if(c == EOF)
+ error(Q);
+ if(c == '\\') {
+ c = getchr();
+ if(c != '\n')
+ *gp++ = '\\';
+ }
+ *gp++ = c;
+ if(gp >= &globuf[GBSIZE-2])
+ error(Q);
+ }
+ if(gp == globuf)
+ *gp++ = 'p';
+ *gp++ = '\n';
+ *gp = 0;
+ for(a1=zero; a1<=dol; a1++) {
+ *a1 &= ~01;
+ if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
+ *a1 |= 01;
+ }
+
+ /*
+ * Special case: g/.../d (avoid n^2 algorithm)
+ */
+ if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
+ gdelete();
+ return;
+ }
+ for(a1=zero; a1<=dol; a1++) {
+ if(*a1 & 01) {
+ *a1 &= ~01;
+ dot = a1;
+ globp = globuf;
+ commands();
+ a1 = zero;
+ }
+ }
+}
+
+void
+join(void)
+{
+ Rune *gp, *lp;
+ int *a1;
+
+ nonzero();
+ gp = genbuf;
+ for(a1=addr1; a1<=addr2; a1++) {
+ lp = getline(*a1);
+ while(*gp = *lp++)
+ if(gp++ >= &genbuf[LBSIZE-2])
+ error(Q);
+ }
+ lp = linebuf;
+ gp = genbuf;
+ while(*lp++ = *gp++)
+ ;
+ *addr1 = putline();
+ if(addr1 < addr2)
+ rdelete(addr1+1, addr2);
+ dot = addr1;
+}
+
+void
+substitute(int inglob)
+{
+ int *mp, *a1, nl, gsubf, n;
+
+ n = getnum(); /* OK even if n==0 */
+ gsubf = compsub();
+ for(a1 = addr1; a1 <= addr2; a1++) {
+ if(match(a1)){
+ int *ozero;
+ int m = n;
+
+ do {
+ int span = loc2-loc1;
+
+ if(--m <= 0) {
+ dosub();
+ if(!gsubf)
+ break;</