morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base.git
Log | Files | Refs

commit c28e6b723b17b1463f8c7425c6c0b09c9311272d
Author: sin <sin@2f30.org>
Date:   Wed Jan 21 01:05:19 +0000

Initial commit

Diffstat:
Makefile | 16++++++++++++++++
awk/Makefile | 21+++++++++++++++++++++
awk/arc4random.c | 12++++++++++++
awk/awk.1 | 800+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/awk.h | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/awkgram.y | 487+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/b.c | 958+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/lex.c | 597+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/lib.c | 739+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/main.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/maketab.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/parse.c | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/proto.h | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/reallocarray.c | 38++++++++++++++++++++++++++++++++++++++
awk/run.c | 2027+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/strlcat.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/strlcpy.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
awk/tran.c | 458+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
awk/util.h | 10++++++++++
sbase/LICENSE | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/Makefile | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/README | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/TODO | 42++++++++++++++++++++++++++++++++++++++++++
sbase/arg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/basename.1 | 26++++++++++++++++++++++++++
sbase/basename.c | 41+++++++++++++++++++++++++++++++++++++++++
sbase/cal.1 | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cal.c | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cat.1 | 29+++++++++++++++++++++++++++++
sbase/cat.c | 45+++++++++++++++++++++++++++++++++++++++++++++
sbase/chgrp.1 | 22++++++++++++++++++++++
sbase/chgrp.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/chmod.1 | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/chmod.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/chown.1 | 20++++++++++++++++++++
sbase/chown.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/chroot.1 | 26++++++++++++++++++++++++++
sbase/chroot.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cksum.1 | 27+++++++++++++++++++++++++++
sbase/cksum.c | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cmp.1 | 42++++++++++++++++++++++++++++++++++++++++++
sbase/cmp.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cols.1 | 41+++++++++++++++++++++++++++++++++++++++++
sbase/cols.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/comm.1 | 43+++++++++++++++++++++++++++++++++++++++++++
sbase/comm.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/compat.h | 6++++++
sbase/config.mk | 15+++++++++++++++
sbase/cp.1 | 42++++++++++++++++++++++++++++++++++++++++++
sbase/cp.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cron.1 | 24++++++++++++++++++++++++
sbase/cron.c | 494+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/crypt.h | 12++++++++++++
sbase/cut.1 | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/cut.c | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/date.1 | 26++++++++++++++++++++++++++
sbase/date.c | 46++++++++++++++++++++++++++++++++++++++++++++++
sbase/dirname.1 | 14++++++++++++++
sbase/dirname.c | 29+++++++++++++++++++++++++++++
sbase/du.1 | 37+++++++++++++++++++++++++++++++++++++
sbase/du.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/echo.1 | 14++++++++++++++
sbase/echo.c | 32++++++++++++++++++++++++++++++++
sbase/env.1 | 41+++++++++++++++++++++++++++++++++++++++++
sbase/env.c | 45+++++++++++++++++++++++++++++++++++++++++++++
sbase/expand.1 | 25+++++++++++++++++++++++++
sbase/expand.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/expr.1 | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/expr.c | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/false.1 | 16++++++++++++++++
sbase/false.c | 6++++++
sbase/fold.1 | 25+++++++++++++++++++++++++
sbase/fold.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/fs.h | 14++++++++++++++
sbase/grep.1 | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/grep.c | 255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/head.1 | 18++++++++++++++++++
sbase/head.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/hostname.1 | 12++++++++++++
sbase/hostname.c | 34++++++++++++++++++++++++++++++++++
sbase/kill.1 | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/kill.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutf/chartorunearr.c | 27+++++++++++++++++++++++++++
sbase/libutf/readrune.c | 46++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutf/rune.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutf/runetype.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutf/utf.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutf/writerune.c | 23+++++++++++++++++++++++
sbase/libutil/agetcwd.c | 18++++++++++++++++++
sbase/libutil/apathmax.c | 22++++++++++++++++++++++
sbase/libutil/concat.c | 20++++++++++++++++++++
sbase/libutil/cp.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/crypt.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/ealloc.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/enmasse.c | 41+++++++++++++++++++++++++++++++++++++++++
sbase/libutil/eprintf.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/eregcomp.c | 25+++++++++++++++++++++++++
sbase/libutil/estrtod.c | 18++++++++++++++++++
sbase/libutil/estrtol.c | 27+++++++++++++++++++++++++++
sbase/libutil/fnck.c | 20++++++++++++++++++++
sbase/libutil/getlines.c | 27+++++++++++++++++++++++++++
sbase/libutil/human.c | 22++++++++++++++++++++++
sbase/libutil/md5.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/mode.c | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/putword.c | 16++++++++++++++++
sbase/libutil/recurse.c | 42++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/rm.c | 17+++++++++++++++++
sbase/libutil/sha1.c | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/sha256.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/sha512.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/strcasestr.c | 34++++++++++++++++++++++++++++++++++
sbase/libutil/strlcat.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/libutil/strlcpy.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
sbase/link.1 | 14++++++++++++++
sbase/link.c | 17+++++++++++++++++
sbase/ln.1 | 36++++++++++++++++++++++++++++++++++++
sbase/ln.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/logger.1 | 50++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/logger.c | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/logname.1 | 8++++++++
sbase/logname.c | 20++++++++++++++++++++
sbase/ls.1 | 46++++++++++++++++++++++++++++++++++++++++++++++
sbase/ls.c | 310+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/md5.h | 18++++++++++++++++++
sbase/md5sum.1 | 12++++++++++++
sbase/md5sum.c | 43+++++++++++++++++++++++++++++++++++++++++++
sbase/mkdir.1 | 19+++++++++++++++++++
sbase/mkdir.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/mkfifo.1 | 19+++++++++++++++++++
sbase/mkfifo.c | 40++++++++++++++++++++++++++++++++++++++++
sbase/mktemp.1 | 25+++++++++++++++++++++++++
sbase/mktemp.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/mv.1 | 22++++++++++++++++++++++
sbase/mv.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/nice.1 | 18++++++++++++++++++
sbase/nice.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
sbase/nl.1 | 46++++++++++++++++++++++++++++++++++++++++++++++
sbase/nl.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/nohup.1 | 16++++++++++++++++
sbase/nohup.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/paste.1 | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/paste.c | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/printenv.1 | 19+++++++++++++++++++
sbase/printenv.c | 38++++++++++++++++++++++++++++++++++++++
sbase/printf.1 | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/printf.c | 497+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/pwd.1 | 32++++++++++++++++++++++++++++++++
sbase/pwd.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/queue.h | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/readlink.1 | 25+++++++++++++++++++++++++
sbase/readlink.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/renice.1 | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/renice.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/rm.1 | 22++++++++++++++++++++++
sbase/rm.c | 42++++++++++++++++++++++++++++++++++++++++++
sbase/rmdir.1 | 25+++++++++++++++++++++++++
sbase/rmdir.c | 29+++++++++++++++++++++++++++++
sbase/runetypebody.h | 1886+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/seq.1 | 36++++++++++++++++++++++++++++++++++++
sbase/seq.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/setsid.1 | 7+++++++
sbase/setsid.c | 45+++++++++++++++++++++++++++++++++++++++++++++
sbase/sha1.h | 18++++++++++++++++++
sbase/sha1sum.1 | 12++++++++++++
sbase/sha1sum.c | 43+++++++++++++++++++++++++++++++++++++++++++
sbase/sha256.h | 18++++++++++++++++++
sbase/sha256sum.1 | 12++++++++++++
sbase/sha256sum.c | 43+++++++++++++++++++++++++++++++++++++++++++
sbase/sha512.h | 18++++++++++++++++++
sbase/sha512sum.1 | 12++++++++++++
sbase/sha512sum.c | 43+++++++++++++++++++++++++++++++++++++++++++
sbase/sleep.1 | 11+++++++++++
sbase/sleep.c | 30++++++++++++++++++++++++++++++
sbase/sort.1 | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/sort.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/split.1 | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/split.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/sponge.1 | 15+++++++++++++++
sbase/sponge.c | 41+++++++++++++++++++++++++++++++++++++++++
sbase/strings.1 | 17+++++++++++++++++
sbase/strings.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/sync.1 | 14++++++++++++++
sbase/sync.c | 22++++++++++++++++++++++
sbase/tail.1 | 20++++++++++++++++++++
sbase/tail.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/tar.1 | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/tar.c | 332+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/tee.1 | 14++++++++++++++
sbase/tee.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
sbase/test.1 | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/test.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/text.h | 11+++++++++++
sbase/touch.1 | 32++++++++++++++++++++++++++++++++
sbase/touch.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/tr.1 | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/tr.c | 289++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/true.1 | 16++++++++++++++++
sbase/true.c | 6++++++
sbase/tty.1 | 13+++++++++++++
sbase/tty.c | 27+++++++++++++++++++++++++++
sbase/uname.1 | 32++++++++++++++++++++++++++++++++
sbase/uname.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/unexpand.1 | 25+++++++++++++++++++++++++
sbase/unexpand.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/uniq.1 | 39+++++++++++++++++++++++++++++++++++++++
sbase/uniq.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/unlink.1 | 16++++++++++++++++
sbase/unlink.c | 23+++++++++++++++++++++++
sbase/utf.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/util.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/uudecode.1 | 19+++++++++++++++++++
sbase/uudecode.c | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/uuencode.1 | 16++++++++++++++++
sbase/uuencode.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/wc.1 | 25+++++++++++++++++++++++++
sbase/wc.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/xargs.1 | 50++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/xargs.c | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sbase/yes.1 | 10++++++++++
sbase/yes.c | 24++++++++++++++++++++++++
sdhcp/LICENSE | 22++++++++++++++++++++++
sdhcp/Makefile | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/TODO | 41+++++++++++++++++++++++++++++++++++++++++
sdhcp/arg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/config.mk | 13+++++++++++++
sdhcp/debug.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/sdhcp.1 | 49+++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/sdhcp.c | 495+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/util.h | 9+++++++++
sdhcp/util/eprintf.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sdhcp/util/strlcpy.c | 32++++++++++++++++++++++++++++++++
sed/Makefile | 9+++++++++
sed/compile.c | 861+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/defs.h | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/extern.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/fgetln.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/main.c | 358+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/misc.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/process.c | 625+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/reallocarray.c | 38++++++++++++++++++++++++++++++++++++++
sed/sed.1 | 566+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/strlcat.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
sed/strlcpy.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
sed/util.h | 12++++++++++++
sinit/LICENSE | 21+++++++++++++++++++++
sinit/Makefile | 39+++++++++++++++++++++++++++++++++++++++
sinit/README | 26++++++++++++++++++++++++++
sinit/config.def.h | 5+++++
sinit/config.h | 5+++++
sinit/config.mk | 12++++++++++++
sinit/sinit.8 | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sinit/sinit.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/LICENSE | 22++++++++++++++++++++++
smdev/Makefile | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/README | 25+++++++++++++++++++++++++
smdev/TODO | 6++++++
smdev/arg.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/bin/simevent | 18++++++++++++++++++
smdev/config.def.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/config.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/config.mk | 11+++++++++++
smdev/mkpath.h | 2++
smdev/smdev.c | 441+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/util.h | 18++++++++++++++++++
smdev/util/agetcwd.c | 18++++++++++++++++++
smdev/util/apathmax.c | 25+++++++++++++++++++++++++
smdev/util/dev.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/util/eprintf.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
smdev/util/estrtol.c | 27+++++++++++++++++++++++++++
smdev/util/mkpath.c | 29+++++++++++++++++++++++++++++
smdev/util/recurse.c | 40++++++++++++++++++++++++++++++++++++++++
smdev/util/strlcpy.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
std.mk | 12++++++++++++
ubase/LICENSE | 33+++++++++++++++++++++++++++++++++
ubase/Makefile | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/README | 29+++++++++++++++++++++++++++++
ubase/TODO | 35+++++++++++++++++++++++++++++++++++
ubase/arg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/chvt.1 | 8++++++++
ubase/chvt.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/clear.1 | 3+++
ubase/clear.c | 10++++++++++
ubase/config.def.h | 11+++++++++++
ubase/config.h | 11+++++++++++
ubase/config.mk | 16++++++++++++++++
ubase/ctrlaltdel.8 | 20++++++++++++++++++++
ubase/ctrlaltdel.c | 44++++++++++++++++++++++++++++++++++++++++++++
ubase/dd.1 | 45+++++++++++++++++++++++++++++++++++++++++++++
ubase/dd.c | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/df.1 | 23+++++++++++++++++++++++
ubase/df.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/dmesg.1 | 22++++++++++++++++++++++
ubase/dmesg.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/eject.1 | 12++++++++++++
ubase/eject.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/fallocate.1 | 20++++++++++++++++++++
ubase/fallocate.c | 46++++++++++++++++++++++++++++++++++++++++++++++
ubase/free.1 | 21+++++++++++++++++++++
ubase/free.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/freeramdisk.8 | 8++++++++
ubase/freeramdisk.c | 39+++++++++++++++++++++++++++++++++++++++
ubase/fsfreeze.8 | 24++++++++++++++++++++++++
ubase/fsfreeze.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/getty.8 | 11+++++++++++
ubase/getty.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/halt.8 | 16++++++++++++++++
ubase/halt.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/hwclock.8 | 25+++++++++++++++++++++++++
ubase/hwclock.c | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/id.1 | 21+++++++++++++++++++++
ubase/id.c | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/insmod.8 | 10++++++++++
ubase/insmod.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/killall5.8 | 18++++++++++++++++++
ubase/killall5.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/last.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/lastlog.8 | 11+++++++++++
ubase/lastlog.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/agetcwd.c | 18++++++++++++++++++
ubase/libutil/agetline.c | 13+++++++++++++
ubase/libutil/apathmax.c | 22++++++++++++++++++++++
ubase/libutil/concat.c | 20++++++++++++++++++++
ubase/libutil/ealloc.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/eprintf.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/estrtol.c | 27+++++++++++++++++++++++++++
ubase/libutil/estrtoul.c | 26++++++++++++++++++++++++++
ubase/libutil/explicit_bzero.c | 12++++++++++++
ubase/libutil/passwd.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/proc.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/putword.c | 16++++++++++++++++
ubase/libutil/recurse.c | 42++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/strlcat.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/strlcpy.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
ubase/libutil/tty.c | 39+++++++++++++++++++++++++++++++++++++++
ubase/login.1 | 13+++++++++++++
ubase/login.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/lsmod.8 | 6++++++
ubase/lsmod.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/lsusb.8 | 6++++++
ubase/lsusb.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/mesg.1 | 19+++++++++++++++++++
ubase/mesg.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/mknod.1 | 23+++++++++++++++++++++++
ubase/mknod.c | 45+++++++++++++++++++++++++++++++++++++++++++++
ubase/mkswap.8 | 12++++++++++++
ubase/mkswap.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/mount.8 | 33+++++++++++++++++++++++++++++++++
ubase/mount.c | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/mountpoint.1 | 22++++++++++++++++++++++
ubase/mountpoint.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/pagesize.1 | 8++++++++
ubase/pagesize.c | 32++++++++++++++++++++++++++++++++
ubase/passwd.1 | 11+++++++++++
ubase/passwd.c | 257+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/passwd.h | 4++++
ubase/pidof.1 | 18++++++++++++++++++
ubase/pidof.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/pivot_root.8 | 8++++++++
ubase/pivot_root.c | 31+++++++++++++++++++++++++++++++
ubase/proc.h | 42++++++++++++++++++++++++++++++++++++++++++
ubase/ps.1 | 26++++++++++++++++++++++++++
ubase/ps.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/queue.h | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/readahead.8 | 11+++++++++++
ubase/readahead.c | 38++++++++++++++++++++++++++++++++++++++
ubase/reboot.h | 32++++++++++++++++++++++++++++++++
ubase/respawn.1 | 18++++++++++++++++++
ubase/respawn.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/rmmod.8 | 23+++++++++++++++++++++++
ubase/rmmod.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/rtc.h | 20++++++++++++++++++++
ubase/stat.1 | 14++++++++++++++
ubase/stat.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/su.1 | 20++++++++++++++++++++
ubase/su.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/swaplabel.8 | 11+++++++++++
ubase/swaplabel.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/swapoff.8 | 11+++++++++++
ubase/swapoff.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/swapon.8 | 19+++++++++++++++++++
ubase/swapon.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/switch_root.8 | 15+++++++++++++++
ubase/switch_root.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/sysctl.8 | 13+++++++++++++
ubase/sysctl.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/text.h | 13+++++++++++++
ubase/truncate.1 | 20++++++++++++++++++++
ubase/truncate.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/umount.8 | 30++++++++++++++++++++++++++++++
ubase/umount.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/unshare.1 | 43+++++++++++++++++++++++++++++++++++++++++++
ubase/unshare.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/uptime.1 | 12++++++++++++
ubase/uptime.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/util.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/vtallow.1 | 22++++++++++++++++++++++
ubase/vtallow.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/watch.1 | 19+++++++++++++++++++
ubase/watch.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ubase/who.1 | 21+++++++++++++++++++++
ubase/who.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
401 files changed, 36226 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile @@ -0,0 +1,16 @@ +TARG = awk sbase sdhcp sed sinit smdev ubase + +all: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + for i in $(TARG); do cd $$i && $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" || exit; cd ..; done + +install: all + for i in $(TARG); do cd $$i && $(MAKE) install || exit; cd ..; done + +uninstall: + for i in $(TARG); do cd $$i && $(MAKE) uninstall || exit; cd ..; done + +clean: + for i in $(TARG); do cd $$i && $(MAKE) clean || exit; cd ..; done diff --git a/awk/Makefile b/awk/Makefile @@ -0,0 +1,21 @@ +OBJ = arc4random.o b.o lex.o lib.o main.o parse.o proctab.o reallocarray.o run.o strlcat.o strlcpy.o tran.o ytab.o +TARG = awk +LDLIBS = -lm + +all: ytab.c proctab.c $(TARG) + +ytab.c ytab.h: awkgram.y + $(YACC) -d awkgram.y + mv y.tab.c ytab.c + mv y.tab.h ytab.h + +proctab.c: maketab + ./maketab > proctab.c + +maketab: ytab.h maketab.c + $(CC) maketab.c -o $@ + +include ../std.mk + +clean: + rm -f $(TARG) $(OBJ) maketab proctab.c ytab.[ch] diff --git a/awk/arc4random.c b/awk/arc4random.c @@ -0,0 +1,12 @@ +#include <sys/types.h> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +uint32_t +arc4random(void) +{ + srand(time(NULL) ^ getpid() ^ 0x42); + return rand(); +} diff --git a/awk/awk.1 b/awk/awk.1 @@ -0,0 +1,800 @@ +.\" $OpenBSD: awk.1,v 1.41 2014/03/17 21:48:51 jmc Exp $ +.\" +.\" 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. +.\" +.Dd $Mdocdate: March 17 2014 $ +.Dt AWK 1 +.Os +.Sh NAME +.Nm awk +.Nd pattern-directed scanning and processing language +.Sh SYNOPSIS +.Nm awk +.Op Fl safe +.Op Fl V +.Op Fl d Ns Op Ar n +.Op Fl F Ar fs +.Op Fl v Ar var Ns = Ns Ar value +.Op Ar prog | Fl f Ar progfile +.Ar +.Sh DESCRIPTION +.Nm +scans each input +.Ar file +for lines that match any of a set of patterns specified literally in +.Ar prog +or in one or more files specified as +.Fl f Ar progfile . +With each pattern there can be an associated action that will be performed +when a line of a +.Ar 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 +.Sq - +means the standard input. +Any +.Ar file +of the form +.Ar var Ns = Ns Ar value +is treated as an assignment, not a filename, +and is executed at the time it would have been opened if it were a filename. +.Pp +The options are as follows: +.Bl -tag -width "-safe " +.It Fl d Ns Op Ar n +Debug mode. +Set debug level to +.Ar n , +or 1 if +.Ar n +is not specified. +A value greater than 1 causes +.Nm +to dump core on fatal errors. +.It Fl F Ar fs +Define the input field separator to be the regular expression +.Ar fs . +.It Fl f Ar progfile +Read program code from the specified file +.Ar progfile +instead of from the command line. +.It Fl safe +Disable file output +.Pf ( Ic print No > , +.Ic print No >> ) , +process creation +.Po +.Ar cmd | Ic getline , +.Ic print | , +.Ic system +.Pc +and access to the environment +.Pf ( Va ENVIRON ; +see the section on variables below). +This is a first +.Pq and not very reliable +approximation to a +.Dq safe +version of +.Nm . +.It Fl V +Print the version number of +.Nm +to standard output and exit. +.It Fl v Ar var Ns = Ns Ar value +Assign +.Ar value +to variable +.Ar var +before +.Ar prog +is executed; +any number of +.Fl v +options may be present. +.El +.Pp +The input is normally made up of input lines +.Pq records +separated by newlines, or by the value of +.Va RS . +If +.Va RS +is null, then any number of blank lines are used as the record separator, +and newlines are used as field separators +(in addition to the value of +.Va FS ) . +This is convenient when working with multi-line records. +.Pp +An input line is normally made up of fields separated by whitespace, +or by the regular expression +.Va FS . +The fields are denoted +.Va $1 , $2 , ... , +while +.Va $0 +refers to the entire line. +If +.Va FS +is null, the input line is split into one field per character. +.Pp +Normally, any number of blanks separate fields. +In order to set the field separator to a single blank, use the +.Fl F +option with a value of +.Sq [\ \&] . +If a field separator of +.Sq t +is specified, +.Nm +treats it as if +.Sq \et +had been specified and uses +.Aq TAB +as the field separator. +In order to use a literal +.Sq t +as the field separator, use the +.Fl F +option with a value of +.Sq [t] . +.Pp +A pattern-action statement has the form +.Pp +.D1 Ar pattern Ic \&{ Ar action Ic \&} +.Pp +A missing +.Ic \&{ Ar action Ic \&} +means print the line; +a missing pattern always matches. +Pattern-action statements are separated by newlines or semicolons. +.Pp +Newlines are permitted after a terminating statement or following a comma +.Pq Sq ,\& , +an open brace +.Pq Sq { , +a logical AND +.Pq Sq && , +a logical OR +.Pq Sq || , +after the +.Sq do +or +.Sq else +keywords, +or after the closing parenthesis of an +.Sq if , +.Sq for , +or +.Sq while +statement. +Additionally, a backslash +.Pq Sq \e +can be used to escape a newline between tokens. +.Pp +An action is a sequence of statements. +A statement can be one of the following: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Xo Ic if ( Ar expression ) Ar statement +.Op Ic else Ar statement +.Xc +.It Ic while ( Ar expression ) Ar statement +.It Xo Ic for +.No ( Ar expression ; expression ; expression ) statement +.Xc +.It Xo Ic for +.No ( Ar var Ic in Ar array ) statement +.Xc +.It Xo Ic do +.Ar statement Ic while ( Ar expression ) +.Xc +.It Ic break +.It Ic continue +.It Xo Ic { +.Op Ar statement ... +.Ic } +.Xc +.It Xo Ar expression +.No # commonly +.Ar var No = Ar expression +.Xc +.It Xo Ic print +.Op Ar expression-list +.Op > Ns Ar expression +.Xc +.It Xo Ic printf Ar format +.Op Ar ... , expression-list +.Op > Ns Ar expression +.Xc +.It Ic return Op Ar expression +.It Xo Ic next +.No # skip remaining patterns on this input line +.Xc +.It Xo Ic nextfile +.No # skip rest of this file, open next, start at top +.Xc +.It Xo Ic delete +.Sm off +.Ar array Ic \&[ Ar expression Ic \&] +.Sm on +.No # delete an array element +.Xc +.It Xo Ic delete Ar array +.No # delete all elements of array +.Xc +.It Xo Ic exit +.Op Ar expression +.No # exit immediately; status is Ar expression +.Xc +.El +.Pp +Statements are terminated by +semicolons, newlines or right braces. +An empty +.Ar expression-list +stands for +.Ar $0 . +String constants are quoted +.Li \&"" , +with the usual C escapes recognized within +(see +.Xr printf 1 +for a complete list of these). +Expressions take on string or numeric values as appropriate, +and are built using the operators +.Ic + \- * / % ^ +.Pq exponentiation , +and concatenation +.Pq indicated by whitespace . +The operators +.Ic \&! ++ \-\- += \-= *= /= %= ^= +.Ic > >= < <= == != ?: +are also available in expressions. +Variables may be scalars, array elements +(denoted +.Li 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 +.Li [i,j,k] +are permitted; the constituents are concatenated, +separated by the value of +.Va SUBSEP +.Pq see the section on variables below . +.Pp +The +.Ic print +statement prints its arguments on the standard output +(or on a file if +.Pf > Ns Ar file +or +.Pf >> Ns Ar file +is present or on a pipe if +.Pf |\ \& Ar cmd +is present), separated by the current output field separator, +and terminated by the output record separator. +.Ar file +and +.Ar cmd +may be literal names or parenthesized expressions; +identical string values in different statements denote +the same open file. +The +.Ic printf +statement formats its expression list according to the format +(see +.Xr printf 1 ) . +.Pp +Patterns are arbitrary Boolean combinations +(with +.Ic "\&! || &&" ) +of regular expressions and +relational expressions. +.Nm +supports extended regular expressions +.Pq EREs . +See +.Xr re_format 7 +for more information on regular expressions. +Isolated regular expressions +in a pattern apply to the entire line. +Regular expressions may also occur in +relational expressions, using the operators +.Ic ~ +and +.Ic !~ . +.Pf / Ns Ar re Ns / +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 +through an occurrence of the second. +.Pp +A relational expression is one of the following: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ar expression matchop regular-expression +.It Ar expression relop expression +.It Ar expression Ic in Ar array-name +.It Xo Ic \&( Ns +.Ar expr , expr , \&... Ns Ic \&) in +.Ar array-name +.Xc +.El +.Pp +where a +.Ar relop +is any of the six relational operators in C, and a +.Ar matchop +is either +.Ic ~ +(matches) +or +.Ic !~ +(does not match). +A conditional is an arithmetic expression, +a relational expression, +or a Boolean combination +of these. +.Pp +The special patterns +.Ic BEGIN +and +.Ic END +may be used to capture control before the first input line is read +and after the last. +.Ic BEGIN +and +.Ic END +do not combine with other patterns. +.Pp +Variable names with special meanings: +.Pp +.Bl -tag -width "FILENAME " -compact +.It Va ARGC +Argument count, assignable. +.It Va ARGV +Argument array, assignable; +non-null members are taken as filenames. +.It Va CONVFMT +Conversion format when converting numbers +(default +.Qq Li %.6g ) . +.It Va ENVIRON +Array of environment variables; subscripts are names. +.It Va FILENAME +The name of the current input file. +.It Va FNR +Ordinal number of the current record in the current file. +.It Va FS +Regular expression used to separate fields; also settable +by option +.Fl F Ar fs . +.It Va NF +Number of fields in the current record. +.Va $NF +can be used to obtain the value of the last field in the current record. +.It Va NR +Ordinal number of the current record. +.It Va OFMT +Output format for numbers (default +.Qq Li %.6g ) . +.It Va OFS +Output field separator (default blank). +.It Va ORS +Output record separator (default newline). +.It Va RLENGTH +The length of the string matched by the +.Fn match +function. +.It Va RS +Input record separator (default newline). +.It Va RSTART +The starting position of the string matched by the +.Fn match +function. +.It Va SUBSEP +Separates multiple subscripts (default 034). +.El +.Sh FUNCTIONS +The awk language has a variety of built-in functions: +arithmetic, string, input/output, general, and bit-operation. +.Pp +Functions may be defined (at the position of a pattern-action statement) +thusly: +.Pp +.Dl 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. +.Ss Arithmetic Functions +.Bl -tag -width "atan2(y, x)" +.It Fn atan2 y x +Return the arctangent of +.Fa y Ns / Ns Fa x +in radians. +.It Fn cos x +Return the cosine of +.Fa x , +where +.Fa x +is in radians. +.It Fn exp x +Return the exponential of +.Fa x . +.It Fn int x +Return +.Fa x +truncated to an integer value. +.It Fn log x +Return the natural logarithm of +.Fa x . +.It Fn rand +Return a random number, +.Fa n , +such that +.Sm off +.Pf 0 \*(Le Fa n No \*(Lt 1 . +.Sm on +.It Fn sin x +Return the sine of +.Fa x , +where +.Fa x +is in radians. +.It Fn sqrt x +Return the square root of +.Fa x . +.It Fn srand expr +Sets seed for +.Fn rand +to +.Fa expr +and returns the previous seed. +If +.Fa expr +is omitted, the time of day is used instead. +.El +.Ss String Functions +.Bl -tag -width "split(s, a, fs)" +.It Fn gsub r t s +The same as +.Fn sub +except that all occurrences of the regular expression are replaced. +.Fn gsub +returns the number of replacements. +.It Fn index s t +The position in +.Fa s +where the string +.Fa t +occurs, or 0 if it does not. +.It Fn length s +The length of +.Fa s +taken as a string, +or of +.Va $0 +if no argument is given. +.It Fn match s r +The position in +.Fa s +where the regular expression +.Fa r +occurs, or 0 if it does not. +The variable +.Va RSTART +is set to the starting position of the matched string +.Pq which is the same as the returned value +or zero if no match is found. +The variable +.Va RLENGTH +is set to the length of the matched string, +or \-1 if no match is found. +.It Fn split s a fs +Splits the string +.Fa s +into array elements +.Va a[1] , a[2] , ... , a[n] +and returns +.Va n . +The separation is done with the regular expression +.Ar fs +or with the field separator +.Va FS +if +.Ar fs +is not given. +An empty string as field separator splits the string +into one array element per character. +.It Fn sprintf fmt expr ... +The string resulting from formatting +.Fa expr , ... +according to the +.Xr printf 1 +format +.Fa fmt . +.It Fn sub r t s +Substitutes +.Fa t +for the first occurrence of the regular expression +.Fa r +in the string +.Fa s . +If +.Fa s +is not given, +.Va $0 +is used. +An ampersand +.Pq Sq & +in +.Fa t +is replaced in string +.Fa s +with regular expression +.Fa r . +A literal ampersand can be specified by preceding it with two backslashes +.Pq Sq \e\e . +A literal backslash can be specified by preceding it with another backslash +.Pq Sq \e\e . +.Fn sub +returns the number of replacements. +.It Fn substr s m n +Return at most the +.Fa n Ns -character +substring of +.Fa s +that begins at position +.Fa m +counted from 1. +If +.Fa n +is omitted, or if +.Fa n +specifies more characters than are left in the string, +the length of the substring is limited by the length of +.Fa s . +.It Fn tolower str +Returns a copy of +.Fa str +with all upper-case characters translated to their +corresponding lower-case equivalents. +.It Fn toupper str +Returns a copy of +.Fa str +with all lower-case characters translated to their +corresponding upper-case equivalents. +.El +.Ss Input/Output and General Functions +.Bl -tag -width "getline [var] < file" +.It Fn close expr +Closes the file or pipe +.Fa expr . +.Fa expr +should match the string that was used to open the file or pipe. +.It Ar cmd | Ic getline Op Va var +Read a record of input from a stream piped from the output of +.Ar cmd . +If +.Va var +is omitted, the variables +.Va $0 +and +.Va NF +are set. +Otherwise +.Va var +is set. +If the stream is not open, it is opened. +As long as the stream remains open, subsequent calls +will read subsequent records from the stream. +The stream remains open until explicitly closed with a call to +.Fn close . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Fn fflush [expr] +Flushes any buffered output for the file or pipe +.Fa expr , +or all open files or pipes if +.Fa expr +is omitted. +.Fa expr +should match the string that was used to open the file or pipe. +.It Ic getline +Sets +.Va $0 +to the next input record from the current input file. +This form of +.Ic getline +sets the variables +.Va NF , +.Va NR , +and +.Va FNR . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Ic getline Va var +Sets +.Va $0 +to variable +.Va var . +This form of +.Ic getline +sets the variables +.Va NR +and +.Va FNR . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Xo +.Ic getline Op Va var +.Pf \ \&< Ar file +.Xc +Sets +.Va $0 +to the next record from +.Ar file . +If +.Va var +is omitted, the variables +.Va $0 +and +.Va NF +are set. +Otherwise +.Va var +is set. +If +.Ar file +is not open, it is opened. +As long as the stream remains open, subsequent calls will read subsequent +records from +.Ar file . +.Ar file +remains open until explicitly closed with a call to +.Fn close . +.It Fn system cmd +Executes +.Fa cmd +and returns its exit status. +.El +.Ss Bit-Operation Functions +.Bl -tag -width "lshift(a, b)" +.It Fn compl x +Returns the bitwise complement of integer argument x. +.It Fn and x y +Performs a bitwise AND on integer arguments x and y. +.It Fn or x y +Performs a bitwise OR on integer arguments x and y. +.It Fn xor x y +Performs a bitwise Exclusive-OR on integer arguments x and y. +.It Fn lshift x n +Returns integer argument x shifted by n bits to the left. +.It Fn rshift x n +Returns integer argument x shifted by n bits to the right. +.El +.Sh EXIT STATUS +.Ex -std awk +.Pp +But note that the +.Ic exit +expression can modify the exit status. +.Sh EXAMPLES +Print lines longer than 72 characters: +.Pp +.Dl length($0) > 72 +.Pp +Print first two fields in opposite order: +.Pp +.Dl { print $2, $1 } +.Pp +Same, with input fields separated by comma and/or blanks and tabs: +.Bd -literal -offset indent +BEGIN { FS = ",[ \et]*|[ \et]+" } + { print $2, $1 } +.Ed +.Pp +Add up first column, print sum and average: +.Bd -literal -offset indent +{ s += $1 } +END { print "sum is", s, " average is", s/NR } +.Ed +.Pp +Print all lines between start/stop pairs: +.Pp +.Dl /start/, /stop/ +.Pp +Simulate echo(1): +.Bd -literal -offset indent +BEGIN { # Simulate echo(1) + for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i] + printf "\en" + exit } +.Ed +.Pp +Print an error message to standard error: +.Bd -literal -offset indent +{ print "error!" > "/dev/stderr" } +.Ed +.Sh SEE ALSO +.Xr lex 1 , +.Xr printf 1 , +.Xr sed 1 , +.Xr re_format 7 , +.Xr script 7 +.Rs +.%A A. V. Aho +.%A B. W. Kernighan +.%A P. J. Weinberger +.%T The AWK Programming Language +.%I Addison-Wesley +.%D 1988 +.%O ISBN 0-201-07981-X +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification, +except +.Nm +does not support {n,m} pattern matching. +.Pp +The flags +.Op Fl \&dV +and +.Op Fl safe , +as well as the commands +.Cm fflush , compl , and , or , +.Cm xor , lshift , rshift , +are extensions to that specification. +.Sh HISTORY +An +.Nm +utility appeared in +.At v7 . +.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 +.Li \&"" +to it. +.Pp +The scope rules for variables in functions are a botch; +the syntax is worse. diff --git a/awk/awk.h b/awk/awk.h @@ -0,0 +1,240 @@ +/* $OpenBSD: awk.h,v 1.13 2008/10/06 20:38:33 millert Exp $ */ +/**************************************************************** +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 <assert.h> + +typedef double Awkfloat; + +/* unsigned char is more trouble than it's worth */ + +typedef unsigned char uschar; + +#define xfree(a) { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } } + +#define NN(p) ((p) ? (p) : "(null)") /* guaranteed non-null for dprintf +*/ +#define DEBUG +#ifdef DEBUG + /* uses have to be doubly parenthesized */ +# define dprintf(x) if (dbg) printf x +#else +# define dprintf(x) +#endif + +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 FAND 15 +#define FFOR 16 +#define FXOR 17 +#define FCOMPL 18 +#define FLSHIFT 19 +#define FRSHIFT 20 + +/* 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 || (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 ) + +/* structures used by regular expression matching machinery, mostly b.c: */ + +#define NCHARS (256+3) /* 256 handles 8-bit chars; 128 does 7-bit */ + /* watch out in match(), etc. */ +#define NSTATES 32 + +typedef struct rrow { + long ltype; /* long avoids pointer warnings on 64-bit */ + union { + int i; + Node *np; + uschar *up; + } lval; /* because Al stores a pointer in it! */ + int *lfollow; +} rrow; + +typedef struct fa { + uschar gototab[NSTATES][NCHARS]; + uschar out[NSTATES]; + uschar *restr; + int *posns[NSTATES]; + int anchor; + int use; + int initstat; + int curstat; + int accept; + int reset; + struct rrow re[1]; /* variable: actual size set by calling malloc */ +} fa; + + +#include "proto.h" diff --git a/awk/awkgram.y b/awk/awkgram.y @@ -0,0 +1,487 @@ +/* $OpenBSD: awkgram.y,v 1.9 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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" + +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 EMPTYRE +%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 ',' opt_nl pa_pat { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); } + | pa_pat ',' opt_nl pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $4, $6); } + | 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); } + | 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); } + | CLOSE term { $$ = op1(CLOSE, $2); } + | 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/awk/b.c b/awk/b.c @@ -0,0 +1,958 @@ +/* $OpenBSD: b.c,v 1.17 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +/* lasciate ogne speranza, voi ch'intrate. */ + +#define DEBUG + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "ytab.h" + +#define HAT (NCHARS+2) /* matches ^ in regular expr */ + /* NCHARS is 2**n */ +#define MAXLIN 22 + +#define type(v) (v)->nobj /* badly overloaded here */ +#define info(v) (v)->ntype /* badly overloaded here */ +#define left(v) (v)->narg[0] +#define right(v) (v)->narg[1] +#define parent(v) (v)->nnext + +#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL: +#define ELEAF case EMPTYRE: /* empty string in regexp */ +#define UNARY case STAR: case PLUS: case QUEST: + +/* encoding in tree Nodes: + leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE): + left is index, right contains value or pointer to value + unary (STAR, PLUS, QUEST): left is child, right is null + binary (CAT, OR): left and right are children + parent contains pointer to parent +*/ + + +int *setvec; +int *tmpset; +int maxsetvec = 0; + +int rtok; /* next token in current re */ +int rlxval; +static uschar *rlxstr; +static uschar *prestr; /* current position in current re */ +static uschar *lastre; /* origin of last re */ + +static int setcnt; +static int poscnt; + +char *patbeg; +int patlen; + +#define NFA 20 /* cache this many dynamic fa's */ +fa *fatab[NFA]; +int nfatab = 0; /* entries in fatab */ + +fa *makedfa(const char *s, int anchor) /* returns dfa for reg expr s */ +{ + int i, use, nuse; + fa *pfa; + static int now = 1; + + if (setvec == 0) { /* first time through any RE */ + maxsetvec = MAXLIN; + setvec = (int *) calloc(maxsetvec, sizeof(int)); + tmpset = (int *) calloc(maxsetvec, sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space initializing makedfa"); + } + + if (compile_time) /* a constant for sure */ + return mkdfa(s, anchor); + for (i = 0; i < nfatab; i++) /* is it there already? */ + if (fatab[i]->anchor == anchor + && strcmp((const char *) fatab[i]->restr, s) == 0) { + fatab[i]->use = now++; + return fatab[i]; + } + pfa = mkdfa(s, anchor); + if (nfatab < NFA) { /* room for another */ + fatab[nfatab] = pfa; + fatab[nfatab]->use = now++; + nfatab++; + return pfa; + } + use = fatab[0]->use; /* replace least-recently used */ + nuse = 0; + for (i = 1; i < nfatab; i++) + if (fatab[i]->use < use) { + use = fatab[i]->use; + nuse = i; + } + freefa(fatab[nuse]); + fatab[nuse] = pfa; + pfa->use = now++; + return pfa; +} + +fa *mkdfa(const char *s, int anchor) /* does the real work of making a dfa */ + /* anchor = 1 for anchored matches, else 0 */ +{ + Node *p, *p1; + fa *f; + + p = reparse(s); + p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p); + /* put ALL STAR in front of reg. exp. */ + p1 = op2(CAT, p1, op2(FINAL, NIL, NIL)); + /* put FINAL after reg. exp. */ + + poscnt = 0; + penter(p1); /* enter parent pointers and leaf indices */ + if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL) + overflo("out of space for fa"); + f->accept = poscnt-1; /* penter has computed number of positions in re */ + cfoll(f, p1); /* set up follow sets */ + freetr(p1); + if ((f->posns[0] = (int *) calloc(*(f->re[0].lfollow), sizeof(int))) == NULL) + overflo("out of space in makedfa"); + if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL) + overflo("out of space in makedfa"); + *f->posns[1] = 0; + f->initstat = makeinit(f, anchor); + f->anchor = anchor; + f->restr = (uschar *) tostring(s); + return f; +} + +int makeinit(fa *f, int anchor) +{ + int i, k; + + f->curstat = 2; + f->out[2] = 0; + f->reset = 0; + k = *(f->re[0].lfollow); + xfree(f->posns[2]); + if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL) + overflo("out of space in makeinit"); + for (i=0; i <= k; i++) { + (f->posns[2])[i] = (f->re[0].lfollow)[i]; + } + if ((f->posns[2])[1] == f->accept) + f->out[2] = 1; + for (i=0; i < NCHARS; i++) + f->gototab[2][i] = 0; + f->curstat = cgoto(f, 2, HAT); + if (anchor) { + *f->posns[2] = k-1; /* leave out position 0 */ + for (i=0; i < k; i++) { + (f->posns[0])[i] = (f->posns[2])[i]; + } + + f->out[0] = f->out[2]; + if (f->curstat != 2) + --(*f->posns[f->curstat]); + } + return f->curstat; +} + +void penter(Node *p) /* set up parent pointers and leaf indices */ +{ + switch (type(p)) { + ELEAF + LEAF + info(p) = poscnt; + poscnt++; + break; + UNARY + penter(left(p)); + parent(left(p)) = p; + break; + case CAT: + case OR: + penter(left(p)); + penter(right(p)); + parent(left(p)) = p; + parent(right(p)) = p; + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in penter", type(p)); + break; + } +} + +void freetr(Node *p) /* free parse tree */ +{ + switch (type(p)) { + ELEAF + LEAF + xfree(p); + break; + UNARY + freetr(left(p)); + xfree(p); + break; + case CAT: + case OR: + freetr(left(p)); + freetr(right(p)); + xfree(p); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in freetr", type(p)); + break; + } +} + +/* in the parsing of regular expressions, metacharacters like . have */ +/* to be seen literally; \056 is not a metacharacter. */ + +int hexstr(uschar **pp) /* find and eval hex string at pp, return new p */ +{ /* only pick up one 8-bit byte (2 chars) */ + uschar *p; + int n = 0; + int i; + + for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) { + if (isdigit(*p)) + n = 16 * n + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + n = 16 * n + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + n = 16 * n + *p - 'A' + 10; + } + *pp = (uschar *) p; + return n; +} + +#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */ + +int quoted(uschar **pp) /* pick up next thing after a \\ */ + /* and increment *pp */ +{ + uschar *p = *pp; + int c; + + if ((c = *p++) == 't') + c = '\t'; + else if (c == 'n') + c = '\n'; + else if (c == 'f') + c = '\f'; + else if (c == 'r') + c = '\r'; + else if (c == 'b') + c = '\b'; + else if (c == '\\') + c = '\\'; + else if (c == 'x') { /* hexadecimal goo follows */ + c = hexstr(&p); /* this adds a null if number is invalid */ + } else if (isoctdigit(c)) { /* \d \dd \ddd */ + int n = c - '0'; + if (isoctdigit(*p)) { + n = 8 * n + *p++ - '0'; + if (isoctdigit(*p)) + n = 8 * n + *p++ - '0'; + } + c = n; + } /* else */ + /* c = c; */ + *pp = p; + return c; +} + +char *cclenter(const char *argp) /* add a character class */ +{ + int i, c, c2; + uschar *p = (uschar *) argp; + uschar *op, *bp; + static uschar *buf = 0; + static int bufsz = 100; + + op = p; + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space for character class [%.10s...] 1", p); + bp = buf; + for (i = 0; (c = *p++) != 0; ) { + if (c == '\\') { + c = quoted(&p); + } else if (c == '-' && i > 0 && bp[-1] != 0) { + if (*p != 0) { + c = bp[-1]; + c2 = *p++; + if (c2 == '\\') + c2 = quoted(&p); + if (c > c2) { /* empty; ignore */ + bp--; + i--; + continue; + } + while (c < c2) { + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1")) + FATAL("out of space for character class [%.10s...] 2", p); + *bp++ = ++c; + i++; + } + continue; + } + } + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2")) + FATAL("out of space for character class [%.10s...] 3", p); + *bp++ = c; + i++; + } + *bp = 0; + dprintf( ("cclenter: in = |%s|, out = |%s|\n", op, buf) ); + xfree(op); + return (char *) tostring((char *) buf); +} + +void overflo(const char *s) +{ + FATAL("regular expression too big: %.30s...", s); +} + +void cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */ +{ + int i; + int *p; + + switch (type(v)) { + ELEAF + LEAF + f->re[info(v)].ltype = type(v); + f->re[info(v)].lval.np = right(v); + while (f->accept >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in cfoll()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + follow(v); /* computes setvec and setcnt */ + if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL) + overflo("out of space building follow set"); + f->re[info(v)].lfollow = p; + *p = setcnt; + for (i = f->accept; i >= 0; i--) + if (setvec[i] == 1) + *++p = i; + break; + UNARY + cfoll(f,left(v)); + break; + case CAT: + case OR: + cfoll(f,left(v)); + cfoll(f,right(v)); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in cfoll", type(v)); + } +} + +int first(Node *p) /* collects initially active leaves of p into setvec */ + /* returns 0 if p matches empty string */ +{ + int b, lp; + + switch (type(p)) { + ELEAF + LEAF + lp = info(p); /* look for high-water mark of subscripts */ + while (setcnt >= maxsetvec || lp >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in first()"); + } + if (type(p) == EMPTYRE) { + setvec[lp] = 0; + return(0); + } + if (setvec[lp] != 1) { + setvec[lp] = 1; + setcnt++; + } + if (type(p) == CCL && (*(char *) right(p)) == '\0') + return(0); /* empty CCL */ + else return(1); + case PLUS: + if (first(left(p)) == 0) return(0); + return(1); + case STAR: + case QUEST: + first(left(p)); + return(0); + case CAT: + if (first(left(p)) == 0 && first(right(p)) == 0) return(0); + return(1); + case OR: + b = first(right(p)); + if (first(left(p)) == 0 || b == 0) return(0); + return(1); + } + FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */ + return(-1); +} + +void follow(Node *v) /* collects leaves that can follow v into setvec */ +{ + Node *p; + + if (type(v) == FINAL) + return; + p = parent(v); + switch (type(p)) { + case STAR: + case PLUS: + first(v); + follow(p); + return; + + case OR: + case QUEST: + follow(p); + return; + + case CAT: + if (v == left(p)) { /* v is left child of p */ + if (first(right(p)) == 0) { + follow(p); + return; + } + } else /* v is right child */ + follow(p); + return; + } +} + +int member(int c, const char *sarg) /* is c in s? */ +{ + uschar *s = (uschar *) sarg; + + while (*s) + if (c == *s++) + return(1); + return(0); +} + +int match(fa *f, const char *p0) /* shortest match ? */ +{ + int s, ns; + uschar *p = (uschar *) p0; + + s = f->reset ? makeinit(f,0) : f->initstat; + if (f->out[s]) + return(1); + do { + /* assert(*p < NCHARS); */ + if ((ns = f->gototab[s][*p]) != 0) + s = ns; + else + s = cgoto(f, s, *p); + if (f->out[s]) + return(1); + } while (*p++ != 0); + return(0); +} + +int pmatch(fa *f, const char *p0) /* longest match, for sub */ +{ + int s, ns; + uschar *p = (uschar *) p0; + uschar *q; + int i, k; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patbeg = (char *) p; + patlen = -1; + do { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen >= 0) { + patbeg = (char *) p; + return(1); + } + else + goto nextin; /* no match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen >= 0) { + patbeg = (char *) p; + return(1); + } + nextin: + s = 2; + if (f->reset) { + for (i = 2; i <= f->curstat; i++) + xfree(f->posns[i]); + k = *f->posns[0]; + if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL) + overflo("out of space in pmatch"); + for (i = 0; i <= k; i++) + (f->posns[2])[i] = (f->posns[0])[i]; + f->initstat = f->curstat = 2; + f->out[2] = f->out[0]; + for (i = 0; i < NCHARS; i++) + f->gototab[2][i] = 0; + } + } while (*p++ != 0); + return (0); +} + +int nematch(fa *f, const char *p0) /* non-empty match, for sub */ +{ + int s, ns; + uschar *p = (uschar *) p0; + uschar *q; + int i, k; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patlen = -1; + while (*p) { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen > 0) { + patbeg = (char *) p; + return(1); + } else + goto nnextin; /* no nonempty match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen > 0 ) { + patbeg = (char *) p; + return(1); + } + nnextin: + s = 2; + if (f->reset) { + for (i = 2; i <= f->curstat; i++) + xfree(f->posns[i]); + k = *f->posns[0]; + if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL) + overflo("out of state space"); + for (i = 0; i <= k; i++) + (f->posns[2])[i] = (f->posns[0])[i]; + f->initstat = f->curstat = 2; + f->out[2] = f->out[0]; + for (i = 0; i < NCHARS; i++) + f->gototab[2][i] = 0; + } + p++; + } + return (0); +} + +Node *reparse(const char *p) /* parses regular expression pointed to by p */ +{ /* uses relex() to scan regular expression */ + Node *np; + + dprintf( ("reparse <%s>\n", p) ); + lastre = prestr = (uschar *) p; /* prestr points to string to be parsed */ + rtok = relex(); + /* GNU compatibility: an empty regexp matches anything */ + if (rtok == '\0') { + /* FATAL("empty regular expression"); previous */ + return(op2(EMPTYRE, NIL, NIL)); + } + np = regexp(); + if (rtok != '\0') + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + return(np); +} + +Node *regexp(void) /* top-level parse of reg expr */ +{ + return (alt(concat(primary()))); +} + +Node *primary(void) +{ + Node *np; + + switch (rtok) { + case CHAR: + np = op2(CHAR, NIL, itonp(rlxval)); + rtok = relex(); + return (unary(np)); + case ALL: + rtok = relex(); + return (unary(op2(ALL, NIL, NIL))); + case EMPTYRE: + rtok = relex(); + return (unary(op2(ALL, NIL, NIL))); + case DOT: + rtok = relex(); + return (unary(op2(DOT, NIL, NIL))); + case CCL: + np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case NCCL: + np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case '^': + rtok = relex(); + return (unary(op2(CHAR, NIL, itonp(HAT)))); + case '$': + rtok = relex(); + return (unary(op2(CHAR, NIL, NIL))); + case '(': + rtok = relex(); + if (rtok == ')') { /* special pleading for () */ + rtok = relex(); + return unary(op2(CCL, NIL, (Node *) tostring(""))); + } + np = regexp(); + if (rtok == ')') { + rtok = relex(); + return (unary(np)); + } + else + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + default: + FATAL("illegal primary in regular expression %s at %s", lastre, prestr); + } + return 0; /*NOTREACHED*/ +} + +Node *concat(Node *np) +{ + switch (rtok) { + case CHAR: case DOT: case ALL: case EMPTYRE: case CCL: case NCCL: case '$': case '(': + return (concat(op2(CAT, np, primary()))); + } + return (np); +} + +Node *alt(Node *np) +{ + if (rtok == OR) { + rtok = relex(); + return (alt(op2(OR, np, concat(primary())))); + } + return (np); +} + +Node *unary(Node *np) +{ + switch (rtok) { + case STAR: + rtok = relex(); + return (unary(op2(STAR, np, NIL))); + case PLUS: + rtok = relex(); + return (unary(op2(PLUS, np, NIL))); + case QUEST: + rtok = relex(); + return (unary(op2(QUEST, np, NIL))); + default: + return (np); + } +} + +/* + * Character class definitions conformant to the POSIX locale as + * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source + * and operating character sets are both ASCII (ISO646) or supersets + * thereof. + * + * Note that to avoid overflowing the temporary buffer used in + * relex(), the expanded character class (prior to range expansion) + * must be less than twice the size of their full name. + */ + +/* Because isblank doesn't show up in any of the header files on any + * system i use, it's defined here. if some other locale has a richer + * definition of "blank", define HAS_ISBLANK and provide your own + * version. + * the parentheses here are an attempt to find a path through the maze + * of macro definition and/or function and/or version provided. thanks + * to nelson beebe for the suggestion; let's see if it works everywhere. + */ + +#ifndef HAS_ISBLANK + +int (xisblank)(int c) +{ + return c==' ' || c=='\t'; +} + +#endif + +struct charclass { + const char *cc_name; + int cc_namelen; + int (*cc_func)(int); +} charclasses[] = { + { "alnum", 5, isalnum }, + { "alpha", 5, isalpha }, +#ifndef HAS_ISBLANK + { "blank", 5, isspace }, /* was isblank */ +#else + { "blank", 5, isblank }, +#endif + { "cntrl", 5, iscntrl }, + { "digit", 5, isdigit }, + { "graph", 5, isgraph }, + { "lower", 5, islower }, + { "print", 5, isprint }, + { "punct", 5, ispunct }, + { "space", 5, isspace }, + { "upper", 5, isupper }, + { "xdigit", 6, isxdigit }, + { NULL, 0, NULL }, +}; + + +int relex(void) /* lexical analyzer for reparse */ +{ + int c, n; + int cflag; + static uschar *buf = 0; + static int bufsz = 100; + uschar *bp; + struct charclass *cc; + int i; + + switch (c = *prestr++) { + case '|': return OR; + case '*': return STAR; + case '+': return PLUS; + case '?': return QUEST; + case '.': return DOT; + case '\0': prestr--; return '\0'; + case '^': + case '$': + case '(': + case ')': + return c; + case '\\': + rlxval = quoted(&prestr); + return CHAR; + default: + rlxval = c; + return CHAR; + case '[': + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space in reg expr %.10s..", lastre); + bp = buf; + if (*prestr == '^') { + cflag = 1; + prestr++; + } + else + cflag = 0; + n = 2 * strlen((const char *) prestr)+1; + if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1")) + FATAL("out of space for reg expr %.10s...", lastre); + for (; ; ) { + if ((c = *prestr++) == '\\') { + *bp++ = '\\'; + if ((c = *prestr++) == '\0') + FATAL("nonterminated character class %.20s...", lastre); + *bp++ = c; + /* } else if (c == '\n') { */ + /* FATAL("newline in character class %.20s...", lastre); */ + } else if (c == '[' && *prestr == ':') { + /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */ + for (cc = charclasses; cc->cc_name; cc++) + if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0) + break; + if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' && + prestr[2 + cc->cc_namelen] == ']') { + prestr += cc->cc_namelen + 3; + for (i = 0; i < NCHARS; i++) { + if (!adjbuf((char **) &buf, &bufsz, bp-buf+1, 100, (char **) &bp, "relex2")) + FATAL("out of space for reg expr %.10s...", lastre); + if (cc->cc_func(i)) { + *bp++ = i; + n++; + } + } + } else + *bp++ = c; + } else if (c == '\0') { + FATAL("nonterminated character class %.20s", lastre); + } else if (bp == buf) { /* 1st char is special */ + *bp++ = c; + } else if (c == ']') { + *bp++ = 0; + rlxstr = (uschar *) tostring((char *) buf); + if (cflag == 0) + return CCL; + else + return NCCL; + } else + *bp++ = c; + } + } +} + +int cgoto(fa *f, int s, int c) +{ + int i, j, k; + int *p, *q; + + assert(c == HAT || c < NCHARS); + while (f->accept >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in cgoto()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + /* compute positions of gototab[s,c] into setvec */ + p = f->posns[s]; + for (i = 1; i <= *p; i++) { + if ((k = f->re[p[i]].ltype) != FINAL) { + if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np)) + || (k == DOT && c != 0 && c != HAT) + || (k == ALL && c != 0) + || (k == EMPTYRE && c != 0) + || (k == CCL && member(c, (char *) f->re[p[i]].lval.up)) + || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) { + q = f->re[p[i]].lfollow; + for (j = 1; j <= *q; j++) { + if (q[j] >= maxsetvec) { + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("cgoto overflow"); + } + if (setvec[q[j]] == 0) { + setcnt++; + setvec[q[j]] = 1; + } + } + } + } + } + /* determine if setvec is a previous state */ + tmpset[0] = setcnt; + j = 1; + for (i = f->accept; i >= 0; i--) + if (setvec[i]) { + tmpset[j++] = i; + } + /* tmpset == previous state? */ + for (i = 1; i <= f->curstat; i++) { + p = f->posns[i]; + if ((k = tmpset[0]) != p[0]) + goto different; + for (j = 1; j <= k; j++) + if (tmpset[j] != p[j]) + goto different; + /* setvec is state i */ + f->gototab[s][c] = i; + return i; + different:; + } + + /* add tmpset to current set of states */ + if (f->curstat >= NSTATES-1) { + f->curstat = 2; + f->reset = 1; + for (i = 2; i < NSTATES; i++) + xfree(f->posns[i]); + } else + ++(f->curstat); + for (i = 0; i < NCHARS; i++) + f->gototab[f->curstat][i] = 0; + xfree(f->posns[f->curstat]); + if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL) + overflo("out of space in cgoto"); + + f->posns[f->curstat] = p; + f->gototab[s][c] = f->curstat; + for (i = 0; i <= setcnt; i++) + p[i] = tmpset[i]; + if (setvec[f->accept]) + f->out[f->curstat] = 1; + else + f->out[f->curstat] = 0; + return f->curstat; +} + + +void freefa(fa *f) /* free a finite automaton */ +{ + int i; + + if (f == NULL) + return; + for (i = 0; i <= f->curstat; i++) + xfree(f->posns[i]); + for (i = 0; i <= f->accept; i++) { + xfree(f->re[i].lfollow); + if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL) + xfree((f->re[i].lval.np)); + } + xfree(f->restr); + xfree(f); +} diff --git a/awk/lex.c b/awk/lex.c @@ -0,0 +1,597 @@ +/* $OpenBSD: lex.c,v 1.12 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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 "ytab.h" + +extern YYSTYPE yylval; +extern int infunc; + +int lineno = 1; +int bracecnt = 0; +int brackcnt = 0; +int parencnt = 0; + +typedef struct Keyword { + const char *word; + int sub; + int type; +} Keyword; + +Keyword keywords[] ={ /* keep sorted: binary searched */ + { "BEGIN", XBEGIN, XBEGIN }, + { "END", XEND, XEND }, + { "NF", VARNF, VARNF }, + { "and", FAND, BLTIN }, + { "atan2", FATAN, BLTIN }, + { "break", BREAK, BREAK }, + { "close", CLOSE, CLOSE }, + { "compl", FCOMPL, BLTIN }, + { "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 }, + { "lshift", FLSHIFT, BLTIN }, + { "match", MATCHFCN, MATCHFCN }, + { "next", NEXT, NEXT }, + { "nextfile", NEXTFILE, NEXTFILE }, + { "or", FFOR, BLTIN }, + { "print", PRINT, PRINT }, + { "printf", PRINTF, PRINTF }, + { "rand", FRAND, BLTIN }, + { "return", RETURN, RETURN }, + { "rshift", FRSHIFT, BLTIN }, + { "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 }, + { "while", WHILE, WHILE }, + { "xor", FXOR, BLTIN }, +}; + +#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); } + +int peek(void); +int gettok(char **, int *); +int binsearch(char *, Keyword *, int); + +int peek(void) +{ + int c = input(); + unput(c); + return c; +} + +int gettok(char **pbuf, int *psz) /* get next input token */ +{ + int c, retc; + 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, "gettok")) + FATAL( "out of space for name %.10s...", buf ); + if (isalnum(c) || c == '_') + *bp++ = c; + else { + *bp = 0; + unput(c); + break; + } + } + *bp = 0; + retc = 'a'; /* alphanumeric */ + } else { /* maybe it's a number, but could be . */ + 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, "gettok")) + 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 */ + if (rem == buf) { /* it wasn't a valid number at all */ + buf[1] = 0; /* return one character as token */ + retc = buf[0]; /* character is its own type */ + unputstr(rem+1); /* put rest back for later */ + } else { /* some prefix was a number */ + unputstr(rem); /* put rest back for later */ + rem[0] = 0; /* truncate buf after number part */ + retc = '0'; /* type is number */ + } + } + *pbuf = buf; + *psz = sz; + return retc; +} + +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 = 5; /* BUG: setting this small causes core dump! */ + + 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)) { + 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 (isalpha(c)) { + if (strcmp(buf, "NF") == 0) { /* very special */ + unputstr("(NF)"); + RET(INDIRECT); + } + c = peek(); + if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) { + unputstr(buf); + RET(INDIRECT); + } + yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab); + RET(IVAR); + } else if (c == 0) { /* */ + SYNTAX( "unexpected end of input after $" ); + RET(';'); + } 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, "string")) + FATAL("out of space for string %.10s...", buf); + switch (c) { + case '\n': + case '\r': + case 0: + SYNTAX( "non-terminated string %.10s...", buf ); + lineno++; + if (c == 0) /* hopeless */ + FATAL( "giving up" ); + 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", (unsigned int *) &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])); +/* BUG: this ought to be inside the if; in theory could fault (daniel barrett) */ + kp = keywords + n; + if (n != -1) { /* found in table */ + yylval.i = kp->sub; + switch (kp->type) { /* special handling */ + case BLTIN: + if (kp->sub == FSYSTEM && 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 yylex will return a regular expression */ +{ + reg = 1; +} + +int regexpr(void) +{ + int c, openclass = 0; + 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()) != '/' || openclass == 1) && c != 0; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr")) + 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 { + if (c == '[') + openclass = 1; + else if (c == ']') + openclass = 0; + *bp++ = c; + } + } + *bp = 0; + if (c == 0) + SYNTAX("non-terminated regular expression %.10s...", buf); + 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 = (uschar)*--yysptr; + else if (lexprog != NULL) { /* awk '...' */ + if ((c = (uschar)*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(const char *s) /* put a string back on input */ +{ + int i; + + for (i = strlen(s)-1; i >= 0; i--) + unput(s[i]); +} diff --git a/awk/lib.c b/awk/lib.c @@ -0,0 +1,739 @@ +/* $OpenBSD: lib.c,v 1.20 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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 <unistd.h> +#include <stdarg.h> +#include "awk.h" +#include "util.h" +#include "ytab.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 2 +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) +{ + if ( (record = (char *) malloc(n)) == NULL + || (fields = (char *) malloc(n+1)) == NULL + || (fldtab = (Cell **) calloc(nfields+1, sizeof(Cell *))) == NULL + || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL ) + FATAL("out of space for $0 and fields"); + *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; + snprintf(temp, sizeof temp, "%d", i); + fldtab[i]->nval = tostring(temp); + } +} + +void initgetrec(void) +{ + int i; + char *p; + + for (i = 1; i < *ARGC; i++) { + p = getargv(i); /* find 1st real filename */ + if (p == NULL || *p == '\0') { /* deleted or zapped */ + argno++; + continue; + } + if (!isclvar(p)) { + setsval(lookup("FILENAME", symtab), p); + return; + } + setclvar(p); /* a commandline assignment before filename */ + argno++; + } + infile = stdin; /* no filenames, so use stdin */ +} + +static int firsttime = 1; + +int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ +{ /* note: cares whether buf == record */ + int c; + char *buf = *pbuf; + uschar saveb0; + int bufsize = *pbufsize, savebufsize = bufsize; + + 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; + } + saveb0 = buf[0]; + 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 == NULL || *file == '\0') { /* deleted or 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++; + } + buf[0] = saveb0; + *pbuf = buf; + *pbufsize = savebufsize; + return 0; /* true end of file */ +} + +void nextfile(void) +{ + if (infile != NULL && 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); + /*fflush(stdout); avoids some buffering problem but makes it 25% slower*/ + strlcpy(inputFS, *FS, sizeof inputFS); /* 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; + + snprintf(temp, sizeof temp, "%d", n); + if (lookup(temp, ARGVtab) == NULL) + return NULL; + 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 */ + /* possibly with a final trailing \0 not associated with any field */ + 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+2)) == NULL) /* possibly 2 final \0s */ + FATAL("out of space for fields in fldbld %d", n); + fieldssize = n; + } + fr = fields; + i = 0; /* number of fields accumulated here */ + strlcpy(inputFS, *FS, sizeof(inputFS)); + 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 */ + /* subtlecase : if length(FS) == 1 && length(RS > 0) + * \n is NOT a field separator (cf awk book 61,84). + * this variable is tested in the inner while loop. + */ + int rtest = '\n'; /* normal case */ + if (strlen(*RS) > 0) + rtest = '\0'; + 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 != rtest && *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 out of range 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; + size_t s; + + if (n > nf) + nf = n; + s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ + if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */ + fldtab = (Cell **) realloc(fldtab, s); + else /* overflow sizeof int */ + xfree(fldtab); /* make it null */ + if (fldtab == NULL) + FATAL("out of space creating %d fields", nf); + makefields(nfields+1, nf); + nfields = nf; +} + +int refldbld(const char *rec, const 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; + int i, tempstat, n; + fa *pfa; + + 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; + pfa = makedfa(fs, 1); + dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); + tempstat = pfa->initstat; + 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(pfa, rec)) { + pfa->initstat = 2; /* horrible coupling to b.c */ + 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) ); + strlcpy(fr, rec, fields + fieldssize - fr); + pfa->initstat = tempstat; + 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, (void*)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, (void*)fldtab[0]) ); + dprintf( ("recbld = |%s|\n", record) ); + donerec = 1; +} + +int errorflag = 0; + +void yyerror(const char *s) +{ + SYNTAX("%s", s); +} + +void SYNTAX(const 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); + fprintf(stderr, " at source line %d", lineno); + if (curfname != NULL) + fprintf(stderr, " in function %s", curfname); + if (compile_time == 1 && cursource() != NULL) + fprintf(stderr, " source file %s", cursource()); + fprintf(stderr, "\n"); + errorflag = 2; + eprint(); +} + +void fpecatch(int sig) +{ + extern Node *curnode; + char buf[1024]; + + snprintf(buf, sizeof buf, "floating point exception\n"); + write(STDERR_FILENO, buf, strlen(buf)); + + if (compile_time != 2 && NR && *NR > 0) { + snprintf(buf, sizeof buf, " input record number %d", (int) (*FNR)); + write(STDERR_FILENO, buf, strlen(buf)); + + if (strcmp(*FILENAME, "-") != 0) { + snprintf(buf, sizeof buf, ", file %s", *FILENAME); + write(STDERR_FILENO, buf, strlen(buf)); + } + write(STDERR_FILENO, "\n", 1); + } + if (compile_time != 2 && curnode) { + snprintf(buf, sizeof buf, " source line number %d", curnode->lineno); + write(STDERR_FILENO, buf, strlen(buf)); + } else if (compile_time != 2 && lineno) { + snprintf(buf, sizeof buf, " source line number %d", lineno); + write(STDERR_FILENO, buf, strlen(buf)); + } + if (compile_time == 1 && cursource() != NULL) { + snprintf(buf, sizeof buf, " source file %s", cursource()); + write(STDERR_FILENO, buf, strlen(buf)); + } + write(STDERR_FILENO, "\n", 1); + if (dbg > 1) /* core dump if serious debugging on */ + abort(); + _exit(1); +} + +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(const 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(const 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; + + fprintf(stderr, "\n"); + if (compile_time != 2 && NR && *NR > 0) { + fprintf(stderr, " input record number %d", (int) (*FNR)); + if (strcmp(*FILENAME, "-") != 0) + fprintf(stderr, ", file %s", *FILENAME); + fprintf(stderr, "\n"); + } + if (compile_time != 2 && curnode) + fprintf(stderr, " source line number %d", curnode->lineno); + else if (compile_time != 2 && lineno) + fprintf(stderr, " source line number %d", lineno); + if (compile_time == 1 && cursource() != NULL) + fprintf(stderr, " source file %s", cursource()); + 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, const 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(const char *s) /* is s of form var=something ? */ +{ + const char *os = s; + + if (!isalpha((uschar) *s) && *s != '_') + return 0; + for ( ; *s; s++) + if (!(isalnum((uschar) *s) || *s == '_')) + break; + return *s == '=' && s > os && *(s+1) != '='; +} + +/* strtod is supposed to be a proper test of what's a valid number */ +/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ +/* wrong: violates 4.10.1.4 of ansi C standard */ + +#include <math.h> +int is_number(const char *s) +{ + double r; + char *ep; + 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/awk/main.c b/awk/main.c @@ -0,0 +1,212 @@ +/* $OpenBSD: main.c,v 1.17 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +const char *version = "version 20110810"; + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include "awk.h" +#include "ytab.h" + +extern char **environ; +extern int nfields; +extern char *__progname; + +int dbg = 0; +Awkfloat srand_seed = 1; +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 */ + +#define MAX_PFILE 20 /* max number of -f's */ + +char *pfile[MAX_PFILE]; /* 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[]) +{ + const char *fs = NULL; + + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ + cmdname = __progname; + if (argc == 1) { + fprintf(stderr, "usage: %s [-safe] [-V] [-d[n]] [-F fs] " + "[-v var=value] [prog | -f progfile]\n\tfile ...\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 */ + if (argv[1][2] != 0) { /* arg is -fsomething */ + if (npfile >= MAX_PFILE - 1) + FATAL("too many -f options"); + pfile[npfile++] = &argv[1][2]; + } else { /* arg is -f something */ + argc--; argv++; + if (argc <= 1) + FATAL("no program filename"); + if (npfile >= MAX_PFILE - 1) + FATAL("too many -f options"); + 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) { /* arg is -vsomething */ + if (isclvar(&argv[1][2])) + setclvar(&argv[1][2]); + else + FATAL("invalid -v option argument: %s", &argv[1][2]); + } else { /* arg is -v something */ + argc--; argv++; + if (argc <= 1) + FATAL("no variable name"); + if (isclvar(argv[1])) + setclvar(argv[1]); + else + FATAL("invalid -v option argument: %s", argv[1]); + } + 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(); + setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */ + 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/awk/maketab.c b/awk/maketab.c @@ -0,0 +1,172 @@ +/* $OpenBSD: maketab.c,v 1.11 2010/06/13 17:58:19 millert Exp $ */ +/**************************************************************** +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 ytab.h, produced by yacc. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "ytab.h" + +struct xx +{ int token; + const char *name; + const 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, "awkgetline", "getline" }, + { 0, "", "" }, +}; + +#define SIZE (LASTTOKEN - FIRSTTOKEN + 1) +const char *table[SIZE]; +char *names[SIZE]; + +int main(int argc, char *argv[]) +{ + const 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 \"ytab.h\"\n\n"); + for (i = SIZE; --i >= 0; ) + names[i] = ""; + + if ((fp = fopen("ytab.h", "r")) == NULL) { + fprintf(stderr, "maketab: can't open ytab.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 (n != 4 || c != '#' || strcmp(def, "define") != 0) + continue; /* not a valid #define */ + if (tok < FIRSTTOKEN || tok > LASTTOKEN) { + /* fprintf(stderr, "maketab: funny token %d %s ignored\n", tok, buf); */ + continue; + } + names[tok-FIRSTTOKEN] = (char *) strdup(name); + if (names[tok-FIRSTTOKEN] == NULL) { + fprintf(stderr, "maketab: out of memory\n"); + exit(1); + } + 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(" snprintf(buf, sizeof buf, \"token %%d\", n);\n"); + printf(" return buf;\n"); + printf(" }\n"); + printf(" return printname[n-FIRSTTOKEN];\n"); + printf("}\n"); + return 0; +} diff --git a/awk/parse.c b/awk/parse.c @@ -0,0 +1,277 @@ +/* $OpenBSD: parse.c,v 1.6 2002/12/19 21:24:28 millert Exp $ */ +/**************************************************************** +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 "ytab.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; + } + if (isarg(v->nval) != -1) { + SYNTAX( "`%s' is both function name and argument 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(const 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/awk/proto.h b/awk/proto.h @@ -0,0 +1,196 @@ +/* $OpenBSD: proto.h,v 1.9 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +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(const char *); +extern int yylook(void); +extern int yyback(int *, int); +extern int yyinput(void); + +extern fa *makedfa(const char *, int); +extern fa *mkdfa(const char *, int); +extern int makeinit(fa *, int); +extern void penter(Node *); +extern void freetr(Node *); +extern int hexstr(uschar **); +extern int quoted(uschar **); +extern char *cclenter(const char *); +extern void overflo(const char *); +extern void cfoll(fa *, Node *); +extern int first(Node *); +extern void follow(Node *); +extern int member(int, const char *); +extern int match(fa *, const char *); +extern int pmatch(fa *, const char *); +extern int nematch(fa *, const char *); +extern Node *reparse(const char *); +extern Node *regexp(void); +extern Node *primary(void); +extern Node *concat(Node *); +extern Node *alt(Node *); +extern Node *unary(Node *); +extern int relex(void); +extern int cgoto(fa *, int, int); +extern void freefa(fa *); + +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(const 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 *, const char *); +extern Cell *setsymtab(const char *, const char *, double, unsigned int, Array *); +extern int hash(const char *, int); +extern void rehash(Array *); +extern Cell *lookup(const char *, Array *); +extern double setfval(Cell *, double); +extern void funnyvar(Cell *, const char *); +extern char *setsval(Cell *, const char *); +extern double getfval(Cell *); +extern char *getsval(Cell *); +extern char *getpssval(Cell *); /* for print */ +extern char *tostring(const char *); +extern char *qstring(const 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(const char *, const char *); +extern void recbld(void); +extern Cell *fieldadr(int); +extern void yyerror(const char *); +extern void fpecatch(int); +extern void bracecheck(void); +extern void bcheck2(int, int, int); +extern void SYNTAX(const char *, ...); +extern void FATAL(const char *, ...); +extern void WARNING(const char *, ...); +extern void error(void); +extern void eprint(void); +extern void bclass(int); +extern double errcheck(double, const char *); +extern int isclvar(const char *); +extern int is_number(const char *); + +extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, const 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 *awkgetline(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 *, const 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, const char *); +extern const 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/awk/reallocarray.c b/awk/reallocarray.c @@ -0,0 +1,38 @@ +/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/awk/run.c b/awk/run.c @@ -0,0 +1,2027 @@ +/* $OpenBSD: run.c,v 1.35 2014/10/11 03:07:29 doug Exp $ */ +/**************************************************************** +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 <limits.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "awk.h" +#include "util.h" +#include "ytab.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); +} +*/ + +/* do we really need these? */ +/* #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; +int use_srandom; +extern int pairstack[]; +extern Awkfloat srand_seed; + +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 */ + +void stdinit(void); +void flush_all(void); + +/* buffer memory management */ +int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr, + const 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); + dprintf( ("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, *pbuf, tbuf) ); + 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 */ +{ + stdinit(); + execute(a); + closeall(); +} + +Cell *execute(Node *u) /* execute a node of the parse tree */ +{ + 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); + } + if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */ + FATAL("illegal statement"); + proc = proctab[a->nobj-FIRSTTOKEN]; + x = (*proc)(a->narg, a->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; + int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */ + 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, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(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); + } else if (t == y && t->csub == CCOPY) { + t->csub = CTEMP; + tempfree(t); + freed = 1; + } + } + tempfree(fcn); + if (isexit(y) || isnext(y)) + return y; + if (freed == 0) { + tempfree(y); /* don't 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? */ + if (isstr(x)) + y->sval = tostring(x->sval); + 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 *awkgetline(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, "array")) + FATAL("out of memory for %s[%s...]", x->nval, buf); + strlcat(buf, s, bufsz); + if (np->nnext) + strlcat(buf, *SUBSEP, bufsz); + tempfree(y); + } + if (!isarr(x)) { + dprintf( ("making %s into an array\n", NN(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, "awkdelete")) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strlcat(buf, s, bufsz); + if (np->nnext) + strlcat(buf, *SUBSEP, bufsz); + 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, "intest")) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strlcat(buf, s, bufsz); + tempfree(x); + if (p->nnext) + strlcat(buf, *SUBSEP, bufsz); + } + 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; + fa *pfa; + int (*mf)(fa *, const char *) = match, mode = 0; + + if (n == MATCHFCN) { + mf = pmatch; + mode = 1; + } + x = execute(a[1]); /* a[1] = target text */ + s = getsval(x); + if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */ + i = (*mf)((fa *) a[2], s); + else { + y = execute(a[2]); /* a[2] = regular expr */ + t = getsval(y); + pfa = makedfa(t, mode); + i = (*mf)(pfa, s); + tempfree(y); + } + tempfree(x); + if (n == MATCHFCN) { + int start = patbeg - s + 1; + if (patlen < 0) + start = 0; + setfval(rstartloc, (Awkfloat) start); + setfval(rlengthloc, (Awkfloat) 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", NN(a->nval), NN(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] ) */ +{ + Awkfloat val; + Cell *x; + int m; + char *s; + + x = execute(a[0]); + val = getfval(x); /* freebsd: defend against super large field numbers */ + if ((Awkfloat)INT_MAX < val) + FATAL("trying to access out of range field %s", x->nval); + m = (int) val; + 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; + 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 = 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(); + temp = s[n+m-1]; /* with thanks to John Linderman */ + s[n+m-1] = '\0'; + setsval(y, s + m - 1); + s[n+m-1] = 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) (p1 - s1 + 1); /* origin 1 */ + break; + } + } + tempfree(x); + tempfree(y); + setfval(z, v); + return(z); +} + +#define MAXNUMSIZE 50 + +int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like conversions */ +{ + char *fmt; + char *p, *t; + const char *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, "format1"); + 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, "format2"); + for (t = fmt; (*t++ = *s) != '\0'; s++) { + if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3")) + FATAL("format item %.30s... ran format() out of memory", os); + if (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L') + break; /* the ansi panoply */ + if (*s == '*') { + if (a == NULL) + FATAL("not enough args in printf(%s)", os); + x = execute(a); + a = a->nnext; + snprintf(t-1, fmt + fmtsz - (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, "format4"); + + switch (*s) { + case 'f': case 'e': case 'g': case 'E': case 'G': + flag = 'f'; + break; + case 'd': case 'i': + flag = 'd'; + 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' ? 'd' : 'u'; + break; + case 's': + flag = 's'; + break; + case 'c': + flag = 'c'; + break; + default: + WARNING("weird printf conversion %s", fmt); + flag = '?'; + 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, "format5"); + switch (flag) { + case '?': /* unknown, so dump it too */ + snprintf(p, buf + bufsize - p, "%s", fmt); + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6"); + p += strlen(p); + snprintf(p, buf + bufsize - p, "%s", t); + break; + case 'f': snprintf(p, buf + bufsize - p, fmt, getfval(x)); break; + case 'd': snprintf(p, buf + bufsize - p, fmt, (long) getfval(x)); break; + case 'u': snprintf(p, buf + bufsize - p, fmt, (int) getfval(x)); break; + case 's': + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7")) + FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t); + snprintf(p, buf + bufsize - p, fmt, t); + break; + case 'c': + if (isnum(x)) { + if (getfval(x)) + snprintf(p, buf + bufsize - p, fmt, (int) getfval(x)); + else { + *p++ = '\0'; /* explicit null byte */ + *p = '\0'; /* next output will start here */ + } + } else + snprintf(p, buf + bufsize - p, fmt, getsval(x)[0]); + break; + default: + FATAL("can't happen: bad conversion %c in format()", flag); + } + 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; + size_t len; + + x = execute(a[0]); + y = execute(a[1]); + getsval(x); + getsval(y); + n1 = strlen(x->sval); + n2 = strlen(y->sval); + len = n1 + n2 + 1; + s = (char *) malloc(len); + if (s == NULL) + FATAL("out of space concatenating %.15s... and %.15s...", + x->sval, y->sval); + strlcpy(s, x->sval, len); + strlcpy(s+n1, y->sval, len - n1); + tempfree(x); + tempfree(y); + z = gettemp(); + z->sval = s; + z->tval = STR; + 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, tempstat, 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, NN(ap->nval), fs) ); + ap->tval &= ~STR; + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + + n = 0; + if (arg3type == REGEXPR && strlen((char*)((fa*)a[2])->restr) == 0) { + /* split(s, a, //); have to arrange that it looks like empty sep */ + arg3type = 0; + fs = ""; + sep = 0; + } + if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) { /* reg expr */ + fa *pfa; + if (arg3type == REGEXPR) { /* it's ready already */ + pfa = (fa *) a[2]; + } else { + pfa = makedfa(fs, 1); + } + if (nematch(pfa,s)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + do { + n++; + snprintf(num, sizeof num, "%d", n); + temp = *patbeg; + *patbeg = '\0'; + if (is_number(s)) + setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, s, 0.0, STR, (Array *) ap->sval); + *patbeg = temp; + s = patbeg + patlen; + if (*(patbeg+patlen-1) == 0 || *s == 0) { + n++; + snprintf(num, sizeof num, "%d", n); + setsymtab(num, "", 0.0, STR, (Array *) ap->sval); + pfa->initstat = tempstat; + goto spdone; + } + } while (nematch(pfa,s)); + pfa->initstat = tempstat; /* bwk: has to be here to reset */ + /* cf gsub and refldbld */ + } + n++; + snprintf(num, sizeof num, "%d", n); + if (is_number(s)) + setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, s, 0.0, STR, (Array *) ap->sval); + spdone: + pfa = 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'; + snprintf(num, sizeof 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++; + snprintf(num, sizeof num, "%d", n); + buf[0] = *s; + buf[1] = 0; + if (isdigit((uschar)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'; + snprintf(num, sizeof 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) || 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; + Awkfloat tmp; + char *p, *buf; + Node *nextarg; + FILE *fp; + + t = ptoi(a[0]); + x = execute(a[1]); + nextarg = a[1]->nnext; + switch (t) { + case FLENGTH: + if (isarr(x)) + u = ((Array *) x->sval)->nelem; /* GROT. should be function*/ + else + u = strlen(getsval(x)); + 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 FCOMPL: + u = ~((int)getfval(x)); + break; + case FAND: + if (nextarg == 0) { + WARNING("and requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) & ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FFOR: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) | ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FXOR: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) ^ ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FLSHIFT: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) << ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FRSHIFT: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) >> ((int)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: + if (use_srandom) + u = (Awkfloat) (random() % RAND_MAX) / RAND_MAX; + else + u = (Awkfloat)arc4random() / 0xffffffff; + break; + case FSRAND: + if (isrec(x)) /* no argument provided, want arc4random() */ + use_srandom = 0; + else { + use_srandom = 1; + u = getfval(x); + tmp = u; + srandom((unsigned int) u); + u = srand_seed; + srand_seed = tmp; + } + break; + case FTOUPPER: + case FTOLOWER: + buf = tostring(getsval(x)); + if (t == FTOUPPER) { + for (p = buf; *p; p++) + if (islower((uschar) *p)) + *p = toupper((uschar)*p); + } else { + for (p = buf; *p; p++) + if (isupper((uschar) *p)) + *p = tolower((uschar)*p); + } + tempfree(x); + x = gettemp(); + setsval(x, buf); + free(buf); + return x; + case FFLUSH: + if (isrec(x) || strlen(getsval(x)) == 0) { + flush_all(); /* fflush() or fflush("") -> all */ + u = 0; + } else if ((fp = openfile(FFLUSH, getsval(x))) == NULL) + u = EOF; + else + u = fflush(fp); + break; + 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] */ +{ + 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(getpssval(y), fp); + tempfree(y); + if (x->nnext == NULL) + fputs(*ORS, fp); + else + fputs(*OFS, fp); + } + if (a[1] != 0) + fflush(fp); + if (ferror(fp)) + 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; + const char *fname; + int mode; /* '|', 'a', 'w' => LE/LT, GT */ +} *files; + +int nfiles; + +void stdinit(void) /* in case stdin, etc., are not constants */ +{ + nfiles = FOPEN_MAX; + files = calloc(nfiles, sizeof(*files)); + if (files == NULL) + FATAL("can't allocate file memory for %u files", nfiles); + files[0].fp = stdin; + files[0].fname = "/dev/stdin"; + files[0].mode = LT; + files[1].fp = stdout; + files[1].fname = "/dev/stdout"; + files[1].mode = GT; + files[2].fp = stderr; + files[2].fname = "/dev/stderr"; + files[2].mode = GT; +} + +FILE *openfile(int a, const char *us) +{ + const char *s = us; + int i, m; + FILE *fp = 0; + + if (*s == '\0') + FATAL("null file name in print or getline"); + for (i=0; i < nfiles; 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 < nfiles; i++) + if (files[i].fp == 0) + break; + if (i >= nfiles) { + struct files *nf; + int nnf = nfiles + FOPEN_MAX; + nf = reallocarray(files, nnf, sizeof(*nf)); + if (nf == NULL) + FATAL("cannot grow files for %s and %d files", s, nnf); + memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf)); + nfiles = nnf; + files = nf; + } + 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; +} + +const char *filename(FILE *fp) +{ + int i; + + for (i = 0; i < nfiles; 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); + stat = -1; + for (i = 0; i < nfiles; 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); + x = gettemp(); + setfval(x, (Awkfloat) stat); + return(x); +} + +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 flush_all(void) +{ + int i; + + for (i = 0; i < nfiles; i++) + if (files[i].fp) + fflush(files[i].fp); +} + +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; + fa *pfa; + 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 */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + result = False; + if (pmatch(pfa, 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, *q; + char *buf; + fa *pfa; + int mflag, tempstat, 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 */ + t = getsval(x); + if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + if (pmatch(pfa, t)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + 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 == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + } + if (*t == 0) /* at end */ + goto done; + adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub"); + *pb++ = *t++; + 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 = t; + 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 == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + t = patbeg + patlen; + if (patlen == 0 || *t == 0 || *(t-1) == 0) + goto done; + if (pb > buf + bufsz) + FATAL("gsub result1 %.30s too big; can't happen", buf); + mflag = 1; + } + } while (pmatch(pfa,t)); + sptr = t; + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub"); + while ((*pb++ = *sptr++) != 0) + ; + done: if (pb < buf + bufsz) + *pb = '\0'; + else if (*(pb-1) != '\0') + FATAL("gsub result2 %.30s truncated; can't happen", buf); + setsval(x, buf); /* BUG: should be able to avoid copy + free */ + pfa->initstat = tempstat; + } + 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/awk/strlcat.c b/awk/strlcat.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <string.h> +#include <sys/types.h> + +#include "util.h" + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/awk/strlcpy.c b/awk/strlcpy.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <string.h> +#include <sys/types.h> + +#include "util.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + return(s - src - 1); /* count does not include NUL */ +} diff --git a/awk/tran.c b/awk/tran.c @@ -0,0 +1,458 @@ +/* $OpenBSD: tran.c,v 1.15 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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 "ytab.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 *fsloc; /* FS */ +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); + + fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab); + FS = &fsloc->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++) { + snprintf(temp, sizeof 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; + if( p == *envp ) /* no left hand side name in env string */ + 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->nelem--; + } + tp->tab[i] = 0; + } + if (tp->nelem != 0) + WARNING("can't happen: inconsistent element count freeing %s", ap->nval); + free(tp->tab); + free(tp); +} + +void freeelem(Cell *ap, const 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(const char *n, const 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", + (void*)p, NN(p->nval), NN(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", + (void*)p, p->nval, p->sval, p->fval, p->tval) ); + return(p); +} + +int hash(const 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(const 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", (void*)vp, NN(vp->nval), f, vp->tval) ); + return vp->fval = f; +} + +void funnyvar(Cell *vp, const 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, const char *s) /* set string val of a Cell */ +{ + char *t; + int fldno; + + dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", + (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) ); + 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 */ + if (freeable(vp)) + xfree(vp->sval); + vp->tval &= ~NUM; + vp->tval |= STR; + vp->tval &= ~DONTFREE; + dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", + (void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) ); + 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", + (void*)vp, NN(vp->nval), vp->fval, vp->tval) ); + return(vp->fval); +} + +static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */ +{ + int n; + 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 */ + n = asprintf(&vp->sval, "%.30g", vp->fval); + else + n = asprintf(&vp->sval, *fmt, vp->fval); + if (n == -1) + FATAL("out of space in get_str_val"); + vp->tval &= ~DONTFREE; + vp->tval |= STR; + } + dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", + (void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) ); + return(vp->sval); +} + +char *getsval(Cell *vp) /* get string val of a Cell */ +{ + return get_str_val(vp, CONVFMT); +} + +char *getpssval(Cell *vp) /* get string val of a Cell for print */ +{ + return get_str_val(vp, OFMT); +} + + +char *tostring(const char *s) /* make a copy of string s */ +{ + char *p; + + p = strdup(s); + if (p == NULL) + FATAL("out of space in tostring on %s", s); + return p; +} + +char *qstring(const char *is, int delim) /* collect string up to next delim */ +{ + const char *os = is; + int c, n; + uschar *s = (uschar *) is; + uschar *buf, *bp; + + if ((buf = (uschar *) malloc(strlen(is)+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 (char *) buf; +} diff --git a/awk/util.h b/awk/util.h @@ -0,0 +1,10 @@ +#include <sys/stat.h> + +#include <stdint.h> + +uint32_t arc4random(void); + +void *reallocarray(void *, size_t, size_t); + +size_t strlcat(char *, const char *, size_t); +size_t strlcpy(char *, const char *, size_t); diff --git a/sbase/LICENSE b/sbase/LICENSE @@ -0,0 +1,57 @@ +MIT/X Consortium License + +© 2011 Connor Lane Smith <cls@lubutu.com> +© 2011-2015 Dimitris Papastamos <sin@2f30.org> + +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. + +Authors/contributors include: + +© 2011 Kamil Cholewiński <harry666t@gmail.com> +© 2011 Rob Pilling <robpilling@gmail.com> +© 2011 Hiltjo Posthuma <hiltjo@codemadness.org> +© 2011 pancake <pancake@youterm.com> +© 2011 Random832 <random832@fastmail.us> +© 2012 William Haddon <william@haddonthethird.net> +© 2012 Kurt H. Maier <khm@intma.in> +© 2012 Christoph Lohmann <20h@r-36.net> +© 2012 David Galos <galosd83@students.rowan.edu> +© 2012 Robert Ransom <rransom.8774@gmail.com> +© 2013 Jakob Kramer <jakob.kramer@gmx.de> +© 2013 Anselm R Garbe <anselm@garbe.us> +© 2013 Truls Becken <truls.becken@gmail.com> +© 2013 dsp <dsp@2f30.org> +© 2013 Markus Teich <markus.teich@stusta.mhn.de> +© 2013 Jesse Ogle <jesse.p.ogle@gmail.com> +© 2013 Lorenzo Cogotti <miciamail@hotmail.it> +© 2013 Federico G. Benavento <benavento@gmail.com> +© 2013 Roberto E. Vargas Caballero <k0ga@shike2.com> +© 2013 Christian Hesse <mail@eworm.de> +© 2013 Markus Wichmann <nullplan@gmx.net> +© 2014 Silvan Jegen <s.jegen@gmail.com> +© 2014 Laslo Hunhold <dev@frign.de> +© 2014 Daniel Bainton <dpb@driftaway.org> +© 2014 Tuukka Kataja <stuge@xor.fi> +© 2014 Jeffrey Picard <jeff@jeffreypicard.com> +© 2014 Evan Gates <evan.gates@gmail.com> +© 2014 Michael Forney <mforney@mforney.org> +© 2014 Ari Malinen <ari.malinen@gmail.com> +© 2014 Brandon Mulcahy <brandon@jangler.info> +© 2014 Adria Garriga <rhaps0dy@installgentoo.com> +© 2014 Greg Reagle <greg.reagle@umbc.edu> diff --git a/sbase/Makefile b/sbase/Makefile @@ -0,0 +1,205 @@ +include config.mk + +.SUFFIXES: +.SUFFIXES: .o .c + +HDR =\ + arg.h\ + compat.h\ + crypt.h\ + fs.h\ + md5.h\ + queue.h\ + runetypebody.h\ + sha1.h\ + sha256.h\ + sha512.h\ + text.h\ + utf.h\ + util.h + +LIBUTF = libutf.a +LIBUTFSRC =\ + libutf/chartorunearr.c\ + libutf/readrune.c\ + libutf/rune.c\ + libutf/runetype.c\ + libutf/utf.c\ + libutf/writerune.c + +LIBUTIL = libutil.a +LIBUTILSRC =\ + libutil/agetcwd.c\ + libutil/apathmax.c\ + libutil/concat.c\ + libutil/cp.c\ + libutil/crypt.c\ + libutil/ealloc.c\ + libutil/enmasse.c\ + libutil/eprintf.c\ + libutil/eregcomp.c\ + libutil/estrtod.c\ + libutil/estrtol.c\ + libutil/fnck.c\ + libutil/getlines.c\ + libutil/human.c\ + libutil/md5.c\ + libutil/mode.c\ + libutil/putword.c\ + libutil/recurse.c\ + libutil/rm.c\ + libutil/sha1.c\ + libutil/sha256.c\ + libutil/sha512.c\ + libutil/strcasestr.c\ + libutil/strlcat.c\ + libutil/strlcpy.c + +LIB = $(LIBUTF) $(LIBUTIL) + +BIN =\ + basename\ + cal\ + cat\ + chgrp\ + chmod\ + chown\ + chroot\ + cksum\ + cmp\ + cols\ + comm\ + cp\ + cron\ + cut\ + date\ + dirname\ + du\ + echo\ + env\ + expand\ + expr\ + false\ + fold\ + grep\ + head\ + hostname\ + kill\ + link\ + ln\ + logger\ + logname\ + ls\ + md5sum\ + mkdir\ + mkfifo\ + mktemp\ + mv\ + nice\ + nl\ + nohup\ + paste\ + printenv\ + printf\ + pwd\ + readlink\ + renice\ + rm\ + rmdir\ + seq\ + setsid\ + sha1sum\ + sha256sum\ + sha512sum\ + sleep\ + sort\ + split\ + sponge\ + strings\ + sync\ + tail\ + tar\ + tee\ + test\ + touch\ + tr\ + true\ + tty\ + uname\ + unexpand\ + uniq\ + unlink\ + uudecode\ + uuencode\ + wc\ + xargs\ + yes + +LIBUTFOBJ = $(LIBUTFSRC:.c=.o) +LIBUTILOBJ = $(LIBUTILSRC:.c=.o) +OBJ = $(BIN:=.o) $(LIBUTFOBJ) $(LIBUTILOBJ) +SRC = $(BIN:=.c) +MAN = $(BIN:=.1) + +all: $(BIN) + +$(BIN): $(LIB) $(@:=.o) + +$(OBJ): $(HDR) config.mk + +.o: + $(LD) $(LDFLAGS) -o $@ $< $(LIB) + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(LIBUTF): $(LIBUTFOBJ) + $(AR) -r -c $@ $? + $(RANLIB) $@ + +$(LIBUTIL): $(LIBUTILOBJ) + $(AR) -r -c $@ $? + $(RANLIB) $@ + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + cd $(DESTDIR)$(PREFIX)/bin && chmod 755 $(BIN) + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + for m in $(MAN); do sed "s/VERSION/$(VERSION)/g" < "$$m" > $(DESTDIR)$(MANPREFIX)/man1/"$$m"; done + cd $(DESTDIR)$(MANPREFIX)/man1 && chmod 644 $(MAN) + +uninstall: + cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN) + cd $(DESTDIR)$(MANPREFIX)/man1 && rm -f $(MAN) + +dist: clean + mkdir -p sbase-$(VERSION) + cp -r LICENSE Makefile README TODO config.mk $(SRC) $(MAN) libutf libutil $(HDR) sbase-$(VERSION) + tar -cf sbase-$(VERSION).tar sbase-$(VERSION) + gzip sbase-$(VERSION).tar + rm -rf sbase-$(VERSION) + +sbase-box: $(LIB) $(SRC) + mkdir -p build + cp $(HDR) build + for f in $(SRC); do sed "s/^main(/`basename $$f .c`_&/" < $$f > build/$$f; done + echo '#include <libgen.h>' > build/$@.c + echo '#include <stdio.h>' >> build/$@.c + echo '#include <stdlib.h>' >> build/$@.c + echo '#include <string.h>' >> build/$@.c + echo '#include "util.h"' >> build/$@.c + for f in $(SRC); do echo "int `basename $$f .c`_main(int, char **);" >> build/$@.c; done + echo 'int main(int argc, char *argv[]) { char *s = basename(argv[0]); if(!strcmp(s,"sbase-box")) { argc--; argv++; s = basename(argv[0]); } if(0) ;' >> build/$@.c + for f in $(SRC); do echo "else if(!strcmp(s, \"`basename $$f .c`\")) return `basename $$f .c`_main(argc, argv);" >> build/$@.c; done + echo 'else {' >> build/$@.c + for f in $(SRC); do echo "printf(\"`basename $$f .c`\"); putchar(' ');" >> build/$@.c; done + echo "putchar(0xa); }; return 0; }" >> build/$@.c + $(LD) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ build/*.c $(LIB) + rm -r build + +clean: + rm -f $(BIN) $(OBJ) $(LIB) sbase-box sbase-$(VERSION).tar.gz + +.PHONY: + all install uninstall dist sbase-box clean diff --git a/sbase/README b/sbase/README @@ -0,0 +1,120 @@ +sbase - suckless unix tools +=========================== + +sbase is a collection of unix tools that are inherently portable +across UNIX and UNIX-like systems. + +The following tools are implemented ('*' == finished, '#' == UTF-8 support, +'=' == implicit UTF-8 support): + + UTILITY POSIX 2008 COMPLIANT MISSING OPTIONS + ------- -------------------- --------------- +=* basename yes none +=* cal yes none +=* cat yes none += chgrp no -h, -H, -L, -P +=* chmod yes none += chown no -h, -H, -L, -P += chroot non-posix none +=* cksum yes none + * cmp yes none + * cols non-posix none +=* comm yes none += cp no -H, -i, -L +=* cron non-posix none + * cut yes none += date yes none += dirname yes none += du no -H, -L, -x += echo yes none += env yes none +# expand yes none + expr yes none +=* false yes none + fold yes none + * grep yes none + head yes none += hostname non-posix none +=* kill yes none += link yes none += ln yes none +=* logger yes none += logname yes none += ls no -C, -R, -q, -u + md5sum non-posix none += mkdir yes none += mkfifo yes none += mktemp non-posix none += mv no -i += nice yes none += nl no -d, -f, -h, -l, -n, -p, -v, -w += nohup yes none + paste yes none += printenv non-posix none + printf stolen stolen +=* pwd yes none += readlink non-posix none += renice yes none += rm no -i += rmdir no -p += sleep yes none += setsid non-posix none + sort no -m, -o, -d, -f, -i + split yes none += sponge non-posix none + strings no -a, -n, -t += sync non-posix none += tail no -c, -f += tar non-posix none += tee no -i + test yes none += touch no -r +#* tr yes none +=* true yes none += tty yes none += uudecode no -o += uuencode no -m += uname yes none +# unexpand yes none += uniq no -f, -s += unlink yes none + seq non-posix none += sha1sum non-posix none += sha256sum non-posix none += sha512sum non-posix none + wc yes none += xargs no -I, -L, -p, -s, -t, -x += yes yes none + +The complement of sbase is ubase[1] which is Linux-specific and +provides all the non-portable tools. Together they are intended to +form a base system similar to busybox but much smaller and suckless. + +Building +-------- + +You need GNU make to build sbase on OpenBSD. + +To build sbase, simply type make. You may have to fiddle with +config.mk depending on your system. + +You can also build sbase-box, which generates a single binary +containing all the required tools. You can then symlink the +individual tools to sbase-box. + +Ideally you will want to statically link sbase. If you are on Linux +we recommend using musl-libc[2]. + +Portability +----------- + +sbase has been compiled on a variety of different operating systems, +including Linux, *BSD, OSX, Haiku, Solaris, SCO OpenServer and others. + +Various combinations of operating systems and architectures have also +been built. + +You can build sbase with gcc, clang, tcc, nwcc and pcc. + +[1] http://git.suckless.org/ubase/ +[2] http://www.musl-libc.org/ diff --git a/sbase/TODO b/sbase/TODO @@ -0,0 +1,42 @@ +The following list of commands is taken from the toybox roadmap[0] and +has been stripped down accordingly. Commands that belong to ubase[1] +are not listed here as well as commands that fall outside the scope of +sbase such as vi and sh are also not listed here. + +at +awk +bc +diff +ed +file +find +getconf +install +join +make +od +patch +pathchk +sed +stty +tabs +tput + +The following program(s) have been imported from OpenBSD and need +replacing or cleaning up: + +printf + +If you are looking for some work to do on sbase, another option is to +pick a utility of interest and check the POSIX specification to see if +there are any features missing. + +We also need to add support for UTF-8. We have imported libutf from +suckless.org. As a first step we should rework existing tools that use +wide characters to use the libutf library. + +Update manpages to use mdoc(7). Look at grep.1 or at the OpenBSD +manpages for more information. + +[0] http://landley.net/toybox/roadmap.html +[1] http://git.suckless.org/ubase/ diff --git a/sbase/arg.h b/sbase/arg.h @@ -0,0 +1,63 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/sbase/basename.1 b/sbase/basename.1 @@ -0,0 +1,26 @@ +.Dd November 21, 2014 +.Dt BASENAME 1 sbase\-VERSION +.Os +.Sh NAME +.Nm basename +.Nd strip leading path component +.Sh SYNOPSIS +.Nm basename +.Ar string +.Op Ar suffix +.Sh DESCRIPTION +.Nm +prints the +.Ar string +with any leading path components, and the +.Ar suffix , +removed. +.Sh SEE ALSO +.Xr dirname 1 , +.Xr basename 3 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/basename.c b/sbase/basename.c @@ -0,0 +1,41 @@ +/* See LICENSE file for copyright and license details. */ +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +static void usage(void); + +void +usage(void) +{ + eprintf("usage: %s name [suffix]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char *p; + size_t off; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 1) + usage(); + + p = strlen(argv[0]) ? basename(argv[0]) : "."; + if (argc == 2 && *p != '/') { + if (strlen(argv[1]) < strlen(p)) { + off = strlen(p) - strlen(argv[1]); + if (strcmp(&p[off], argv[1]) == 0) + p[off] = '\0'; + } + } + puts(p); + return 0; +} diff --git a/sbase/cal.1 b/sbase/cal.1 @@ -0,0 +1,67 @@ +.Dd January 18, 2015 +.Dt CAL 1 sbase\-VERSION +.Sh NAME +.Nm cal +.Nd show calendar +.Sh SYNOPSIS +.Nm cal +.Op Fl 1 | Fl 3 | Fl y | Fl n Ar nmonths +.Op Fl s | Fl m | Fl f Ar firstday +.Op Fl c Ar columns +.Oo Oo Ar month Oc Ar year Oc +.Sh DESCRIPTION +Print +.Ar nmonths +calendars side by side beginning with +.Ar month +and +.Ar year . +Each row of calendars contains at most +.Ar columns +calendars. The date formatting is obtained using +.Xr localtime 3 . +.Pp +If +.Ar year +is given without +.Ar month , +print the whole year, unless overridden by other options. +.Pp +The Julian calendar is used until Sep 2, 1752. The Gregorian calendar is used +starting the next day on Sep 14, 1752. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl 1 +Output current month. This is the default. +.It Fl 3 +Output previous, current and next month. +.It Fl n Ar nmonths +Output in total +.Ar nmonths +starting from the current month. +.It Fl y Ar year +Output an entire +.Ar year . +.It Fl s +Output Sunday as first day of week. +.It Fl m +Output Monday as first day of week. +.It Fl f Ar firstday +Output +.Ar firstday +(0 is Sunday, 6 is Saturday) as first day of week. +.It Fl c Ar columns +Set number of calendars in a row. The default is 3. +.El +.Sh SEE ALSO +.Xr localtime 3 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl 13ynsmfc +are an extension to that specification. diff --git a/sbase/cal.c b/sbase/cal.c @@ -0,0 +1,216 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "util.h" + +enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; +enum caltype { JULIAN, GREGORIAN }; +enum { TRANS_YEAR = 1752, TRANS_MONTH = SEP, TRANS_DAY = 2 }; + +static int +isleap(int year, enum caltype cal) +{ + if (cal == GREGORIAN) { + if (year % 400 == 0) + return 1; + if (year % 100 == 0) + return 0; + return (year % 4 == 0); + } + else { /* cal == Julian */ + return (year % 4 == 0); + } +} + +static int +monthlength(int year, int month, enum caltype cal) +{ + int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + return (month == FEB && isleap(year,cal)) ? 29 : mdays[month]; +} + +/* From http://www.tondering.dk/claus/cal/chrweek.php#calcdow */ +static int +dayofweek(int year, int month, int dom, enum caltype cal) +{ + int m, y, a; + + month += 1; /* in this formula, 1 <= month <= 12 */ + a = (14 - month) / 12; + y = year - a; + m = month + 12 * a - 2; + + if (cal == GREGORIAN) + return (dom + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7; + else /* cal == Julian */ + return (5 + dom + y + y / 4 + (31 * m) / 12) % 7; +} + +static void +printgrid(int year, int month, int fday, int line) +{ + enum caltype cal; + int trans; /* are we in the transition from Julian to Gregorian? */ + int offset, dom, d = 0; + + if (year < TRANS_YEAR || (year == TRANS_YEAR && month <= TRANS_MONTH)) + cal = JULIAN; + else + cal = GREGORIAN; + trans = (year == TRANS_YEAR && month == TRANS_MONTH); + offset = dayofweek(year, month, 1, cal) - fday; + if (offset < 0) + offset += 7; + if (line == 1) { + for ( ; d < offset; ++d) + printf(" "); + dom = 1; + } else { + dom = 8 - offset + (line - 2) * 7; + if (trans && !(line == 2 && fday == 3)) + dom += 11; + } + for ( ; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) { + printf("%2d ", dom); + if (trans && dom==TRANS_DAY) + dom += 11; + } + for ( ; d < 7; ++d) + printf(" "); +} + +static void +drawcal(int year, int month, int ncols, int nmons, int fday) +{ + char *smon[] = {" January", " February", " March", " April", + " May", " June", " July", " August", + "September", " October", " November", " December" }; + char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", }; + int m, n, col, cur_year, cur_month, line, dow; + + for (m = 0; m < nmons; ) { + n = m; + for (col = 0; m < nmons && col < ncols; ++col, ++m) { + cur_year = year + m / 12; + cur_month = month + m % 12; + if (cur_month > 11) { + cur_month -= 12; + cur_year += 1; + } + printf(" %s %d ", smon[cur_month], cur_year); + printf(" "); + } + printf("\n"); + for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) { + for (dow = fday; dow < (fday + 7); ++dow) + printf("%s ", days[dow % 7]); + printf(" "); + } + printf("\n"); + for (line = 1; line <= 6; ++line) { + for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) { + cur_year = year + m / 12; + cur_month = month + m % 12; + if (cur_month > 11) { + cur_month -= 12; + cur_year += 1; + } + printgrid(cur_year, cur_month, fday, line); + printf(" "); + } + printf("\n"); + } + } +} + +static void +usage(void) +{ + eprintf("usage: %s [-1 | -3 | -y | -n nmonths] " + "[-s | -m | -f firstday] [-c columns] [[month] year]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int year, month, ncols, nmons, fday; + struct tm *ltime; + time_t now; + + now = time(NULL); + ltime = localtime(&now); + year = ltime->tm_year + 1900; + month = ltime->tm_mon + 1; + fday = 0; + + ncols = 3; + nmons = 0; + + ARGBEGIN { + case '1': + nmons = 1; + break; + case '3': + nmons = 3; + month -= 1; + if (month == 0) { + month = 12; + year--; + } + break; + case 'c': + ncols = estrtol(EARGF(usage()), 0); + break; + case 'f': + fday = estrtol(EARGF(usage()), 0); + break; + case 'm': /* Monday */ + fday = 1; + break; + case 'n': + nmons = estrtol(EARGF(usage()), 0); + break; + case 's': /* Sunday */ + fday = 0; + break; + case 'y': + month = 1; + nmons = 12; + break; + default: + usage(); + } ARGEND; + + if (nmons == 0) { + if (argc == 1) { + month = 1; + nmons = 12; + } else { + nmons = 1; + } + } + + switch (argc) { + case 2: + month = estrtol(argv[0], 0); + argv++; + case 1: + year = estrtol(argv[0], 0); + break; + case 0: + break; + default: + usage(); + } + + if (ncols < 0 || month < 1 || month > 12 || nmons < 1 || fday < 0 || fday > 6) { + usage(); + } + + drawcal(year, month - 1, ncols, nmons, fday); + + return 0; +} diff --git a/sbase/cat.1 b/sbase/cat.1 @@ -0,0 +1,29 @@ +.Dd January 16, 2015 +.Dt CAT 1 sbase\-VERSION +.Sh NAME +.Nm cat +.Nd concatenate files +.Sh SYNOPSIS +.Nm cat +.Op Fl u +.Op Ar file ... +.Sh DESCRIPTION +.Nm +reads each +.Ar file +in sequence and writes it to stdout. If no +.Ar file +is given, +.Nm +reads from stdin. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl u +Unbuffered output +.El +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/cat.c b/sbase/cat.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "text.h" +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [-u] [file...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + int ret = 0; + + ARGBEGIN { + case 'u': + setbuf(stdout, NULL); + break; + default: + usage(); + } ARGEND; + + if (argc == 0) { + concat(stdin, "<stdin>", stdout, "<stdout>"); + } else { + for (; argc; argc--, argv++) { + if (argv[0][0] == '-' && !argv[0][1]) + argv[0] = "/dev/fd/0"; + if (!(fp = fopen(argv[0], "r"))) { + weprintf("fopen %s:", argv[0]); + ret = 1; + continue; + } + concat(fp, argv[0], stdout, "<stdout>"); + fclose(fp); + } + } + return ret; +} diff --git a/sbase/chgrp.1 b/sbase/chgrp.1 @@ -0,0 +1,22 @@ +.TH CHGRP 1 sbase\-VERSION +.SH NAME +chgrp \- change the file group ownership +.SH SYNOPSIS +.B chgrp +.RB [ \-R ] +.I groupname +.I file... +.SH DESCRIPTION +.B chgrp +sets the group id of the files specified by +.IR file +to the gid of the group named +.IR group. +If the +.IR R +flag is specified, this process is recursively applied to +everything in +.IR file. + +.SH SEE ALSO +.IR chown (1) chown (2) chmod (1) chmod (2) getgrnam (3) diff --git a/sbase/chgrp.c b/sbase/chgrp.c @@ -0,0 +1,70 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "util.h" + +static int gid; +static int status; +static int rflag; +static struct stat st; + +static void +usage(void) +{ + eprintf("usage: chgrp [-R] groupname file...\n"); +} + +static void +chgrp(const char *path) +{ + if (chown(path, st.st_uid, gid) < 0) { + weprintf("chown %s:", path); + status = 1; + } + if (rflag) + recurse(path, chgrp); +} + +int +main(int argc, char *argv[]) +{ + struct group *gr; + + ARGBEGIN { + case 'R': + rflag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + errno = 0; + gr = getgrnam(argv[0]); + if (!gr) { + if (errno) + eprintf("getgrnam %s:", argv[0]); + else + eprintf("getgrnam %s: no such group\n", argv[0]); + } + gid = gr->gr_gid; + + while (*++argv) { + if (stat(*argv, &st) < 0) { + weprintf("stat %s:", *argv); + status = 1; + continue; + } + chgrp(*argv); + } + return status; +} diff --git a/sbase/chmod.1 b/sbase/chmod.1 @@ -0,0 +1,66 @@ +.Dd January 17, 2015 +.Dt CHMOD 1 sbase\-VERSION +.Sh NAME +.Nm chmod +.Nd change file mode +.Sh SYNOPSIS +.Nm chmod +.Op Fl R +.Ar mode +.Op Ar file ... +.Sh DESCRIPTION +.Nm +changes the file mode of the given files. +.Pp +If +.Ar mode +is +.Em octal +"[sog]e" +.Bl -tag -width Ds +.It s +.Xr sticky 1 => s += 1 +.Pp +.Xr setgid 2 => s += 2 +.Pp +.Xr setuid 4 => s += 4 +.It o|g|e +owner | group | everyone +.Pp +.Xr execute 1 => o|g|e += 1 +.Pp +.Xr write 2 => o|g|e += 2 +.Pp +.Xr read 4 => o|g|e += 4 +.El +.Pp +Leading zeroes may be omitted. +.Pp +If +.Ar mode +is +.Em symbolic +"[ugoa]*[+-=][rwxst]*" +.Bl -tag -width Ds +.It u|g|o|a +owner | group | other (non-group) | everyone +.It +|-|= +add | remove | set +.It r|w|x|s|t +read | write | execute | setuid and setgid | sticky +.El +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl R +Change modes recursively +.El +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The +.Op Fl R +flag is an extension to that specification. diff --git a/sbase/chmod.c b/sbase/chmod.c @@ -0,0 +1,74 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "util.h" + +static int rflag = 0; +static char *modestr = ""; +static mode_t mask = 0; +static int ret = 0; + +void +chmodr(const char *path) +{ + struct stat st; + mode_t m; + + if (stat(path, &st) < 0) { + weprintf("stat %s:", path); + ret = 1; + return; + } + + m = parsemode(modestr, st.st_mode, mask); + if (chmod(path, m) < 0) { + weprintf("chmod %s:", path); + ret = 1; + } + if (rflag) + recurse(path, chmodr); +} + +static void +usage(void) +{ + eprintf("usage: %s [-R] mode [file ...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + size_t i; + + argv0 = argv[0]; + for (i = 1; i < argc && argv[i][0] == '-'; i++) { + switch (argv[i][1]) { + case 'R': + rflag = 1; + break; + case 'r': case 'w': case 'x': case 's': case 't': + /* + * -[rwxst] are valid modes so do not interpret + * them as options - in any case we are done if + * we hit this case + */ + goto done; + default: + usage(); + } + } +done: + mask = getumask(); + modestr = argv[i]; + + if (argc - i - 1 < 1) + usage(); + + for (++i; i < argc; i++) + chmodr(argv[i]); + + return ret; +} diff --git a/sbase/chown.1 b/sbase/chown.1 @@ -0,0 +1,20 @@ +.TH CHOWN 1 sbase\-VERSION +.SH NAME +chown \- change file ownership +.SH SYNOPSIS +.B chown +.RB [ \-Rr ] +.RI [ owner ][: group ] +.RI [ file ...] +.SH DESCRIPTION +.B chown +changes the user or group ownership for the given files. +.SH OPTIONS +.TP +.B \-R +equivalent to -r. +.TP +.B \-r +change directory ownership recursively. +.SH SEE ALSO +.IR chown (2) diff --git a/sbase/chown.c b/sbase/chown.c @@ -0,0 +1,90 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <grp.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" + +static void chownpwgr(const char *); + +static int rflag = 0; +static uid_t uid = -1; +static gid_t gid = -1; +static int ret = 0; + +static void +usage(void) +{ + eprintf("usage: %s [-Rr] [owner][:[group]] file...\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char *owner, *group, *end; + struct passwd *pw; + struct group *gr; + + ARGBEGIN { + case 'R': + case 'r': + rflag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc == 0) + usage(); + + owner = argv[0]; + argv++; + argc--; + if ((group = strchr(owner, ':'))) + *group++ = '\0'; + + if (owner && *owner) { + errno = 0; + pw = getpwnam(owner); + if (pw) { + uid = pw->pw_uid; + } else { + if (errno != 0) + eprintf("getpwnam %s:", owner); + uid = strtoul(owner, &end, 10); + if (*end != '\0') + eprintf("getpwnam %s: no such user\n", owner); + } + } + if (group && *group) { + errno = 0; + gr = getgrnam(group); + if (gr) { + gid = gr->gr_gid; + } else { + if (errno != 0) + eprintf("getgrnam %s:", group); + gid = strtoul(group, &end, 10); + if (*end != '\0') + eprintf("getgrnam %s: no such group\n", group); + } + } + for (; argc > 0; argc--, argv++) + chownpwgr(argv[0]); + + return ret; +} + +void +chownpwgr(const char *path) +{ + if (chown(path, uid, gid) < 0) { + weprintf("chown %s:", path); + ret = 1; + } + if (rflag) + recurse(path, chownpwgr); +} diff --git a/sbase/chroot.1 b/sbase/chroot.1 @@ -0,0 +1,26 @@ +.TH CHROOT 1 sbase\-VERSION +.SH NAME +chroot \- invoke a command with a different root directory +.SH SYNOPSIS +.B chroot +.IR dir +.RI [ command +.RI [ arg ...]] + +.SH DESCRIPTION +.B chroot +runs +.IR command +after changing the root directory to +.IR dir +with the +.B chroot +system call, and changing the working directory to the new root. + +If +.IR command +is not specified, an interactive shell is started in the new root. + +.SH SEE ALSO +.IR chroot (2) +.IR chdir (2) diff --git a/sbase/chroot.c b/sbase/chroot.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: chroot dir [command [arg...]]\n"); +} + +int +main(int argc, char *argv[]) +{ + char *shell[] = { "/bin/sh", "-i", NULL }, *aux, *p; + int savederrno; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 1) + usage(); + + if ((aux = getenv("SHELL"))) + shell[0] = aux; + + if (chroot(argv[0]) < 0) + eprintf("chroot %s:", argv[0]); + + if (chdir("/") < 0) + eprintf("chdir:"); + + if (argc == 1) { + p = *shell; + execvp(*shell, shell); + } else { + p = argv[1]; + execvp(argv[1], argv+1); + } + + savederrno = errno; + weprintf("execvp %s:", p); + _exit(savederrno == ENOENT ? 127 : 126); + /* unreachable */ + return 0; +} diff --git a/sbase/cksum.1 b/sbase/cksum.1 @@ -0,0 +1,27 @@ +.Dd January 17, 2015 +.Dt CKSUM 1 sbase\-VERSION +.Sh NAME +.Nm cksum +.Nd compute file checksum +.Sh SYNOPSIS +.Nm cksum +.Op Ar file ... +.Sh DESCRIPTION +.Nm +calculates a cyclic redundancy check (CRC) of +.Ar file +according to +.St -iso8802-3 +and writes it, the file size in bytes and path to stdout. +.Pp +If no +.Ar file +is given, +.Nm +reads from stdin. +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/cksum.c b/sbase/cksum.c @@ -0,0 +1,118 @@ +/* See LICENSE file for copyright and license details. */ +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +static const unsigned long crctab[] = { 0x00000000, +0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, +0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, +0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, +0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, +0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, +0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, +0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, +0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, +0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, +0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, +0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, +0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, +0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, +0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, +0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, +0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, +0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, +0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, +0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, +0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, +0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, +0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, +0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, +0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, +0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, +0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, +0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, +0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, +0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, +0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, +0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, +0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, +0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, +0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, +0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, +0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, +0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, +0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, +0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, +0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, +0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, +0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, +0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, +0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, +0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, +0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, +0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, +0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, +0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, +0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, +0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static void +cksum(FILE *fp, const char *s) +{ + unsigned char buf[BUFSIZ]; + uint32_t ck = 0; + size_t len = 0; + size_t i, n; + + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + for (i = 0; i < n; i++) + ck = (ck << 8) ^ crctab[(ck >> 24) ^ buf[i]]; + len += n; + } + if (ferror(fp)) + eprintf("%s: read error:", s ? s : "<stdin>"); + + for (i = len; i > 0; i >>= 8) + ck = (ck << 8) ^ crctab[(ck >> 24) ^ (i & 0xFF)]; + + printf("%"PRIu32" %lu", ~ck, (unsigned long)len); + if (s) + printf(" %s", s); + putchar('\n'); +} + +static void +usage(void) +{ + eprintf("usage: %s [files ...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc == 0) + cksum(stdin, NULL); + else { + for (; argc > 0; argc--, argv++) { + if (!(fp = fopen(argv[0], "r"))) { + weprintf("fopen %s:", argv[0]); + continue; + } + cksum(fp, argv[0]); + fclose(fp); + } + } + return 0; +} diff --git a/sbase/cmp.1 b/sbase/cmp.1 @@ -0,0 +1,42 @@ +.Dd November 21, 2014 +.Dt CMP 1 sbase\-VERSION +.Os +.Sh NAME +.Nm cmp +.Nd compare two files +.Sh SYNOPSIS +.Nm cmp +.Op Fl l | Fl s +.Ar file1 file2 +.Sh DESCRIPTION +.Nm +compares two files byte by byte. If the files differ, +.Nm +prints the byte and +line number at which the difference occurred. +.Pp +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl l +Print the byte number, and the differing bytes (in octal), for each difference. +.It Fl s +Print nothing, only returns status. +.El +.Sh EXIT STATUS +.Bl -tag -width Ds +.It 0 +The files are identical. +.It 1 +The files are different. +.It > 1 +An error occured. +.El +.Sh SEE ALSO +.Xr comm 1 , +.Xr diff 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/cmp.c b/sbase/cmp.c @@ -0,0 +1,86 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "util.h" + +enum { Same = 0, Diff = 1, Error = 2 }; + +static void +usage(void) +{ + enprintf(Error, "usage: %s [-l | -s] file1 file2\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int lflag = 0; + int sflag = 0; + int same = 1; + int b[2], i; + long line = 1, n = 1; + FILE *fp[2]; + + ARGBEGIN { + case 'l': + lflag = 1; + break; + case 's': + sflag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 2 || (lflag && sflag)) + usage(); + + if (argv[0][0] == '-' && !argv[0][1]) + argv[0] = "/dev/fd/0"; + fp[0] = fopen(argv[0], "r"); + if (!fp[0]) { + if (!sflag) + weprintf("fopen %s:", argv[0]); + exit(Error); + } + + if (argv[1][0] == '-' && !argv[1][1]) + argv[1] = "/dev/fd/0"; + fp[1] = fopen(argv[1], "r"); + if (!fp[1]) { + if (!sflag) + weprintf("fopen %s:", argv[1]); + exit(Error); + } + + for (n = 1; ; n++) { + b[0] = getc(fp[0]); + b[1] = getc(fp[1]); + if (b[0] == EOF && b[1] == EOF) + break; + if (b[0] == '\n' && b[1] == '\n') + line++; + if (b[0] == b[1]) + continue; + for (i = 0; i < 2; i++) { + if (b[i] == EOF) { + if (!sflag) + fprintf(stderr, "cmp: EOF on %s\n", + !argv[i] ? "<stdin>" : argv[i]); + exit(Diff); + } + } + if (!lflag) { + if (!sflag) + printf("%s %s differ: char %ld, line %ld\n", + argv[0], !argv[1] ? "<stdin>" : argv[1], n, line); + exit(Diff); + } else { + printf("%ld %o %o\n", n, b[0], b[1]); + same = 0; + } + } + return same ? Same : Diff; +} diff --git a/sbase/cols.1 b/sbase/cols.1 @@ -0,0 +1,41 @@ +.Dd December 8, 2014 +.Dt COLS 1 sbase\-VERSION +.Os +.Sh NAME +.Nm cols +.Nd columnize output +.Sh SYNOPSIS +.Nm cols +.Op Fl c Ar chars +.Op Ar file ... +.Sh DESCRIPTION +.Nm +reads each file in sequence and writes them to stdout, +in as many vertical columns as will fit in +.Ar chars +character columns. +If no file is given, cols reads from stdin. +.Sh OPTIONS +.Bl -tag -width xxxxxxxx +.It Fl c Ar chars +Specifies the maximum number of character columns to use +(unless the input contains lines longer than +.Ar chars +characters). By default cols tries to figure out the width +of the output device, if that fails it defaults to 65 +chars. +.El +.Sh BUGS +This implementation of +.Nm +assumes that each UTF-8 code point occupies one character cell, +and thus mishandles TAB characters (among others). +.Pp +.Nm +currently mangles files which contain embedded NULs. +.Sh HISTORY +.Nm +is similar to the mc(1) command on Plan 9. It was renamed to +.Nm +to avoid the name collision with the popular file manager +Midnight Commander. diff --git a/sbase/cols.c b/sbase/cols.c @@ -0,0 +1,94 @@ +/* See LICENSE file for copyright and license details. */ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "text.h" +#include "utf.h" +#include "util.h" + +static long chars = 65; +static int cflag; +static struct linebuf b = EMPTY_LINEBUF; + +static long n_columns; +static long n_rows; + +static void +usage(void) +{ + eprintf("usage: %s [-c chars] [file ...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + long i, l, col; + size_t len, bytes; + int maxlen = 0; + struct winsize w; + FILE *fp; + + ARGBEGIN { + case 'c': + cflag = 1; + chars = estrtol(EARGF(usage()), 0); + if (chars < 3) + eprintf("%d: too few character columns"); + break; + default: + usage(); + } ARGEND; + + if (cflag == 0) { + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + if (w.ws_col != 0) + chars = w.ws_col; + } + + if (argc == 0) { + getlines(stdin, &b); + } else for (; argc > 0; argc--, argv++) { + if (!(fp = fopen(argv[0], "r"))) + eprintf("fopen %s:", argv[0]); + getlines(fp, &b); + fclose(fp); + } + + for (l = 0; l < b.nlines; ++l) { + len = utflen(b.lines[l]); + bytes = strlen(b.lines[l]); + if (len > 0 && b.lines[l][bytes-1] == '\n') { + b.lines[l][bytes-1] = '\0'; + --len; + } + if (len > maxlen) + maxlen = len; + if (maxlen > (chars - 1) / 2) + break; + } + + n_columns = (chars + 1) / (maxlen + 1); + if (n_columns <= 1) { + for (l = 0; l < b.nlines; ++l) { + fputs(b.lines[l], stdout); + } + return 0; + } + + n_rows = (b.nlines + (n_columns - 1)) / n_columns; + for (i = 0; i < n_rows; ++i) { + for (l = i, col = 1; l < b.nlines; l += n_rows, ++col) { + len = utflen(b.lines[l]); + fputs(b.lines[l], stdout); + if (col < n_columns) + printf("%*s", maxlen + 1 - (int)len, ""); + } + fputs("\n", stdout); + } + + return 0; +} diff --git a/sbase/comm.1 b/sbase/comm.1 @@ -0,0 +1,43 @@ +.Dd January 18, 2015 +.Dt COMM 1 sbase\-VERSION +.Sh NAME +.Nm comm +.Nd select or reject lines common to two files +.Sh SYNOPSIS +.Nm comm +.Op Fl 123 +.Ar file1 +.Ar file2 +.Sh DESCRIPTION +.Nm +reads +.Ar file1 +and +.Ar file2, +which should both be sorted lexically, and writes three text columns +to stdout: +.Bl -tag -width Ds +.It 1 +Lines only in +.Ar file1 . +.It 2 +Lines only in +.Ar file2 . +.It 3 +Common lines. +.El +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl 1 | Fl 2 | Fl 3 +Suppress column 1 | 2 | 3 +.El +.Sh SEE ALSO +.Xr cmp 1 , +.Xr sort 1 , +.Xr uniq 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/comm.c b/sbase/comm.c @@ -0,0 +1,107 @@ +/* See LICENSE file for copyright and license details. */ +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +#define CLAMP(x, l, h) MIN(h, MAX(l, x)) + +static int show = 0x07; + +static void +printline(int pos, char *line) +{ + int i; + + if (!(show & (0x1 << pos))) + return; + + for (i = 0; i < pos; i++) { + if (show & (0x1 << i)) + putchar('\t'); + } + printf("%s", line); +} + +static char * +nextline(char *buf, int n, FILE *f, char *name) +{ + buf = fgets(buf, n, f); + if (!buf && !feof(f)) + eprintf("%s: read error:", name); + if (buf && !strchr(buf, '\n')) + eprintf("%s: line too long\n", name); + return buf; +} + +static void +finish(int pos, FILE *f, char *name) +{ + char buf[LINE_MAX + 1]; + + while (nextline(buf, sizeof(buf), f, name)) + printline(pos, buf); + exit(1); +} + +static void +usage(void) +{ + eprintf("usage: %s [-123] file1 file2\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int i, diff = 0; + FILE *fp[2]; + char lines[2][LINE_MAX + 1]; + + ARGBEGIN { + case '1': + case '2': + case '3': + show &= 0x07 ^ (1 << (ARGC() - '1')); + break; + default: + usage(); + } ARGEND; + + if (argc != 2) + usage(); + + for (i = 0; i < LEN(fp); i++) { + if (argv[i][0] == '-' && !argv[i][1]) + argv[i] = "/dev/fd/0"; + if (!(fp[i] = fopen(argv[i], "r"))) + eprintf("fopen %s:", argv[i]); + } + + for (;;) { + if (diff <= 0) { + lines[0][0] = '\0'; + if (!nextline(lines[0], sizeof(lines[0]), + fp[0], argv[0])) { + if (lines[1][0] != '\0') + printline(1, lines[1]); + finish(1, fp[1], argv[1]); + } + } + if (diff >= 0) { + lines[1][0] = '\0'; + if (!nextline(lines[1], sizeof(lines[1]), + fp[1], argv[1])) { + if (lines[0][0] != '\0') + printline(0, lines[0]); + finish(0, fp[0], argv[0]); + } + } + diff = strcmp(lines[0], lines[1]); + diff = CLAMP(diff, -1, 1); + printline((2-diff) % 3, lines[MAX(0, diff)]); + } + + return 0; +} diff --git a/sbase/compat.h b/sbase/compat.h @@ -0,0 +1,6 @@ +/* See LICENSE file for copyright and license details. */ +#include <limits.h> + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif diff --git a/sbase/config.mk b/sbase/config.mk @@ -0,0 +1,15 @@ +# sbase version +VERSION = 0.0 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +CC = cc +LD = $(CC) +AR = ar +RANLIB = ranlib + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS = -std=c99 -Wall -pedantic +LDFLAGS = -s diff --git a/sbase/cp.1 b/sbase/cp.1 @@ -0,0 +1,42 @@ +.TH CP 1 sbase\-VERSION +.SH NAME +cp \- copy files and directories +.SH SYNOPSIS +.B cp +.RB [ \-Rr ] +.I file +.RI [ name ] +.P +.B cp +.RB [ \-aPpRrv ] +.RI [ file ...] +.RI [ directory ] +.SH DESCRIPTION +.B cp +copies a given file, naming it the given name. If multiple files are listed +they will be copied into the given directory. +.SH OPTIONS +.TP +.B \-a +preserve mode, timestamp, links and permissions. +Implies \-d, \-p, \-r. +.TP +.B \-P +don't dereference symbolic links. +.TP +.B \-p +preserve mode, timestamp and permissions. +.TP +.B \-f +if an existing destination file cannot be opened, remove it and try again. +.TP +.B \-R +equivalent to -r. +.TP +.B \-r +copies directories recursively. If this flag is not specified, directories are +not copied. +.TP +.B \-v +print names of source and destination per file to stdout. In the format: +"source \-> destination". diff --git a/sbase/cp.c b/sbase/cp.c @@ -0,0 +1,51 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdlib.h> +#include <sys/stat.h> + +#include "fs.h" +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [-adfpRrv] source... dest\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct stat st; + + ARGBEGIN { + case 'a': + /* implies -dpr */ + cp_aflag = cp_Pflag = cp_pflag = cp_rflag = 1; + break; + case 'P': + cp_Pflag = 1; + break; + case 'p': + cp_pflag = 1; + break; + case 'f': + cp_fflag = 1; + break; + case 'R': + case 'r': + cp_rflag = 1; + break; + case 'v': + cp_vflag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + if (argc > 2 && !(stat(argv[argc-1], &st) == 0 && S_ISDIR(st.st_mode))) + eprintf("%s: not a directory\n", argv[argc-1]); + enmasse(argc, argv, cp); + return cp_status; +} diff --git a/sbase/cron.1 b/sbase/cron.1 @@ -0,0 +1,24 @@ +.Dd December 16, 2014 +.Dt CRON 1 sbase\-VERSION +.Os +.Sh NAME +.Nm cron +.Nd clock daemon +.Sh SYNOPSIS +.Nm cron +.Op Fl f Ar file +.Op Fl n +.Sh DESCRIPTION +.Nm +schedules commands to be run at specified dates and times. +.Pp +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl f Ar file +Use the specified +.Ar file +instead of the default +.Ar /etc/crontab . +.It Fl n +Do not daemonize. +.El diff --git a/sbase/cron.c b/sbase/cron.c @@ -0,0 +1,494 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "queue.h" +#include "util.h" + +struct field { + /* [low, high] */ + long low; + long high; + /* for every `div' units */ + long div; +}; + +struct ctabentry { + struct field min; + struct field hour; + struct field mday; + struct field mon; + struct field wday; + char *cmd; + TAILQ_ENTRY(ctabentry) entry; +}; + +struct jobentry { + char *cmd; + pid_t pid; + TAILQ_ENTRY(jobentry) entry; +}; + +char *argv0; +static sig_atomic_t chldreap; +static sig_atomic_t reload; +static sig_atomic_t quit; +static TAILQ_HEAD(, ctabentry) ctabhead = TAILQ_HEAD_INITIALIZER(ctabhead); +static TAILQ_HEAD(, jobentry) jobhead = TAILQ_HEAD_INITIALIZER(jobhead); +static char *config = "/etc/crontab"; +static char *pidfile = "/var/run/crond.pid"; +static int nflag; + +static void +loginfo(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_INFO, fmt, ap); + else + vfprintf(stdout, fmt, ap); + fflush(stdout); + va_end(ap); +} + +static void +logwarn(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_WARNING, fmt, ap); + else + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +logerr(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_ERR, fmt, ap); + else + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +runjob(char *cmd) +{ + struct jobentry *je; + time_t t; + pid_t pid; + + t = time(NULL); + + /* If command is already running, skip it */ + TAILQ_FOREACH(je, &jobhead, entry) { + if (strcmp(je->cmd, cmd) == 0) { + loginfo("already running %s pid: %d at %s", + je->cmd, je->pid, ctime(&t)); + return; + } + } + + pid = fork(); + if (pid < 0) { + logerr("error: failed to fork job: %s time: %s", + cmd, ctime(&t)); + return; + } else if (pid == 0) { + setsid(); + loginfo("run: %s pid: %d at %s", + cmd, getpid(), ctime(&t)); + execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL); + logerr("error: failed to execute job: %s time: %s", + cmd, ctime(&t)); + _exit(1); + } else { + je = emalloc(sizeof(*je)); + je->cmd = estrdup(cmd); + je->pid = pid; + TAILQ_INSERT_TAIL(&jobhead, je, entry); + } +} + +static void +waitjob(void) +{ + struct jobentry *je, *tmp; + int status; + time_t t; + pid_t pid; + + t = time(NULL); + + while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { + je = NULL; + TAILQ_FOREACH(tmp, &jobhead, entry) { + if (tmp->pid == pid) { + je = tmp; + break; + } + } + if (je) { + TAILQ_REMOVE(&jobhead, je, entry); + free(je->cmd); + free(je); + } + if (WIFEXITED(status) == 1) + loginfo("complete: pid: %d returned: %d time: %s", + pid, WEXITSTATUS(status), ctime(&t)); + else if (WIFSIGNALED(status) == 1) + loginfo("complete: pid: %d terminated by signal: %s time: %s", + pid, strsignal(WTERMSIG(status)), ctime(&t)); + else if (WIFSTOPPED(status) == 1) + loginfo("complete: pid: %d stopped by signal: %s time: %s", + pid, strsignal(WSTOPSIG(status)), ctime(&t)); + } +} + +static int +isleap(int year) +{ + if (year % 400 == 0) + return 1; + if (year % 100 == 0) + return 0; + return (year % 4 == 0); +} + +static int +daysinmon(int mon, int year) +{ + int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if (year < 1900) + year += 1900; + if (isleap(year)) + days[1] = 29; + return days[mon]; +} + +static int +matchentry(struct ctabentry *cte, struct tm *tm) +{ + struct { + struct field *f; + int tm; + int len; + } matchtbl[] = { + { .f = &cte->min, .tm = tm->tm_min, .len = 60 }, + { .f = &cte->hour, .tm = tm->tm_hour, .len = 24 }, + { .f = &cte->mday, .tm = tm->tm_mday, .len = daysinmon(tm->tm_mon, tm->tm_year) }, + { .f = &cte->mon, .tm = tm->tm_mon, .len = 12 }, + { .f = &cte->wday, .tm = tm->tm_wday, .len = 7 }, + }; + size_t i; + + for (i = 0; i < LEN(matchtbl); i++) { + if (matchtbl[i].f->high == -1) { + if (matchtbl[i].f->low == -1) { + continue; + } else if (matchtbl[i].f->div > 0) { + if (matchtbl[i].tm > 0) { + if (matchtbl[i].tm % matchtbl[i].f->div == 0) + continue; + } else { + if (matchtbl[i].len % matchtbl[i].f->div == 0) + continue; + } + } else if (matchtbl[i].f->low == matchtbl[i].tm) { + continue; + } + } else if (matchtbl[i].f->low <= matchtbl[i].tm && + matchtbl[i].f->high >= matchtbl[i].tm) { + continue; + } + break; + } + if (i != LEN(matchtbl)) + return 0; + return 1; +} + +static int +parsefield(const char *field, long low, long high, struct field *f) +{ + long min, max, div; + char *e1, *e2; + + if (strcmp(field, "*") == 0) { + f->low = -1; + f->high = -1; + return 0; + } + + div = -1; + max = -1; + min = strtol(field, &e1, 10); + + switch (e1[0]) { + case '-': + e1++; + errno = 0; + max = strtol(e1, &e2, 10); + if (e2[0] != '\0' || errno != 0) + return -1; + break; + case '*': + e1++; + if (e1[0] != '/') + return -1; + e1++; + errno = 0; + div = strtol(e1, &e2, 10); + if (e2[0] != '\0' || errno != 0) + return -1; + break; + case '\0': + break; + default: + return -1; + } + + if (min < low || min > high) + return -1; + if (max != -1) + if (max < low || max > high) + return -1; + if (div != -1) + if (div < low || div > high) + return -1; + + f->low = min; + f->high = max; + f->div = div; + return 0; +} + +static void +unloadentries(void) +{ + struct ctabentry *cte, *tmp; + + for (cte = TAILQ_FIRST(&ctabhead); cte; cte = tmp) { + tmp = TAILQ_NEXT(cte, entry); + TAILQ_REMOVE(&ctabhead, cte, entry); + free(cte->cmd); + free(cte); + } +} + +static int +loadentries(void) +{ + struct ctabentry *cte; + FILE *fp; + char *line = NULL, *p, *col; + int r = 0, y; + size_t size = 0; + ssize_t len; + + if ((fp = fopen(config, "r")) == NULL) { + logerr("error: can't open %s: %s\n", config, strerror(errno)); + return -1; + } + + for (y = 0; (len = getline(&line, &size, fp)) != -1; y++) { + p = line; + if (line[0] == '#' || line[0] == '\n' || line[0] == '\0') + continue; + + cte = emalloc(sizeof(*cte)); + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 59, &cte->min) < 0) { + logerr("error: failed to parse `min' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 23, &cte->hour) < 0) { + logerr("error: failed to parse `hour' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 1, 31, &cte->mday) < 0) { + logerr("error: failed to parse `mday' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 1, 12, &cte->mon) < 0) { + logerr("error: failed to parse `mon' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 6, &cte->wday) < 0) { + logerr("error: failed to parse `wday' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\n"); + if (!col) { + logerr("error: missing `cmd' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + cte->cmd = estrdup(col); + + TAILQ_INSERT_TAIL(&ctabhead, cte, entry); + } + + if (r < 0) + unloadentries(); + + free(line); + fclose(fp); + + return r; +} + +static void +reloadentries(void) +{ + unloadentries(); + if (loadentries() < 0) + logwarn("warning: discarding old crontab entries\n"); +} + +static void +sighandler(int sig) +{ + switch (sig) { + case SIGCHLD: + chldreap = 1; + break; + case SIGHUP: + reload = 1; + break; + case SIGTERM: + quit = 1; + break; + } +} + +static void +usage(void) +{ + eprintf("usage: %s [-f file] [-n]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + struct ctabentry *cte; + time_t t; + struct tm *tm; + struct sigaction sa; + + ARGBEGIN { + case 'n': + nflag = 1; + break; + case 'f': + config = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + if (argc > 0) + usage(); + + if (nflag == 0) { + openlog(argv[0], LOG_CONS | LOG_PID, LOG_CRON); + if (daemon(1, 0) < 0) { + logerr("error: failed to daemonize %s\n", strerror(errno)); + return 1; + } + if ((fp = fopen(pidfile, "w"))) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + + sa.sa_handler = sighandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + loadentries(); + + while (1) { + t = time(NULL); + sleep(60 - t % 60); + + if (quit == 1) { + if (nflag == 0) + unlink(pidfile); + unloadentries(); + /* Don't wait or kill forked processes, just exit */ + break; + } + + if (reload == 1 || chldreap == 1) { + if (reload == 1) { + reloadentries(); + reload = 0; + } + if (chldreap == 1) { + waitjob(); + chldreap = 0; + } + continue; + } + + TAILQ_FOREACH(cte, &ctabhead, entry) { + t = time(NULL); + tm = localtime(&t); + if (matchentry(cte, tm) == 1) + runjob(cte->cmd); + } + } + + if (nflag == 0) + closelog(); + + return 0; +} diff --git a/sbase/crypt.h b/sbase/crypt.h @@ -0,0 +1,12 @@ +/* See LICENSE file for copyright and license details. */ +struct crypt_ops { + void (*init)(void *); + void (*update)(void *, const void *, unsigned long); + void (*sum)(void *, uint8_t *); + void *s; +}; + +int cryptcheck(char *, int, char **, struct crypt_ops *, uint8_t *, size_t); +int cryptmain(int, char **, struct crypt_ops *, uint8_t *, size_t); +int cryptsum(struct crypt_ops *, FILE *, const char *, uint8_t *); +void mdprint(const uint8_t *, const char *, size_t); diff --git a/sbase/cut.1 b/sbase/cut.1 @@ -0,0 +1,70 @@ +.Dd January 18, 2015 +.Dt CUT 1 sbase\-VERSION +.Sh NAME +.Nm cut +.Nd extract columns of data +.Sh SYNOPSIS +.Nm cut +.Fl b Ar list +.Op Fl n +.Op Ar file ... +.Nm cut +.Fl c Ar list +.Op Ar file ... +.Nm cut +.Fl f Ar list +.Op Fl d Ar delim +.Op Fl s +.Op Ar file ... +.Sh DESCRIPTION +.Nm +out bytes, characters or delimited fields from each line of +.Ar file +and write to stdout. +.Pp +If no +.Ar file +is given or +.Ar file +is '-', +.Nm +reads from stdin. +.Pp +.Ar list +is a comma or space separated list of numbers and ranges starting +from 1. Ranges have the form 'N-M'. If N or M is missing, +beginning or end of line is assumed. Numbers and ranges +may be repeated, overlapping and in any order. +.Pp +Selected input is written in the same order it is read +and is written exactly once. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl b Ar list | Fl c Ar list +.Ar list +specifies byte | character positions. +.It Fl n +Do not split multibyte characters. A character is written when its +last byte is selected. +.It Fl f Ar list +.Ar list +specifies field numbers. Lines not containing field +delimiters are passed through, unless +.Fl s +is specified. +.It Fl d Ar delim +Use first byte of +.Ar delim +as field delimiter. Default is \et. +.It Fl s +Suppress lines not containing field delimiters. +.El +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The possibility of separating numbers and ranges with a space +is an extension to that specification. diff --git a/sbase/cut.c b/sbase/cut.c @@ -0,0 +1,181 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "text.h" +#include "util.h" + +typedef struct Range { + size_t min, max; + struct Range *next; +} Range; + +static Range *list = NULL; +static char mode = 0; +static char delim = '\t'; +static int nflag = 0; +static int sflag = 0; + +static void +insert(Range *r) +{ + Range *l, *p, *t; + + for (p = NULL, l = list; l; p = l, l = l->next) { + if (r->max && r->max + 1 < l->min) { + r->next = l; + break; + } else if (!l->max || r->min < l->max + 2) { + l->min = MIN(r->min, l->min); + for (p = l, t = l->next; t; p = t, t = t->next) + if (r->max && r->max + 1 < t->min) + break; + l->max = (p->max && r->max) ? MAX(p->max, r->max) : 0; + l->next = t; + return; + } + } + if (p) + p->next = r; + else + list = r; +} + +static void +parselist(char *str) +{ + char *s; + size_t n = 1; + Range *r; + + for (s = str; *s; s++) { + if (*s == ' ') + *s = ','; + if (*s == ',') + n++; + } + r = emalloc(n * sizeof(Range)); + for (s = str; n; n--, s++) { + r->min = (*s == '-') ? 1 : strtoul(s, &s, 10); + r->max = (*s == '-') ? strtoul(s + 1, &s, 10) : r->min; + r->next = NULL; + if (!r->min || (r->max && r->max < r->min) || (*s && *s != ',')) + eprintf("cut: bad list value\n"); + insert(r++); + } +} + +static size_t +seek(const char *s, size_t pos, size_t *prev, size_t count) +{ + const char *t; + size_t n = pos - *prev; + + if (mode == 'b') { + if ((t = memchr(s, 0, n))) + return t - s; + if (nflag) + while (n && !UTF8_POINT(s[n])) + n--; + *prev += n; + return n; + } else if (mode == 'c') { + for (n++, t = s; *t; t++) + if (UTF8_POINT(*t) && !--n) + break; + } else { + for (t = (count < 2) ? s : s + 1; n && *t; t++) + if (*t == delim && !--n && count) + break; + } + *prev = pos; + return t - s; +} + +static void +cut(FILE *fp) +{ + static char *buf = NULL; + static size_t size = 0; + char *s; + size_t i, n, p; + ssize_t len; + Range *r; + + while ((len = getline(&buf, &size, fp)) != -1) { + if (len && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + if (mode == 'f' && !strchr(buf, delim)) { + if (!sflag) + puts(buf); + continue; + } + for (i = 0, p = 1, s = buf, r = list; r; r = r->next, s += n) { + s += seek(s, r->min, &p, i++); + if (!*s) + break; + if (!r->max) { + fputs(s, stdout); + break; + } + n = seek(s, r->max + 1, &p, i++); + if (fwrite(s, 1, n, stdout) != n) + eprintf("write error:"); + } + putchar('\n'); + } +} + +static void +usage(void) +{ + eprintf("usage: cut -b list [-n] [file ...]\n" + " cut -c list [file ...]\n" + " cut -f list [-d delim] [-s] [file ...]\n"); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + + ARGBEGIN { + case 'b': + case 'c': + case 'f': + mode = ARGC(); + parselist(ARGF()); + break; + case 'd': + delim = *ARGF(); + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + default: + usage(); + } ARGEND; + + if (!mode) + usage(); + + if (!argc) + cut(stdin); + else for (; argc--; argv++) { + if (!strcmp(*argv, "-")) + cut(stdin); + else { + if (!(fp = fopen(*argv, "r"))) { + weprintf("fopen %s:", *argv); + continue; + } + cut(fp); + fclose(fp); + } + } + return 0; +} diff --git a/sbase/date.1 b/sbase/date.1 @@ -0,0 +1,26 @@ +.TH DATE 1 sbase\-VERSION +.SH NAME +date \- print date and time +.SH SYNOPSIS +.B date +.RB [ \-d +.IR time ] +.RB [ \-u ] +.RI [+ format ] +.SH DESCRIPTION +.B date +prints the date and time. If a +.I format +is given it is used to format the date as per +.IR strftime (3). +.SH OPTIONS +.TP +.BI \-d " time" +prints +.I time +instead of the system time, given as the number of seconds since the Unix epoch. +.TP +.B \-u +prints UTC time instead of local time. +.SH SEE ALSO +.IR strftime (3) diff --git a/sbase/date.c b/sbase/date.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [-u] [-d format] [+FORMAT]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char buf[BUFSIZ]; + char *fmt = "%c"; + struct tm *now = NULL; + struct tm *(*tztime)(const time_t *) = localtime; + const char *tz = "local"; + time_t t; + + t = time(NULL); + ARGBEGIN { + case 'd': + t = estrtol(EARGF(usage()), 0); + break; + case 'u': + tztime = gmtime; + tz = "gm"; + break; + default: + usage(); + } ARGEND; + if (argc > 0 && argv[0][0] == '+') + fmt = &argv[0][1]; + if (!(now = tztime(&t))) + eprintf("%stime failed\n", tz); + + strftime(buf, sizeof buf, fmt, now); + puts(buf); + + return 0; +} diff --git a/sbase/dirname.1 b/sbase/dirname.1 @@ -0,0 +1,14 @@ +.TH DIRNAME 1 sbase\-VERSION +.SH NAME +dirname \- strip final path component +.SH SYNOPSIS +.B dirname +.I pathname +.SH DESCRIPTION +.B dirname +prints the +.I pathname +with its final path component removed. +.SH SEE ALSO +.IR basename (1), +.IR dirname (3) diff --git a/sbase/dirname.c b/sbase/dirname.c @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s pathname\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 1) + usage(); + + puts(dirname(argv[0])); + + return 0; +} diff --git a/sbase/du.1 b/sbase/du.1 @@ -0,0 +1,37 @@ +.TH DU 1 sbase\-VERSION +.SH NAME +du \- display disk usage statistics +.SH SYNOPSIS +.B du +.RB [ \-a +.RB | +.BR \-s ] +.RB [ \-d +.IR depth ] +.RB [ \-k ] +.RB [ \-h ] +.RI [ file ...] +.SH DESCRIPTION +.B du +displays the file system block usage for each +.I file +argument and for each directory in the file hierarchy rooted in directory argument. +If no file is specified, the block usage of the hierarchy rooted in the current +directory is displayed. +.SH OPTIONS +.TP +.BI \-a +Display an entry for each file in the file hierarchy. +.TP +.BI \-s +Display only the grand total for the specified files. +.TP +.BI "\-d depth" +Maximum directory depth to print files and directories. +.TP +.BI \-k +By default all sizes are reported in 512-byte block counts. +The -k option causes the numbers to be reported in kilobyte counts. +.TP +.BI \-h +Enable human readable output. diff --git a/sbase/du.c b/sbase/du.c @@ -0,0 +1,189 @@ +/* See LICENSE file for copyright and license details. */ +#include <dirent.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "util.h" + +static long blksize = 512; +static char file[PATH_MAX]; +static long depth = -1; +static long curdepth = 0; + +static int aflag = 0; +static int dflag = 0; +static int sflag = 0; +static int kflag = 0; +static int hflag = 0; + +static long du(const char *); +static void print(long n, char *path); + +static void +usage(void) +{ + eprintf("usage: %s [-a | -s] [-d depth] [-h] [-k] [file...]\n", argv0); +} + +static char * +xrealpath(const char *pathname, char *resolved) +{ + char *r; + + r = realpath(pathname, resolved); + if (!r) + eprintf("realpath: %s:", pathname); + return r; +} + +int +main(int argc, char *argv[]) +{ + char *bsize; + long n; + + ARGBEGIN { + case 'a': + aflag = 1; + break; + case 'd': + dflag = 1; + depth = estrtol(EARGF(usage()), 0); + break; + case 's': + sflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'h': + hflag = 1; + break; + default: + usage(); + } ARGEND; + + if ((aflag && sflag) || (dflag && sflag)) + usage(); + + bsize = getenv("BLOCKSIZE"); + if (bsize) + blksize = estrtol(bsize, 0); + + if (kflag) + blksize = 1024; + + if (argc < 1) { + n = du("."); + if (sflag) + print(n, xrealpath(".", file)); + } else { + for (; argc > 0; argc--, argv++) { + curdepth = 0; + n = du(argv[0]); + if (sflag) + print(n, xrealpath(argv[0], file)); + } + } + return 0; +} + +static void +print(long n, char *path) +{ + if (hflag) + printf("%s\t%s\n", humansize(n * blksize), path); + else + printf("%lu\t%s\n", n, path); +} + +static char * +push(const char *path) +{ + char *cwd; + + cwd = agetcwd(); + if (chdir(path) < 0) + eprintf("chdir: %s:", path); + return cwd; +} + +static void +pop(char *path) +{ + if (chdir(path) < 0) + eprintf("chdir: %s:", path); + free(path); +} + +static long +nblks(struct stat *st) +{ + return (512 * st->st_blocks + blksize - 1) / blksize; +} + +static long +du(const char *path) +{ + DIR *dp; + char *cwd; + struct dirent *dent; + struct stat st; + long n = 0, m, t; + int r; + + if (lstat(path, &st) < 0) + eprintf("stat: %s:", path); + n = nblks(&st); + + if (!S_ISDIR(st.st_mode)) + goto done; + + dp = opendir(path); + if (!dp) { + weprintf("opendir %s:", path); + goto done; + } + + cwd = push(path); + while ((dent = readdir(dp))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + if (lstat(dent->d_name, &st) < 0) + eprintf("stat: %s:", dent->d_name); + if (S_ISDIR(st.st_mode)) { + t = curdepth; + curdepth++; + n += du(dent->d_name); + curdepth = t; + continue; + } + m = nblks(&st); + n += m; + if (aflag && !sflag) { + if (S_ISLNK(st.st_mode)) { + r = snprintf(file, sizeof(file), "%s/%s", + cwd, dent->d_name); + if (r >= sizeof(file) || r < 0) + eprintf("path too long\n"); + } else { + xrealpath(dent->d_name, file); + } + if (!dflag || (depth != -1 && curdepth < depth)) + print(m, file); + } + } + pop(cwd); + closedir(dp); + +done: + if (!sflag && (!dflag || (depth != -1 && curdepth <= depth))) + print(n, xrealpath(path, file)); + return n; +} diff --git a/sbase/echo.1 b/sbase/echo.1 @@ -0,0 +1,14 @@ +.TH ECHO 1 sbase\-VERSION +.SH NAME +echo \- print arguments +.SH SYNOPSIS +.B echo +.RB [ \-n ] +.RI [ string ...] +.SH DESCRIPTION +.B echo +prints its arguments to stdout, separated by spaces and terminated by a newline. +.SH OPTIONS +.TP +.B \-n +Do not print terminating newline. diff --git a/sbase/echo.c b/sbase/echo.c @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [-n] text\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int nflag = 0; + + ARGBEGIN { + case 'n': + nflag = 1; + break; + default: + usage(); + } ARGEND; + + for (; argc > 0; argc--, argv++) + putword(argv[0]); + if (!nflag) + putchar('\n'); + + return 0; +} diff --git a/sbase/env.1 b/sbase/env.1 @@ -0,0 +1,41 @@ +.TH ENV 1 sbase\-VERSION +.SH NAME +env \- modify the environment, then print it or run a command. +.SH SYNOPSIS +.B env +.RB [ \-i ] +.RB [ \-u +.IR name ]... +.RI [ name=value ]... +.RI [ cmd +.RI [ arg ...]] + +.SH DESCRIPTION +.B env +removes part of the environment according to the flags, then adds or +sets each variable specified by +.IR name +to equal +.IR value . + +If +.IR cmd +is given, it is executed in this new environment; otherwise, the +modified environment is printed to standard out. + +.SH OPTIONS +.TP +.B \-i +Comptetely ignore the existing environment; start fresh. + +.TP +.B \-u name +Unsets +.IR name +from the environment. + +.SH SEE ALSO +.IR printenv (1) +.IR putenv (3) +.IR environ (7) + diff --git a/sbase/env.c b/sbase/env.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" + +extern char **environ; + +static void +usage(void) +{ + eprintf("usage: env [-i] [-u name]... [name=value]... [cmd [arg...]]\n"); +} + +int +main(int argc, char *argv[]) +{ + ARGBEGIN { + case 'i': + if(environ) + *environ = NULL; + break; + case 'u': + unsetenv(EARGF(usage())); + break; + default: + usage(); + } ARGEND; + + for (; *argv && strchr(*argv, '='); argv++) + putenv(*argv); + + if (*argv) { + execvp(*argv, argv); + enprintf(127 - (errno != EEXIST), "env: '%s':", *argv); + } + + while (environ && *environ) + printf("%s\n", *environ++); + + return 0; +} diff --git a/sbase/expand.1 b/sbase/expand.1 @@ -0,0 +1,25 @@ +.TH EXPAND 1 sbase\-VERSION +.SH NAME +expand \- expand tabs to spaces +.SH SYNOPSIS +.B expand +.RB [ \-t +.IR n ] +.RI [ file ...] +.SH DESCRIPTION +expand processes the named files or the standard input, writing the +standard output with tabs changed into spaces. Backspace characters +are preserved into the output and decrement the column count for tab +calculations. +.SH OPTIONS +.TP +.BI \-i +Only change tabs to spaces at the start of lines. +.TP +.BI \-t " n" +Expand tabs to +.I n +spaces. We currently support only a single numerical argument. +.SH SEE ALSO +.IR unexpand (1), +.IR fold (1) diff --git a/sbase/expand.c b/sbase/expand.c @@ -0,0 +1,98 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> + +#include "utf.h" +#include "util.h" + +static int expand(const char *, FILE *, int); + +static int iflag = 0; + +static void +usage(void) +{ + eprintf("usage: %s [-i] [-t n] [file...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + int tabstop = 8; + int ret = 0; + + ARGBEGIN { + case 'i': + iflag = 1; + break; + case 't': + tabstop = estrtol(EARGF(usage()), 0); + if (!tabstop) + eprintf("tab size cannot be zero\n"); + break; + default: + usage(); + } ARGEND; + + if (argc == 0) { + expand("<stdin>", stdin, tabstop); + } else { + for (; argc > 0; argc--, argv++) { + if (!(fp = fopen(argv[0], "r"))) { + weprintf("fopen %s:", argv[0]); + ret = 1; + continue; + } + expand(argv[0], fp, tabstop); + fclose(fp); + } + } + return ret; +} + +static int +expand(const char *file, FILE *fp, int tabstop) +{ + int col = 0; + Rune r; + int bol = 1; + + for (;;) { + if (!readrune(file, fp, &r)) + break; + + switch (r) { + case '\t': + if (bol || !iflag) { + do { + col++; + putchar(' '); + } while (col % tabstop); + } else { + putchar('\t'); + col += tabstop - col % tabstop; + } + break; + case '\b': + if (col) + col--; + bol = 0; + writerune("<stdout>", stdout, &r); + break; + case '\n': + col = 0; + bol = 1; + writerune("<stdout>", stdout, &r); + break; + default: + col++; + if (r != ' ') + bol = 0; + writerune("<stdout>", stdout, &r); + break; + } + } + + return 0; +} diff --git a/sbase/expr.1 b/sbase/expr.1 @@ -0,0 +1,156 @@ +.\" $OpenBSD: src/bin/expr/expr.1,v 1.22 2014/02/23 18:13:27 schwarze Exp $ +.\" $NetBSD: expr.1,v 1.9 1995/04/28 23:27:13 jtc Exp $ +.\" +.\" Written by J.T. Conklin <jtc@netbsd.org>. +.\" Public domain. +.\" +.Dd $Mdocdate: September 3 2010 $ +.Dt EXPR 1 +.Os +.Sh NAME +.Nm expr +.Nd evaluate expression +.Sh SYNOPSIS +.Nm expr +.Ar expression +.Sh DESCRIPTION +The +.Nm +utility evaluates +.Ar expression +and writes the result on standard output. +All operators are separate arguments to the +.Nm +utility. +Characters special to the command interpreter must be escaped. +.Pp +Operators are listed below in order of increasing precedence. +Operators with equal precedence are grouped within { } symbols. +.Bl -tag -width indent +.It Ar expr1 | expr2 +Returns the evaluation of +.Ar expr1 +if it is neither an empty string nor zero; +otherwise, returns the evaluation of +.Ar expr2 . +.It Ar expr1 Li & Ar expr2 +Returns the evaluation of +.Ar expr1 +if neither expression evaluates to an empty string or zero; +otherwise, returns zero. +.It Ar expr1 Li "{=, >, >=, <, <=, !=}" Ar expr2 +Returns the results of integer comparison if both arguments are integers; +otherwise, returns the results of string comparison using the locale-specific +collation sequence. +The result of each comparison is 1 if the specified relation is true, +or 0 if the relation is false. +.It Ar expr1 Li "{+, -}" Ar expr2 +Returns the results of addition or subtraction of integer-valued arguments. +.It Ar expr1 Li "{*, /, %}" Ar expr2 +Returns the results of multiplication, integer division, or remainder of +integer-valued arguments. +.It Ar expr1 Li : Ar expr2 +The +.Ql \&: +operator matches +.Ar expr1 +against +.Ar expr2 , +which must be a basic regular expression. +The regular expression is anchored +to the beginning of the string with an implicit +.Ql ^ . +.Pp +If the match succeeds and the pattern contains at least one regular +expression subexpression +.Dq "\e(...\e)" , +the string corresponding to +.Dq "\e1" +is returned; +otherwise, the matching operator returns the number of characters matched. +If the match fails and the pattern contains a regular expression subexpression +the null string is returned; +otherwise, returns 0. +.Pp +Note: the empty string cannot be matched using +.Bd -literal -offset indent +expr '' : '$' +.Ed +.Pp +This is because the returned number of matched characters +.Pq zero +is indistinguishable from a failed match, so +.Nm +returns failure +.Pq 0 . +To match the empty string, use a structure such as: +.Bd -literal -offset indent +expr X'' : 'X$' +.Ed +.El +.Pp +Parentheses are used for grouping in the usual manner. +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It 0 +The expression is neither an empty string nor 0. +.It 1 +The expression is an empty string or 0. +.It 2 +The expression is invalid. +.It \*(Gt2 +An error occurred (such as memory allocation failure). +.El +.Sh EXAMPLES +Add 1 to the variable +.Va a : +.Bd -literal -offset indent +$ a=`expr $a + 1` +.Ed +.Pp +Return the filename portion of a pathname stored +in variable +.Va a . +The +.Ql // +characters act to eliminate ambiguity with the division operator: +.Bd -literal -offset indent +$ expr "//$a" \&: '.*/\e(.*\e)' +.Ed +.Pp +Return the number of characters in variable +.Va a : +.Bd -literal -offset indent +$ expr $a \&: '.*' +.Ed +.Sh SEE ALSO +.Xr test 1 , +.Xr re_format 7 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Sh HISTORY +The +.Nm +utility first appeared in the Programmer's Workbench (PWB/UNIX) +and has supported regular expressions since +.At v7 . +It was rewritten from scratch for +.Bx 386 0.1 +and again for +.Nx 1.1 . +.Sh AUTHORS +.An -nosplit +The first free version was written by +.An Pace Willisson +in 1992. +This version was written by +.An John T. Conklin +in 1994. diff --git a/sbase/expr.c b/sbase/expr.c @@ -0,0 +1,287 @@ +/* See LICENSE file for copyright and license details. */ +#include <inttypes.h> +#include <limits.h> +#include <regex.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +enum { + VAL = CHAR_MAX + 1, GE, LE, NE +}; + +typedef struct { + char *s; + intmax_t n; +} Val; + +static void enan(Val); +static void ezero(intmax_t); +static void doop(int*, int**, Val*, Val**); +static Val match(Val, Val); +static int valcmp(Val, Val); +static char *valstr(Val, char *, size_t); +static int lex(char *); +static int parse(char **, int); + +static size_t intlen; +static Val lastval; + +static void +enan(Val v) +{ + if (v.s) + enprintf(2, "syntax error: expected integer got `%s'\n", v.s); +} + +static void +ezero(intmax_t n) +{ + if (n == 0) + enprintf(2, "division by zero\n"); +} + +static void +doop(int *op, int **opp, Val *val, Val **valp) +{ + Val ret, a, b; + int o; + + /* For an operation, we need a valid operator + * and two values on the stack */ + if ((*opp)[-1] == '(') + enprintf(2, "syntax error: extra (\n"); + if (*valp - val < 2) + enprintf(2, "syntax error: missing expression or extra operator\n"); + + a = (*valp)[-2]; + b = (*valp)[-1]; + o = (*opp)[-1]; + + switch (o) { + case '|': + if (a.s && *a.s) { + ret = (Val){ a.s, 0 }; + } else if (!a.s && a.n) { + ret = (Val){ NULL, a.n }; + } else if (b.s && *b.s) { + ret = (Val){ b.s, 0 }; + } else { + ret = (Val){ NULL, b.n }; + } + break; + case '&': + if (((a.s && *a.s) || a.n) && + ((b.s && *b.s) || b.n)) { + ret = a; + } else { + ret = (Val){ NULL, 0 }; + } + break; + case '=': ret = (Val){ NULL, valcmp(a, b) == 0 }; break; + case '>': ret = (Val){ NULL, valcmp(a, b) > 0 }; break; + case GE : ret = (Val){ NULL, valcmp(a, b) >= 0 }; break; + case '<': ret = (Val){ NULL, valcmp(a, b) < 0 }; break; + case LE : ret = (Val){ NULL, valcmp(a, b) <= 0 }; break; + case NE : ret = (Val){ NULL, valcmp(a, b) != 0 }; break; + + case '+': enan(a); enan(b); ret = (Val){ NULL, a.n + b.n }; break; + case '-': enan(a); enan(b); ret = (Val){ NULL, a.n - b.n }; break; + case '*': enan(a); enan(b); ret = (Val){ NULL, a.n * b.n }; break; + case '/': enan(a); enan(b); ezero(b.n); ret = (Val){ NULL, a.n / b.n }; break; + case '%': enan(a); enan(b); ezero(b.n); ret = (Val){ NULL, a.n % b.n }; break; + + case ':': ret = match(a, b); break; + } + + (*valp)[-2] = ret; + (*opp)--; + (*valp)--; +} + +static Val +match(Val vstr, Val vregx) +{ + intmax_t d; + char *anchreg, *ret, *p; + char buf1[intlen], buf2[intlen], *str, *regx; + regoff_t len; + regex_t re; + regmatch_t matches[2]; + + str = valstr(vstr, buf1, sizeof(buf1)); + regx = valstr(vregx, buf2, sizeof(buf2)); + + anchreg = malloc(strlen(regx) + 2); + if (!anchreg) + enprintf(3, "malloc:"); + snprintf(anchreg, strlen(regx) + 2, "^%s", regx); + + enregcomp(3, &re, anchreg, 0); + free(anchreg); + + if (regexec(&re, str, 2, matches, 0)) { + regfree(&re); + return (Val){ (re.re_nsub ? "" : NULL), 0 }; + } + + if (re.re_nsub) { + regfree(&re); + len = matches[1].rm_eo - matches[1].rm_so + 1; + ret = malloc(len); + if (!ret) + enprintf(3, "malloc:"); + strlcpy(ret, str + matches[1].rm_so, len); + d = strtoimax(ret, &p, 10); + if (*ret && !*p) { + free(ret); + return (Val){ NULL, d }; + } + return (Val){ ret, 0 }; + } + regfree(&re); + return (Val){ NULL, matches[0].rm_eo - matches[0].rm_so }; +} + + + +static int +valcmp(Val a, Val b) +{ + char buf1[intlen], buf2[intlen], *astr, *bstr; + + astr = valstr(a, buf1, sizeof(buf1)); + bstr = valstr(b, buf2, sizeof(buf2)); + + return strcmp(astr, bstr); +} + +static char * +valstr(Val val, char *buf, size_t bufsiz) +{ + if (val.s) + return val.s; + snprintf(buf, bufsiz, "%"PRIdMAX, val.n); + return buf; +} + +static int +lex(char *p) +{ + intmax_t d; + char *q, *ops = "|&=><+-*/%():"; + + /* clean integer */ + d = strtoimax(p, &q, 10); + if (*p && !*q) { + lastval = (Val){ NULL, d }; + return VAL; + } + + /* one-char operand */ + if (*p && !*(p+1) && strchr(ops, *p)) + return *p; + + /* two-char operand */ + if (*p && *(p+1) == '=' && !*(p+2)) { + switch (*p) { + case '>': + return GE; + case '<': + return LE; + case '!': + return NE; + } + } + + /* nothing matched, treat as string */ + lastval = (Val){ p, 0 }; + return VAL; +} + +static int +parse(char **expr, int exprlen) +{ + Val val[exprlen], *valp = val; + int op[exprlen], *opp = op; + int i, type, lasttype = 0; + char prec[] = { + [ 0 ] = 0, [VAL] = 0, + ['|'] = 1, + ['&'] = 2, + ['='] = 3, ['>'] = 3, [GE] = 3, ['<'] = 3, [LE] = 3, [NE] = 3, + ['+'] = 4, ['-'] = 4, + ['*'] = 5, ['/'] = 5, ['%'] = 5, + [':'] = 6, + }; + + for (i = 0; i < exprlen; i++) { + type = lex(expr[i]); + + switch (type) { + case VAL: + *valp++ = lastval; + break; + case '(': + *opp++ = '('; + break; + case ')': + if (lasttype == '(') + enprintf(2, "syntax error: empty ( )\n"); + while (opp > op && opp[-1] != '(') + doop(op, &opp, val, &valp); + if (opp == op) + enprintf(2, "syntax error: extra )\n"); + opp--; + break; + default : + if (prec[lasttype]) + enprintf(2, "syntax error: extra operator\n"); + while (opp > op && prec[opp[-1]] >= prec[type]) + doop(op, &opp, val, &valp); + *opp++ = type; + break; + } + lasttype = type; + } + while (opp > op) + doop(op, &opp, val, &valp); + + if (valp == val) + enprintf(2, "syntax error: missing expression\n"); + if (valp - val > 1) + enprintf(2, "syntax error: extra expression\n"); + + valp--; + if (valp->s) + printf("%s\n", valp->s); + else + printf("%"PRIdMAX"\n", valp->n); + + return (valp->s && *valp->s) || valp->n; +} + +static void +usage(void) +{ + eprintf("usage: %s EXPRESSION\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + intmax_t n = INTMAX_MIN; + + /* Get the maximum number of digits (+ sign) */ + for (intlen = (n < 0); n; n /= 10, ++intlen); + + ARGBEGIN { + default: + usage(); + } ARGEND; + + return !parse(argv, argc); +} diff --git a/sbase/false.1 b/sbase/false.1 @@ -0,0 +1,16 @@ +.Dd January 16, 2015 +.Dt FALSE 1 sbase\-VERSION +.Sh NAME +.Nm false +.Nd return failure +.Sh SYNOPSIS +.Nm false +.Sh DESCRIPTION +.Nm +returns a status code indicating failure. +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. diff --git a/sbase/false.c b/sbase/false.c @@ -0,0 +1,6 @@ +/* See LICENSE file for copyright and license details. */ +int +main(void) +{ + return 1; +} diff --git a/sbase/fold.1 b/sbase/fold.1 @@ -0,0 +1,25 @@ +.TH FOLD 1 sbase\-VERSION +.SH NAME +fold \- wrap lines to width +.SH SYNOPSIS +.B fold +.RB [ \-bs ] +.RB [ \-w +.IR width ] +.RI [ file ...] +.SH DESCRIPTION +.B fold +reads each file in sequence and prints its lines, broken such that no line +exceeds 80 UTF-8 characters. If no file is given, fold reads from stdin. +.SH OPTIONS +.TP +.B \-b +counts bytes rather than characters. +.TP +.B \-s +breaks only at spaces. +.TP +.BI \-w " width" +breaks at +.I width +characters, instead of 80. diff --git a/sbase/fold.c b/sbase/fold.c @@ -0,0 +1,113 @@ +/* See LICENSE file for copyright and license details. */ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "text.h" +#include "util.h" + +static void fold(FILE *, long); +static void foldline(const char *, long); + +static int bflag = 0; +static int sflag = 0; + +static void +usage(void) +{ + eprintf("usage: %s [-bs] [-w width] [FILE...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + long width = 80; + FILE *fp; + + ARGBEGIN { + case 'b': + bflag = 1; + break; + case 's': + sflag = 1; + break; + case 'w': + width = estrtol(EARGF(usage()), 0); + break; + ARGNUM: + width = ARGNUMF(0); + break; + default: + usage(); + } ARGEND; + + if (argc == 0) { + fold(stdin, width); + } else { + for (; argc > 0; argc--, argv++) { + if (!(fp = fopen(argv[0], "r"))) { + weprintf("fopen %s:", argv[0]); + continue; + } + fold(fp, width); + fclose(fp); + } + } + + return 0; +} + +static void +fold(FILE *fp, long width) +{ + char *buf = NULL; + size_t size = 0; + + while (getline(&buf, &size, fp) != -1) + foldline(buf, width); + free(buf); +} + +static void +foldline(const char *str, long width) +{ + int space; + long col, j; + size_t i = 0, n = 0; + int c; + + do { + space = 0; + for (j = i, col = 0; str[j] && col <= width; j++) { + c = str[j]; + if (!UTF8_POINT(c) && !bflag) + continue; + if (sflag && isspace(c)) { + space = 1; + n = j+1; + } + else if (!space) + n = j; + + if (!bflag && iscntrl(c)) + switch(c) { + case '\b': + col--; + break; + case '\r': + col = 0; + break; + case '\t': + col += (col+1) % 8; + break; + } + else + col++; + } + if (fwrite(&str[i], 1, n-i, stdout) != n-i) + eprintf("<stdout>: write error:"); + if (str[n]) + putchar('\n'); + } while (str[i = n] && str[i] != '\n'); +} diff --git a/sbase/fs.h b/sbase/fs.h @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +extern int cp_aflag; +extern int cp_fflag; +extern int cp_Pflag; +extern int cp_pflag; +extern int cp_rflag; +extern int cp_vflag; +extern int cp_status; + +extern int rm_fflag; +extern int rm_rflag; + +int cp(const char *, const char *); +void rm(const char *); diff --git a/sbase/grep.1 b/sbase/grep.1 @@ -0,0 +1,86 @@ +.Dd November 21, 2014 +.Dt GREP 1 sbase\-VERSION +.Sh NAME +.Nm grep +.Nd search files for patterns +.Sh SYNOPSIS +.Nm grep +.Op Fl EFHchilnqsvx +.Op Fl e Ar pattern +.Op Fl f Ar file +.Op Ar pattern +.Op Ar file ... +.Sh DESCRIPTION +.Nm +searches the input files for lines that match the +.Ar pattern , +a regular expression as defined in +.Xr regex 7 . +By default each matching line is printed to stdout. If no file is given +.Nm +reads from stdin. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl E +Match using extended regex. +.It Fl F +Match using fixed strings. Treat each pattern specified as a string instead of a regular +expression. +.It Fl H +Prefix each matching line with its filename in the output. This is the +default when there is more than one file specified. +.It Fl c +Print only a count of matching lines. +.It Fl e Ar pattern +Specify a pattern used during the search of the input: an input +line is selected if it matches any of the specified patterns. +This option is most useful when multiple -e options are used to +specify multiple patterns, or when a pattern begins with a dash +.It Fl f Ar file +Read one or more patterns from the file named by the pathname file. +Patterns in file shall be terminated by a <newline>. A null pattern can be +specified by an empty line in pattern_file. Unless the -E or -F option is +also specified, each pattern shall be treated as a BRE. +(`-'). +.It Fl h +Do not prefix each line with 'filename:' prefix. +.It Fl i +Match lines case insensitively. +.It Fl l +Print only the names of files with matching lines. +.It Fl n +Prefix each matching line with its line number in the input. +.It Fl q +Print nothing, only return status. +.It Fl s +Suppress the error messages ordinarily written for nonexistent or unreadable files. +.It Fl v +Select lines which do +.B not +Match the pattern. +.It Fl x +Consider only input lines that use all characters in the line excluding the terminating <newline> to +match an entire fixed string or regular expression to be matching lines. +.El +.Sh EXIT STATUS +.Bl -tag -width Ds +.It 0 +One or more lines were matched. +.It 1 +No lines were matched. +.It > 1 +An error occurred. +.El +.Sh SEE ALSO +.Xr regex 7 , +.Xr sed 1 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl Hh +are an extension to that specification. diff --git a/sbase/grep.c b/sbase/grep.c @@ -0,0 +1,255 @@ +/* See LICENSE file for copyright and license details. */ +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include "queue.h" +#include "text.h" +#include "util.h" + +enum { Match = 0, NoMatch = 1, Error = 2 }; + +static void addpattern(const char *); +static void addpatternfile(FILE *); +static int grep(FILE *, const char *); + +static int Fflag; +static int Hflag; +static int eflag; +static int fflag; +static int hflag; +static int iflag; +static int sflag; +static int vflag; +static int xflag; +static int many; +static int mode; + +struct pattern { + char *pattern; + regex_t preg; + SLIST_ENTRY(pattern) entry; +}; + +static SLIST_HEAD(phead, pattern) phead; + +static void +usage(void) +{ + enprintf(Error, "usage: %s [-EFHchilnqsvx] [-e pattern] [-f file] [pattern] [file ...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct pattern *pnode; + int i, m, flags = REG_NOSUB, match = NoMatch; + FILE *fp; + char *arg; + + SLIST_INIT(&phead); + + ARGBEGIN { + case 'E': + flags |= REG_EXTENDED; + break; + case 'F': + Fflag = 1; + break; + case 'H': + Hflag = 1; + hflag = 0; + break; + case 'e': + arg = EARGF(usage()); + fp = fmemopen(arg, strlen(arg) + 1, "r"); + addpatternfile(fp); + fclose(fp); + eflag = 1; + break; + case 'f': + arg = EARGF(usage()); + fp = fopen(arg, "r"); + if (!fp) + enprintf(Error, "fopen %s:", arg); + addpatternfile(fp); + fclose(fp); + fflag = 1; + break; + case 'h': + hflag = 1; + Hflag = 0; + break; + case 'c': + case 'l': + case 'n': + case 'q': + mode = ARGC(); + break; + case 'i': + flags |= REG_ICASE; + iflag = 1; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + case 'x': + xflag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc == 0 && !eflag && !fflag) + usage(); /* no pattern */ + + /* just add literal pattern to list */ + if (!eflag && !fflag) { + fp = fmemopen(argv[0], strlen(argv[0]) + 1, "r"); + addpatternfile(fp); + fclose(fp); + argc--; + argv++; + } + + if (!Fflag) + /* Compile regex for all search patterns */ + SLIST_FOREACH(pnode, &phead, entry) + enregcomp(Error, &pnode->preg, pnode->pattern, flags); + many = (argc > 1); + if (argc == 0) { + match = grep(stdin, "<stdin>"); + } else { + for (i = 0; i < argc; i++) { + if (!(fp = fopen(argv[i], "r"))) { + if (!sflag) + weprintf("fopen %s:", argv[i]); + match = Error; + continue; + } + m = grep(fp, argv[i]); + if (m == Error || (match != Error && m == Match)) + match = m; + fclose(fp); + } + } + return match; +} + +static void +addpattern(const char *pattern) +{ + struct pattern *pnode; + char *tmp; + + /* a null BRE/ERE matches every line */ + if (!Fflag) + if (pattern[0] == '\0') + pattern = "."; + + if (!Fflag && xflag) { + tmp = malloc(strlen(pattern) + 3); + if (!tmp) + enprintf(Error, "malloc:"); + snprintf(tmp, strlen(pattern) + 3, "%s%s%s", + pattern[0] == '^' ? "" : "^", + pattern, + pattern[strlen(pattern) - 1] == '$' ? "" : "$"); + } else { + tmp = strdup(pattern); + if (!tmp) + enprintf(Error, "strdup:"); + } + + pnode = malloc(sizeof(*pnode)); + if (!pnode) + enprintf(Error, "malloc:"); + pnode->pattern = tmp; + SLIST_INSERT_HEAD(&phead, pnode, entry); +} + +static void +addpatternfile(FILE *fp) +{ + static char *buf = NULL; + static size_t size = 0; + size_t len = 0; + + while ((len = getline(&buf, &size, fp)) != -1) { + if (len && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + addpattern(buf); + } + if (ferror(fp)) + enprintf(Error, "read error:"); +} + +static int +grep(FILE *fp, const char *str) +{ + static char *buf = NULL; + static size_t size = 0; + size_t len = 0; + long c = 0, n; + struct pattern *pnode; + int match = NoMatch; + + for (n = 1; (len = getline(&buf, &size, fp)) != -1; n++) { + /* Remove the trailing newline if one is present. */ + if (len && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + SLIST_FOREACH(pnode, &phead, entry) { + if (!Fflag) { + if (regexec(&pnode->preg, buf[0] == '\0' ? "\n" : buf, 0, NULL, 0) ^ vflag) + continue; + } else { + if (!xflag) { + if ((iflag ? strcasestr : strstr)(buf, pnode->pattern)) + match = Match; + else + match = NoMatch; + } else { + if (!(iflag ? strcasecmp : strcmp)(buf, pnode->pattern)) + match = Match; + else + match = NoMatch; + } + if (match ^ vflag) + continue; + } + switch (mode) { + case 'c': + c++; + break; + case 'l': + puts(str); + goto end; + case 'q': + exit(Match); + default: + if (!hflag && (many || Hflag)) + printf("%s:", str); + if (mode == 'n') + printf("%ld:", n); + puts(buf); + break; + } + match = Match; + break; + } + } + if (mode == 'c') + printf("%ld\n", c); +end: + if (ferror(fp)) { + weprintf("%s: read error:", str); + match = Error; + } + return match; +} diff --git a/sbase/head.1 b/sbase/head.1 @@ -0,0 +1,18 @@ +.TH HEAD 1 sbase\-VERSION +.SH NAME +head \- output first part of files +.SH SYNOPSIS +.B head +.RB [ \-n +.IR lines ] +.RI [ file ...] +.SH DESCRIPTION +.B head +writes the first 10 lines of each file to stdout. If no file is given, head +reads from stdin. +.SH OPTIONS +.TP +.BI \-n " lines" +outputs the given number of lines. +.SH SEE ALSO +.IR tail (1) diff --git a/sbase/head.c b/sbase/head.c @@ -0,0 +1,74 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "text.h" +#include "util.h" + +static void head(FILE *, const char *, long); + +static void +usage(void) +{ + eprintf("usage: %s [-n lines] [file...]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + long n = 10; + FILE *fp; + int ret = 0; + int newline, many; + + ARGBEGIN { + case 'n': + n = estrtol(EARGF(usage()), 0); + break; + ARGNUM: + n = ARGNUMF(0); + break; + default: + usage(); + } ARGEND; + + if (argc == 0) { + head(stdin, "<stdin>", n); + } else { + many = argc > 1; + for (newline = 0; argc > 0; argc--, argv++) { + if (!(fp = fopen(argv[0], "r"))) { + weprintf("fopen %s:", argv[0]); + ret = 1; + continue; + } + if (many) + printf("%s==> %s <==\n", + newline ? "\n" : "", argv[0]); + newline = 1; + head(fp, argv[0], n); + fclose(fp); + } + } + return ret; +} + +static void +head(FILE *fp, const char *str, long n) +{ + char *buf = NULL; + size_t size = 0; + ssize_t len; + unsigned long i = 0; + + while (i < n && ((len = getline(&buf, &size, fp)) != -1)) { + fputs(buf, stdout); + if (buf[len - 1] == '\n') + i++; + } + free(buf); + if (ferror(fp)) + eprintf("%s: read error:", str); +} diff --git a/sbase/hostname.1 b/sbase/hostname.1 @@ -0,0 +1,12 @@ +.TH HOSTNAME 1 sbase\-VERSION +.SH NAME +hostname \- set or print name of current host system +.SH SYNOPSIS +.B hostname +.I [name] +.SH DESCRIPTION +.B hostname +sets or prints the name of the current host. If no argument is given, +the name of the current host is printed. +.SH SEE ALSO +.IR hostname (7) diff --git a/sbase/hostname.c b/sbase/hostname.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [name]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char host[HOST_NAME_MAX + 1]; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 1) { + if (gethostname(host, sizeof(host)) < 0) + eprintf("gethostname:"); + puts(host); + } else { + if (sethostname(argv[0], strlen(argv[0])) < 0) + eprintf("sethostname:"); + } + return 0; +} diff --git a/sbase/kill.1 b/sbase/kill.1 @@ -0,0 +1,59 @@ +.Dd November 23, 2014 +.Dt KILL 1 sbase\-VERSION +.Os +.Sh NAME +.Nm kill +.Nd signal processes +.Sh SYNOPSIS +.Nm kill +.Op Fl s Ar signal_name +.Ar pid ... +.Nm kill +.Fl l Op Ar exit_status +.Nm kill +.Fl Ar signal_name +.Ar pid ... +.Nm kill +.Fl Ar signal_number +.Ar pid ... +.Sh DESCRIPTION +.Nm +by default sends a +.I TERM +signal to the given processes. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl s Ar signal_name +A symbolic signal name specifying the signal to be sent instead +of the default SIGTERM. +Sends the named signal. +.It Fl l Op Ar exit_status +Lists available signals. If an +.Ar exit_status +is given, only the corresponding signal name will be printed. +.It Fl signal_name +A symbolic signal name specifying the signal to be sent instead +of the default SIGTERM. +.It Fl signal_number +A non-negative decimal integer specifying the signal to be sent +instead of the default SIGTERM. +.El +.Sh SEE ALSO +.Xr kill 2 , +.Xr signal 7 +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The +.Fl Ar signal_name +and +.Fl Ar signal_number +syntax is marked by +.St -p1003.1-2008 +as being an +X/OPEN System Interfaces +option. diff --git a/sbase/kill.c b/sbase/kill.c @@ -0,0 +1,138 @@ +/* See LICENSE file for copyright and license details. */ +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "util.h" + +struct { + const char *name; + int sig; +} sigs[] = { + { "0", 0 }, +#define SIG(n) { #n, SIG##n } + SIG(ABRT), SIG(ALRM), SIG(BUS), SIG(CHLD), SIG(CONT), SIG(FPE), SIG(HUP), + SIG(ILL), SIG(INT), SIG(KILL), SIG(PIPE), SIG(QUIT), SIG(SEGV), SIG(STOP), + SIG(TERM), SIG(TSTP), SIG(TTIN), SIG(TTOU), SIG(USR1), SIG(USR2), SIG(URG), +#undef SIG +}; + +const char *sig2name(int); +int name2sig(const char *); + +static void +usage(void) +{ + weprintf("usage: %s [-s signame | -signum | -signame] pid ...\n", argv0); + weprintf(" %s -l [exit_status]\n", argv0); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + char *end; + int ret = 0; + int sig = SIGTERM; + pid_t pid; + size_t i; + + argv0 = argv[0]; + if (argc < 2) + usage(); + + argc--, argv++; + if (strcmp(argv[0], "-l") == 0) { + argc--, argv++; + if (argc == 0) { + for (i = 0; i < LEN(sigs); i++) + puts(sigs[i].name); + exit(0); + } else if (argc > 1) + usage(); + errno = 0; + sig = strtol(argv[0], &end, 10); + if (*end != '\0' || errno != 0) + eprintf("%s: bad signal number\n", argv[0]); + if (sig > 128) + sig = WTERMSIG(sig); + puts(sig2name(sig)); + exit(0); + } + + if (strcmp(argv[0], "-s") == 0) { + argc--, argv++; + if (argc == 0) + usage(); + sig = name2sig(argv[0]); + argc--, argv++; + } else if (argv[0][0] == '-') { + if (isdigit(argv[0][1])) { + /* handle XSI extension -signum */ + errno = 0; + sig = strtol(&argv[0][1], &end, 10); + if (*end != '\0' || errno != 0) + eprintf("%s: bad signal number\n", &argv[0][1]); + sig2name(sig); + argc--, argv++; + } else if (argv[0][1] != '-') { + /* handle XSI extension -signame */ + sig = name2sig(&argv[0][1]); + argc--, argv++; + } + } + + if (argc > 0 && strcmp(argv[0], "--") == 0) + argc--, argv++; + + if (argc == 0) + usage(); + + for (; argc; argc--, argv++) { + errno = 0; + pid = strtol(argv[0], &end, 10); + if (*end == '\0' && errno == 0) { + if (kill(pid, sig) < 0) { + weprintf("kill %d:", pid); + ret = 1; + } + } else { + weprintf("%s: bad pid\n", argv[0]); + ret = 1; + } + } + + exit(ret); +} + +const char * +sig2name(int sig) +{ + size_t i; + + for (i = 0; i < LEN(sigs); i++) + if (sigs[i].sig == sig) + return sigs[i].name; + eprintf("%d: bad signal number\n", sig); + /* unreachable */ + return NULL; +} + +int +name2sig(const char *name) +{ + size_t i; + + for (i = 0; i < LEN(sigs); i++) + if (strcasecmp(sigs[i].name, name) == 0) + return sigs[i].sig; + eprintf("%s: bad signal name\n", name); + /* unreachable */ + return -1; +} diff --git a/sbase/libutf/chartorunearr.c b/sbase/libutf/chartorunearr.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdlib.h> +#include <string.h> + +#include "../util.h" +#include "../utf.h" + +int +chartorunearr(const char *str, Rune **r) +{ + size_t len = strlen(str), rlen, roff, ret = 1, i; + Rune s; + + for (rlen = 0, roff = 0; roff < len && ret; rlen++) { + ret = charntorune(&s, str + roff, MAX(UTFmax, len - roff)); + roff += ret; + } + + *r = emalloc(rlen * sizeof(Rune) + 1); + (*r)[rlen] = 0; + + for (i = 0, roff = 0; roff < len && i < rlen; i++) { + roff += charntorune(&(*r)[i], str + roff, MAX(UTFmax, len - roff)); + } + + return rlen; +} diff --git a/sbase/libutf/readrune.c b/sbase/libutf/readrune.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../utf.h" + +int +readrune(const char *file, FILE *fp, Rune *r) +{ + char buf[UTFmax]; + int c, i; + + if ((c = fgetc(fp)) == EOF) { + if (ferror(fp)) { + fprintf(stderr, "%s: read error: %s\n", + file, strerror(errno)); + exit(1); + } + return 0; + } + + if (c < Runeself) { + *r = (Rune)c; + return 1; + } + + buf[0] = c; + for (i = 1; ;) { + if ((c = fgetc(fp)) == EOF) { + if (ferror(fp)) { + fprintf(stderr, "%s: read error: %s\n", + file, strerror(errno)); + exit(1); + } + return 0; + } + buf[i++] = c; + if (fullrune(buf, i)) { + chartorune(r, buf); + break; + } + } + return 1; +} diff --git a/sbase/libutf/rune.c b/sbase/libutf/rune.c @@ -0,0 +1,148 @@ +/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com> + * + * 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. + */ +#include "../utf.h" + +#define MIN(x,y) ((x) < (y) ? (x) : (y)) + +#define UTFSEQ(x) ((((x) & 0x80) == 0x00) ? 1 /* 0xxxxxxx */ \ + : (((x) & 0xC0) == 0x80) ? 0 /* 10xxxxxx */ \ + : (((x) & 0xE0) == 0xC0) ? 2 /* 110xxxxx */ \ + : (((x) & 0xF0) == 0xE0) ? 3 /* 1110xxxx */ \ + : (((x) & 0xF8) == 0xF0) ? 4 /* 11110xxx */ \ + : (((x) & 0xFC) == 0xF8) ? 5 /* 111110xx */ \ + : (((x) & 0xFE) == 0xFC) ? 6 /* 1111110x */ \ + : 0 ) + +#define BADRUNE(x) ((x) < 0 || (x) > Runemax \ + || ((x) & 0xFFFE) == 0xFFFE \ + || ((x) >= 0xD800 && (x) <= 0xDFFF) \ + || ((x) >= 0xFDD0 && (x) <= 0xFDEF)) + +int +runetochar(char *s, const Rune *p) +{ + Rune r = *p; + + switch(runelen(r)) { + case 1: /* 0aaaaaaa */ + s[0] = r; + return 1; + case 2: /* 00000aaa aabbbbbb */ + s[0] = 0xC0 | ((r & 0x0007C0) >> 6); /* 110aaaaa */ + s[1] = 0x80 | (r & 0x00003F); /* 10bbbbbb */ + return 2; + case 3: /* aaaabbbb bbcccccc */ + s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */ + s[1] = 0x80 | ((r & 0x000FC0) >> 6); /* 10bbbbbb */ + s[2] = 0x80 | (r & 0x00003F); /* 10cccccc */ + return 3; + case 4: /* 000aaabb bbbbcccc ccdddddd */ + s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */ + s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */ + s[2] = 0x80 | ((r & 0x000FC0) >> 6); /* 10cccccc */ + s[3] = 0x80 | (r & 0x00003F); /* 10dddddd */ + return 4; + default: + return 0; /* error */ + } +} + +int +chartorune(Rune *p, const char *s) +{ + return charntorune(p, s, UTFmax); +} + +int +charntorune(Rune *p, const char *s, size_t len) +{ + unsigned int i, n; + Rune r; + + if(len == 0) /* can't even look at s[0] */ + return 0; + + switch((n = UTFSEQ(s[0]))) { + case 1: r = s[0]; break; /* 0xxxxxxx */ + case 2: r = s[0] & 0x1F; break; /* 110xxxxx */ + case 3: r = s[0] & 0x0F; break; /* 1110xxxx */ + case 4: r = s[0] & 0x07; break; /* 11110xxx */ + case 5: r = s[0] & 0x03; break; /* 111110xx */ + case 6: r = s[0] & 0x01; break; /* 1111110x */ + default: /* invalid sequence */ + *p = Runeerror; + return 1; + } + /* add values from continuation bytes */ + for(i = 1; i < MIN(n, len); i++) + if((s[i] & 0xC0) == 0x80) { + /* add bits from continuation byte to rune value + * cannot overflow: 6 byte sequences contain 31 bits */ + r = (r << 6) | (s[i] & 0x3F); /* 10xxxxxx */ + } + else { /* expected continuation */ + *p = Runeerror; + return i; + } + + if(i < n) /* must have reached len limit */ + return 0; + + /* reject invalid or overlong sequences */ + if(BADRUNE(r) || runelen(r) < (int)n) + r = Runeerror; + + *p = r; + return n; +} + +int +runelen(Rune r) +{ + if(BADRUNE(r)) + return 0; /* error */ + else if(r <= 0x7F) + return 1; + else if(r <= 0x07FF) + return 2; + else if(r <= 0xFFFF) + return 3; + else + return 4; +} + +size_t +runenlen(const Rune *p, size_t len) +{ + size_t i, n = 0; + + for(i = 0; i < len; i++) + n += runelen(p[i]); + return n; +} + +int +fullrune(const char *s, size_t len) +{ + Rune r; + + return charntorune(&r, s, len) > 0; +} diff --git a/sbase/libutf/runetype.c b/sbase/libutf/runetype.c @@ -0,0 +1,48 @@ +/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com> + * + * 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. + */ +#include <stdlib.h> +#include "../utf.h" + +#define nelem(x) (sizeof (x) / sizeof *(x)) + +static int rune1cmp(const void *, const void *); +static int rune2cmp(const void *, const void *); + +#include "../runetypebody.h" + +int +rune1cmp(const void *v1, const void *v2) +{ + Rune r1 = *(Rune *)v1, r2 = *(Rune *)v2; + + return r1 - r2; +} + +int +rune2cmp(const void *v1, const void *v2) +{ + Rune r = *(Rune *)v1, *p = (Rune *)v2; + + if(r >= p[0] && r <= p[1]) + return 0; + else + return r - p[0]; +} diff --git a/sbase/libutf/utf.c b/sbase/libutf/utf.c @@ -0,0 +1,129 @@ +/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com> + * + * 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. + */ +#include <string.h> +#include "../utf.h" + +char * +utfecpy(char *to, char *end, const char *from) +{ + Rune r = Runeerror; + size_t i, n; + + /* seek through to find final full rune */ + for(i = 0; r != '\0' && (n = charntorune(&r, &from[i], end - &to[i])); i += n) + ; + memcpy(to, from, i); /* copy over bytes up to this rune */ + + if(i > 0 && r != '\0') + to[i] = '\0'; /* terminate if unterminated */ + return &to[i]; +} + +size_t +utflen(const char *s) +{ + const char *p = s; + size_t i; + Rune r; + + for(i = 0; *p != '\0'; i++) + p += chartorune(&r, p); + return i; +} + +size_t +utfnlen(const char *s, size_t len) +{ + const char *p = s; + size_t i; + Rune r; + int n; + + for(i = 0; (n = charntorune(&r, p, len-(p-s))) && r != '\0'; i++) + p += n; + return i; +} + +char * +utfrune(const char *s, Rune r) +{ + if(r < Runeself) { + return strchr(s, r); + } + else if(r == Runeerror) { + Rune r0; + int n; + + for(; *s != '\0'; s += n) { + n = chartorune(&r0, s); + if(r == r0) + return (char *)s; + } + } + else { + char buf[UTFmax+1]; + int n; + + if(!(n = runetochar(buf, &r))) + return NULL; + buf[n] = '\0'; + return strstr(s, buf); + } + return NULL; +} + +char * +utfrrune(const char *s, Rune r) +{ + const char *p = NULL; + Rune r0; + int n; + + if(r < Runeself) + return strrchr(s, r); + + for(; *s != '\0'; s += n) { + n = chartorune(&r0, s); + if(r == r0) + p = s; + } + return (char *)p; +} + +char * +utfutf(const char *s, const char *t) +{ + const char *p, *q; + Rune r0, r1, r2; + int n, m; + + for(chartorune(&r0, t); (s = utfrune(s, r0)); s++) { + for(p = s, q = t; *q && *p; p += n, q += m) { + n = chartorune(&r1, p); + m = chartorune(&r2, q); + if(r1 != r2) + break; + } + if(!*q) + return (char *)s; + } + return NULL; +} diff --git a/sbase/libutf/writerune.c b/sbase/libutf/writerune.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "../utf.h" + +void +writerune(const char *file, FILE *fp, Rune *r) +{ + char buf[UTFmax]; + int n; + + if ((n = runetochar(buf, r)) > 0) { + fwrite(buf, n, 1, fp); + if (ferror(fp)) { + fprintf(stderr, "%s: write error: %s\n", + file, strerror(errno)); + exit(1); + } + } +} diff --git a/sbase/libutil/agetcwd.c b/sbase/libutil/agetcwd.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include <unistd.h> + +#include "../util.h" + +char * +agetcwd(void) +{ + char *buf; + long size; + + apathmax(&buf, &size); + if (!getcwd(buf, size)) + eprintf("getcwd:"); + + return buf; +} + diff --git a/sbase/libutil/apathmax.c b/sbase/libutil/apathmax.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "../util.h" + +void +apathmax(char **p, long *size) +{ + errno = 0; + + if ((*size = pathconf("/", _PC_PATH_MAX)) < 0) { + if (errno == 0) { + *size = BUFSIZ; + } else { + eprintf("pathconf:"); + } + } + *p = emalloc(*size); +} diff --git a/sbase/libutil/concat.c b/sbase/libutil/concat.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <unistd.h> + +#include "../text.h" +#include "../util.h" + +void +concat(FILE *fp1, const char *s1, FILE *fp2, const char *s2) +{ + char buf[BUFSIZ]; + ssize_t n; + + while ((n = read(fileno(fp1), buf, sizeof buf)) > 0) { + if (write(fileno(fp2), buf, n) != n) + eprintf("%s: write error:", s2); + } + if (n < 0) + eprintf("%s: read error:", s1); +} diff --git a/sbase/libutil/cp.c b/sbase/libutil/cp.c @@ -0,0 +1,153 @@ +/* See LICENSE file for copyright and license details. */ +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <utime.h> + +#include "../fs.h" +#include "../text.h" +#include "../util.h" + +int cp_aflag = 0; +int cp_fflag = 0; +int cp_Pflag = 0; +int cp_pflag = 0; +int cp_rflag = 0; +int cp_vflag = 0; +int cp_status = 0; + +int +cp(const char *s1, const char *s2) +{ + FILE *f1, *f2; + char *ns1, *ns2; + long size1, size2; + struct dirent *d; + struct stat st; + struct utimbuf ut; + char buf[PATH_MAX]; + DIR *dp; + int r; + + if (cp_vflag) + printf("'%s' -> '%s'\n", s1, s2); + + r = cp_Pflag ? lstat(s1, &st) : stat(s1, &st); + if (r < 0) { + weprintf("%s %s:", cp_Pflag ? "lstat" : "stat", s1); + cp_status = 1; + return 0; + } + + if (S_ISLNK(st.st_mode)) { + if (readlink(s1, buf, sizeof(buf) - 1) >= 0) { + if (cp_fflag) + unlink(s2); + if (symlink(buf, s2) != 0) { + weprintf("%s: can't create '%s'\n", argv0, s2); + cp_status = 1; + return 0; + } + } + goto preserve; + } + + if (S_ISDIR(st.st_mode)) { + if (!cp_rflag) + eprintf("%s: is a directory\n", s1); + + if (!(dp = opendir(s1))) + eprintf("opendir %s:", s1); + + if (mkdir(s2, st.st_mode) < 0 && errno != EEXIST) + eprintf("mkdir %s:", s2); + + apathmax(&ns1, &size1); + apathmax(&ns2, &size2); + while ((d = readdir(dp))) { + if (strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) { + r = snprintf(ns1, size1, "%s/%s", s1, d->d_name); + if (r >= size1 || r < 0) { + eprintf("%s/%s: filename too long\n", + s1, d->d_name); + } + r = snprintf(ns2, size2, "%s/%s", s2, d->d_name); + if (r >= size2 || r < 0) { + eprintf("%s/%s: filename too long\n", + s2, d->d_name); + } + fnck(ns1, ns2, cp); + } + } + closedir(dp); + free(ns1); + free(ns2); + goto preserve; + } + + if (cp_aflag) { + if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || + S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode)) { + unlink(s2); + if (mknod(s2, st.st_mode, st.st_rdev) < 0) { + weprintf("%s: can't create '%s':", argv0, s2); + cp_status = 1; + return 0; + } + goto preserve; + } + } + + if (!(f1 = fopen(s1, "r"))) { + weprintf("fopen %s:", s1); + cp_status = 1; + return 0; + } + + if (!(f2 = fopen(s2, "w"))) { + if (cp_fflag) { + unlink(s2); + if (!(f2 = fopen(s2, "w"))) { + weprintf("fopen %s:", s2); + cp_status = 1; + return 0; + } + } else { + weprintf("fopen %s:", s2); + cp_status = 1; + return 0; + } + } + concat(f1, s1, f2, s2); + /* preserve permissions by default */ + fchmod(fileno(f2), st.st_mode); + fclose(f2); + fclose(f1); + +preserve: + if (cp_aflag || cp_pflag) { + if (!(S_ISLNK(st.st_mode))) { + /* timestamp */ + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + utime(s2, &ut); + } + /* preserve owner ? */ + if (S_ISLNK(st.st_mode)) + r = lchown(s2, st.st_uid, st.st_gid); + else + r = chown(s2, st.st_uid, st.st_gid); + if (r < 0) { + weprintf("cp: can't preserve ownership of '%s':", s2); + cp_status = 1; + } + } + return 0; +} diff --git a/sbase/libutil/crypt.c b/sbase/libutil/crypt.c @@ -0,0 +1,156 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../crypt.h" +#include "../text.h" +#include "../util.h" + +static int +hexdec(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; /* unknown character */ +} + +static int +mdcheckline(const char *s, uint8_t *md, size_t sz) +{ + size_t i; + int b1, b2; + + for (i = 0; i < sz; i++) { + if (!*s || (b1 = hexdec(*s++)) < 0) + return -1; /* invalid format */ + if (!*s || (b2 = hexdec(*s++)) < 0) + return -1; /* invalid format */ + if ((uint8_t)((b1 << 4) | b2) != md[i]) + return 0; /* value mismatch */ + } + return (i == sz) ? 1 : 0; +} + +int +cryptcheck(char *sumfile, int argc, char *argv[], + struct crypt_ops *ops, uint8_t *md, size_t sz) +{ + FILE *cfp, *fp; + char *line = NULL, *file, *p; + int r, nonmatch = 0, formatsucks = 0, noread = 0, ret = 0; + size_t bufsiz = 0; + + if (!sumfile) + cfp = stdin; + else if (!(cfp = fopen(sumfile, "r"))) + eprintf("fopen %s:", sumfile); + + while (getline(&line, &bufsiz, cfp) != -1) { + if (!(file = strstr(line, " "))) { + formatsucks++; + continue; + } + if ((file - line) / 2 != sz) { + formatsucks++; /* checksum length mismatch */ + continue; + } + *file = '\0'; + file += 2; + for (p = file; *p && *p != '\n' && *p != '\r'; p++); /* strip newline */ + *p = '\0'; + if (!(fp = fopen(file, "r"))) { + weprintf("fopen %s:", file); + noread++; + continue; + } + cryptsum(ops, fp, file, md); + r = mdcheckline(line, md, sz); + if (r == 1) { + printf("%s: OK\n", file); + } else if (r == 0) { + printf("%s: FAILED\n", file); + nonmatch++; + } else { + formatsucks++; + } + fclose(fp); + } + if (sumfile)